Files
adminandClaude Opus 4.7 8e89f793d5 feat: phase r1+r2 recovery + r3-0 swiftly api ingestion
R1 stabilization: pytest harness with transactional db fixture, smoke
+ alembic + auth + scrape + approval + swiftly tests, github actions
ci yaml. Bearer-token admin auth + signed-cookie session for family
ui mutations. Async POST /api/admin/scrape (BackgroundTasks, returns
202). Path canonicalization (no /list, /planned suffixes). DATABASE_URL
fail-fast on empty.

R2 deferred-risk spikes: live lucky california fetch (R2-A), full
email+per-voter approval click round trip with single-use enforcement
(R2-B, console email backend, sendgrid stub).

R3-0 phase 3 redesign: replaced playwright html scraper with requests
based swiftly json api client. 17 categories, ~10k products per scrape,
upsert by (source, external_id). 401 surfaces actionable token-refresh
message via ScrapeLog.error_message.

Pre-existing defects fixed: shopping_list.py syntax error blocking app
import, MealPlan.votes orphan relationship, JSONB(astext=True) invalid
kwarg, missing requests dep, calorie_target schema drift, every SQLEnum
needed values_callable, 0001 had empty downgrade(), seed had duplicate
ingredient rows.

Migrations added: 0003 grocery_item.description, 0004 family_profile.
calorie_target, 0005 grocery_item.external_id + source + composite index.

Verified: 31/31 pytest green, alembic upgrade->downgrade->upgrade clean,
frontend npm run build clean, live scrape 9,960 grocery_item rows in 36s.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 14:08:19 -07:00

57 lines
2.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# R3-0 (Swiftly API ingestion) — GATE PASSED
Date: 2026-05-05
## Live end-to-end scrape
```
POST /api/admin/scrape (Bearer test-admin-token) → 202 + scrape_log_id
[bg] Swiftly: discovered 17 categories
[bg] Background scrape complete: 10908/10908 items saved
final status: success
items_scraped: 10908
duration_seconds: 36
grocery_item count (after upsert): 9960
```
## Tests
```
pytest -q tests/ → 31 passed in 2.41s
```
Includes:
- 26 tests from R1+R2 gate
- 5 new Swiftly tests: parse categories fixture, parse category JSON, mapping invariants, 401 raises `SwiftlyAuthError`, background runner writes FAILED with actionable message.
## Schema
Migration `0005_grocery_item_external_id.py` adds:
- `grocery_item.external_id TEXT NULLABLE` (Swiftly product id)
- `grocery_item.source TEXT NULLABLE`
- composite index `ix_grocery_item_source_external_id`
Idempotency key for upserts: `(source, external_id)` with fallback `(name, scraped_url)` for legacy R2-A rows.
## Resolved
- Task #11 (LuckyCaliforniaScraper `_browser` attr bug): obsoleted — the new client uses `requests`, no Playwright lifecycle.
- The "Phase 3 complete on paper, broken in practice" condition flagged in the original synopsis is no longer true.
## Behavior to know
- `SWIFTLY_BEARER_TOKEN` is read from env at scraper init. The token in `.env.example` and `.env.test` is the user-supplied one and will expire (~hourly). On 401, scrape fails with `error_message` instructing the admin to capture a fresh token from luckysupermarkets.com's devtools network tab and update the env var.
- Bearer header is scoped to `prod.swiftlyapi.net` only; never sent to the public categories page.
- `LUCKY_STORE_ID=757` (Lucky California in San Pablo). Switch via env to target a different store.
- Playwright is still pinned in `requirements.txt` (other future scrapers may want it). Removable later if no other use case lands.
## Open items
- Task #8: ScrapeStatus lacks distinct queued/started states. Cosmetic.
- `.env.example` carries the live bearer token; user authorized. Refresh whenever the token expires.
## Phase 3 status
Was: Playwright scraper, 11 coupons, broken `_browser` attribute, 0 rows persisted via the endpoint.
Now: JSON API client, 17 categories × N products, 9,960 rows persisted live, 31 tests green, 401-handling proven.