Add 5 plans

This commit is contained in:
2026-06-24 15:30:47 +02:00
parent 5348bedb86
commit d97d8c6cf4
5 changed files with 644 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
# Plan 3 — Performance
**Items covered:** #4 (missing database indexes), #2 (O(n³) timetable loop), #3 (in-Python aggregation in other stats endpoints)
---
## Context
You are working on a FastAPI backend for a baby monitor / sleep tracker app. The codebase lives under `app/`. This plan replaces expensive Python loops in `app/routers/stats.py` with SQL queries, and adds database indexes to make those queries fast. The index fix (#4) must be done first because the SQL queries depend on it being effective.
Do one task at a time. After each file change, re-read the file to verify it is correct. Run the tests between each task.
---
## Tasks
### 1. Add database indexes (`app/models.py`)
#### Step 1 — Read the file first
Read `app/models.py` in full to understand the `SleepSession` model structure and current field definitions.
#### Step 2 — Add indexes
Add indexes on `start_time` and `end_time`. The preferred approach is inline with `sa_column`:
```python
from sqlalchemy import Column, DateTime
from sqlmodel import Field, SQLModel
class SleepSession(SQLModel, table=True):
...
start_time: datetime = Field(sa_column=Column(DateTime, index=True))
end_time: Optional[datetime] = Field(default=None, sa_column=Column(DateTime, index=True, nullable=True))
```
If the fields already use `sa_column`, just add `index=True` to the existing `Column(...)` calls. Do not duplicate field definitions.
#### Step 3 — Verify
Run the tests to make sure the model change doesn't break anything:
```bash
pytest
```
---
### 2. Fix the O(n³) timetable query (`app/routers/stats.py:77-99`)
#### Step 1 — Read the file first
Read `app/routers/stats.py` in full before making any changes. Understand the full timetable endpoint: its parameters, what it returns, and the structure of the current triple-nested loop.
#### Step 2 — Rewrite the loop
The current code iterates over every day, then every 15-minute slot, then every session in memory — ~2.88 million comparisons for 30 days × 1,000 sessions.
Replace it with a per-day SQL query approach:
For each day in the date range:
1. Query the database for sessions that overlap that day: `WHERE start_time < day_end AND (end_time IS NULL OR end_time > day_start)`
2. Build the 96 fifteen-minute slots comparing only against the small set of sessions returned for that day.
This keeps the slot-building logic in Python (which is fine) but moves the session filtering into SQL (which uses the index). The number of sessions compared per day drops from ALL sessions to only the ones active that day.
Keep the return format identical to the current implementation — do not change the shape of the response.
#### Step 3 — Verify
Run the tests:
```bash
pytest
```
All timetable tests must pass. If there is only one happy-path test, that is expected — new tests are added in Plan 5.
---
### 3. Fix in-Python aggregation in the other two stats endpoints (`app/routers/stats.py:28-47` and `119-134`)
Read the file again (it may have changed from the previous step) before editing.
#### `daily-summary` endpoint (around lines 28-47)
The current code loads all sessions for a date range and loops through them in Python to compute per-day totals. Replace with a single SQL `GROUP BY` query:
```sql
SELECT DATE(start_time), SUM(<duration_expression>), COUNT(*)
FROM sleep_session
WHERE start_time >= :from_date AND start_time < :to_date
GROUP BY DATE(start_time)
```
Use SQLAlchemy's query API or a `text()` query to express this. Map the results back to the same response format the endpoint currently returns.
#### `wake-sleep-times` endpoint (around lines 119-134)
The current code loads all sessions and loops per day to find the first wakeup and last bedtime. Replace with:
```sql
SELECT DATE(start_time), MIN(start_time), MAX(end_time)
FROM sleep_session
WHERE start_time >= :from_date AND start_time < :to_date
GROUP BY DATE(start_time)
```
Map the results back to the same response format.
#### Important
Do not change the response shape of either endpoint. The goal is identical output, produced more efficiently.
#### Step — Verify
Run the full test suite:
```bash
pytest
```
All tests must pass before declaring this plan complete. Do not move on to Plan 4 until the suite is green.