Nobody has started this yet — be first.
Business impact
This is the headline number on the whole product -- a habit tracker whose entire pitch (per this project's own README.md) is "correctness-focused ... timezone-aware streak tracking." Right now GET /habits/{id} and GET /habits both report current_streak and longest_streak swapped. A user who just broke a long streak and started a fresh 1-day run would see current_streak still showing their old best (misleadingly telling them they're fine) and longest_streak showing 1 (misleadingly erasing their actual best). This is not a cosmetic bug -- it actively lies to users about the one thing they open the app to check, in both directions at once.
Problem
habit_to_out (the function every habit-returning route funnels through) assigns current_streak=compute_longest_streak(dates) and longest_streak=compute_current_streak(dates, today) -- compute_longest_streak is wired to the current_streak field, and compute_current_streak is wired to the longest_streak field.
Current behavior
GET /habits/{id} and GET /habits report current_streak and longest_streak swapped -- e.g. a user with an old 4-day streak and a fresh 1-day run sees current_streak: 4, longest_streak: 1 instead of current_streak: 1, longest_streak: 4.
Expected behavior
current_streak must be the output of compute_current_streak(dates, today); longest_streak must be the output of compute_longest_streak(dates) -- matching both field names and the docstring at the top of app/crud.py.
Steps to reproduce
cd fastapi/habit_tracker source .venv/bin/activate python - <<'PY' import datetime as dt from fastapi.testclient import TestClient from app.main import app from app import models from app.database import SessionLocal, Base, engine
c = TestClient(app) c.post("/auth/register", json={"email": "demo2@example.com", "password": "supersecret1"}) token = c.post("/auth/login", data={"username": "demo2@example.com", "password": "supersecret1"}).json()["access_token"] h = c.post("/habits", json={"name": "Read", "timezone": "UTC"}, headers={"Authorization": f"Bearer {token}"}).json()
db = SessionLocal() today = dt.date.today() for offset in (10, 9, 8, 7): # an old 4-day run db.add(models.CheckIn(habit_id=h["id"], date=today - dt.timedelta(days=offset))) db.add(models.CheckIn(habit_id=h["id"], date=today)) # a fresh 1-day run db.commit()
Why this matters
compute_current_streak and compute_longest_streak are both pure, well-tested functions (see tests/test_streaks.py::test_pure_*) -- the bug isn't in the streak math, it's a one-line wiring mistake at the point where the two already-correct results get assigned to the two response fields. This is worth internalizing as a class of bug: the unit tests for the underlying logic can be 100% green while the integration point that hands results to callers is still wrong.
Suggested approach
Read habit_to_out line by line against schemas.HabitOut's field names. Nothing needs to change in compute_current_streak or compute_longest_streak themselves.
Acceptance criteria
Verification
pytest practice_tickets/tests/test_ticket03_swapped_streak_fields.py -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
$git checkout -b fix/swapped-streak-fieldsFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/swapped-streak-fieldsSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign inprint(c.get(f"/habits/{h['id']}", headers={"Authorization": f"Bearer {token}"}).json()) PY
docker compose up --buildfastapi/habit_trackerRun the project's own suite with pytest -v (or pytest tests/ -q for a quick pass/fail count) from fastapi/habit_tracker -- a fresh clone should show a handful of failures until TICKET-03 and TICKET-06 are fixed.
Work the tickets in practice_tickets/ (TICKET-01 through TICKET-07); each names one pytest file under practice_tickets/tests/:
pytest practice_tickets/tests/test_ticket01_stats_window.py -v # a single ticket
./practice_tickets/run_tickets.sh # all 7, clean pass/fail summary
./practice_tickets/run_tickets.sh 03 07 # just a subset
practice_tickets/tests/ is intentionally outside pyproject.toml's testpaths = ["tests"], so a plain pytest run from the repo root never picks these up -- they're learning exercises, not part of the project's CI-gating regression suite.
Level 1
Fix a bug
Read existing behaviour, correct it.