1.8.0
⚙️ Patch 1.8.0 — Price Alerts (Qogita + Keepa watchlist pipeline)¶
🚀 Summary¶
- New Price Alerts area (
/price-alerts) with Qogita and Keepa integrations for wholesale watchlist monitoring and margin-based buy targets. - Keepa pipeline on
qogita_watchlist_items: finder → product fetch → EK analysis (10% margin) → push to Qogita — no separate ASIN queue table. - Scheduled jobs: Qogita watchlist sync (stock/prices, Discord on targets met) and Keepa selling-price refresh with auto re-analyze + export when values change.
- Watchlist UI: Qogita-first status display, status filters, refresh button,
react-cron-generator, localStorage prefs, merged Target EK column.
✨ Improvements¶
Price Alerts — Qogita¶
- Credentials CRUD + login test (
POST /api/v1/omni/qogita/credentials/…). - Watchlist sync from Qogita API — stock, price,
areTargetsMet, placeholder cleanup. - Watchlist CRUD — add / patch / delete by GTIN on Qogita + local DB.
- UTC-aware token expiry (fixes naive/aware datetime compare on refresh).
- Sync removes only broken placeholder rows (
name === "Product name unavailable", noqid); out-of-stock items with real names are kept. DELETE /watchlist/items/{gtin}/URL fixed (was wrong path/body).
Price Alerts — Keepa¶
- Keepa credentials (API key only) under same page.
- Pipeline steps (callable individually or combined):
POST /keepa/sync-asins— finder query → ASIN stubs on watchlist (ASIN:{asin}).POST /keepa/fetch-products— product API → real EAN, metrics columns.POST /keepa/analyze-products— fees + Target EK at 10% margin.POST /keepa/push-to-qogita— create/update Qogita watchlist targets.POST /keepa/scan— all steps in one call.GET /keepa/queue/status— pending fetch / analyze / analyzed counts from watchlist.- Keepa metrics stored in dedicated columns (avg90 prices, fees, EK, etc.); raw
keepa_product_jsonremoved. analyze_from_stored_rowfor cron re-analysis without persisting full Keepa payloads.
Price Alerts — Crons (/api/v1/omni/price-alerts/cron/…)¶
qogita_watchlist_sync— sync Qogita → Discord webhook whenare_targets_metnewly true → re-analyze + push changed EK to Qogita.keepa_price_sync— refresh Keepa avg90 selling prices → re-analyze → export to Qogita when EK/target changed.- Start/stop/status/run endpoints; schedules persisted in
cron_jobs(Berlin TZ). - Registered on
service-omnistartup viaCronJobManager.
Push → Qogita¶
- After successful push, upserts Qogita API response then runs full watchlist sync so
qid, stock, and Qogita price appear immediately. - Push response includes
qogita_syncstats.
Frontend (QogitaWatchlistTab)¶
- Sidebar: Rituals → Price Alerts; tabs: Overview, Outlet Monitor, Qogita Watchlist.
- Pipeline buttons: Sync ASINs → Fetch Products → Analyze → Push → Sync from Qogita.
- Refresh — reload table + Keepa queue (no API sync).
- Qogita status filter — all, on Qogita, targets met, in/out of stock, awaiting push, Keepa only (
qogita_statusquery param). - Status chips: Qogita rows show stock/targets met; Keepa-only rows show pipeline state.
- Qogita price/stock columns only when
qidpresent. - Target EK (10%) — single column (
calculated_ek_price??target_price). - Cron UI —
react-cron-generator+ Discord webhook field; prefs in localStorage (qogitaWatchlist.prefs.v1).
Tests¶
test_keepa_workflow_integration.py— live pipeline for first 100 ASINs (RUN_KEEPA_INTEGRATION=1).
🐛 Bug Fixes¶
- Missing DB columns
keepa_product_fetched_at/ pipeline fields — migrationadd_keepa_watchlist_pipeline.sql. - Qogita token check
TypeError(offset-naive vs offset-aware datetimes). - Mass watchlist deletion on sync — unavailable items no longer treated as delete candidates.
- 400 on push for GTINs not in Qogita catalog — expected; logged as warning (not in Qogita assortment).
📁 Files changed¶
Backend (service_omni)
app/api/v1/endpoints/qogita.py,keepa.py,price_alerts.pyapp/services/qogita/qogita_client.py,watchlist_sync.pyapp/services/keepa/keepa_client.py,scanner.py,pricing.py,storage_fees.py,constants.py,watchlist_keys.pyapp/services/price_alerts/watchlist_cron.py,discord_notifications.pyapp/crud/qogita.py,crud/keepa.pyapp/schemas/qogita.py,app/models/qogita.py,app/models/keepa.py,app/models/price_alerts.pyapp/main.py,app/api/v1/api.pyapp/tests/test_keepa_workflow_integration.py,conftest.py
Frontend
src/pages/Rituals/index.jsx,QogitaWatchlistSection.jsxsrc/pages/Statistics/Tabs/QogitaWatchlistTab.jsx,qogitaWatchlistPrefs.jssrc/services/api/omni/qogita.js,keepa.js,priceAlerts.jssrc/layouts/DashboardLayout— Price Alerts menu entry
Postgres
docker/postgres/tables/qogita_credentials.sql,qogita_watchlist_items.sql,keepa_credentials.sqldocker/postgres/migrations/add_qogita_schema.sqldocker/postgres/migrations/add_keepa_watchlist_columns.sqldocker/postgres/migrations/add_keepa_watchlist_pipeline.sqldocker/postgres/migrations/drop_keepa_product_json.sql
🔧 Database & SQL¶
Run on server before deploy (in order):
-- Qogita + Keepa credentials and watchlist base
\i docker/postgres/migrations/add_qogita_schema.sql
-- Keepa analysis columns on watchlist
\i docker/postgres/migrations/add_keepa_watchlist_columns.sql
-- Pipeline timestamp (fetch step)
\i docker/postgres/migrations/add_keepa_watchlist_pipeline.sql
-- Drop unused JSON blob (metrics are columnized)
\i docker/postgres/migrations/drop_keepa_product_json.sql
Or via Docker:
Get-Content docker\postgres\migrations\add_qogita_schema.sql | docker exec -i postgres psql -U <user> -d <db>
Get-Content docker\postgres\migrations\add_keepa_watchlist_columns.sql | docker exec -i postgres psql -U <user> -d <db>
Get-Content docker\postgres\migrations\add_keepa_watchlist_pipeline.sql | docker exec -i postgres psql -U <user> -d <db>
Get-Content docker\postgres\migrations\drop_keepa_product_json.sql | docker exec -i postgres psql -U <user> -d <db>
Restart service-omni after migrations. Rebuild frontend for UI changes.
Usage notes¶
Initial setup
- Run SQL migrations above.
- Price Alerts → Credentials — add Qogita login + Keepa API key.
- Sync ASINs → Fetch → Analyze → Push to Qogita (or use Scan API for all-in-one).
Crons
POST /api/v1/omni/price-alerts/cron/qogita/start— body:schedule,discord_webhook,margin_percent,target_quantity.POST /api/v1/omni/price-alerts/cron/keepa/start— body:schedule,refresh_limit,reanalyze_limit.- Or use the Scheduled jobs panel in the watchlist tab (cron builder + Start/Stop).
Integration test (optional, consumes Keepa tokens):
RUN_KEEPA_INTEGRATION=1 pytest app/tests/test_keepa_workflow_integration.py -v
Inside service-omni container with credentials in DB or KEEPA_API_KEY set.
Watchlist filter — GET /qogita/watchlist/?qogita_status=targets_met for rows where Qogita reports target price/qty met.
Push failures (400) — GTIN not in Qogita catalog; product may exist on Amazon but not on Qogita wholesale. Safe to skip.