Add opt-in SQLite price-history layer (historyDb) - #103
Conversation
|
I'm going to study this for a bit. I gonna enlist @AESternberg and @tecmage for comments. |
|
Thanks for taking a look — no rush at all. A couple of notes that may help the review: it's fully opt-in (historyDb unset = no file, no behavior change; all existing tests pass unmodified), stdlib sqlite3 only, and it commits per observation so a run that dies partway keeps what it saw. I'm running it daily on my own household setup. Happy to adjust anything — schema names, where the hooks sit, splitting it smaller — whatever makes it easier to maintain. |
Adds a PriceHistory class (stdlib sqlite3, WAL, per-observation commits) that appends every cabin-fare and addon/watchlist price check to a local SQLite file when historyDb is set in config.yaml. No-op (zero filesystem touch) when unset, so existing behavior is unchanged for everyone else. - PriceHistory class + schema (runs, price_points) inline in the main script; instantiated right after setup_hybrid_logging, same pattern as apobj/log_file. CruiseAppConfig.history defaults to a disabled PriceHistory instance (never bare None), so code that builds the dataclass directly can't hit None.start_run(). - Hooks: run start/finish + record_cabin_fare at every exit of get_cruise_price() + record_addon at every exit of get_new_order_price(). The error finalize lives in main()'s own existing no-op except block (re-raising via bare `raise`); the `if __name__ == "__main__":` block is untouched, byte-identical to main. Each pricing function builds one shared dict of the fields common to every record_* call in that function, so each call site is a short one-liner. Alert/notify logic itself is untouched - only locals and one record call added per branch. - docker-compose.yml: optional ./data volume for the DB file. - SAMPLE-config.yaml / docs/config.md: document historyDb (opt-in, off by default, Docker path, growth-rate note). - test_price_history.py: PriceHistory unit tests, config-loading tests, and integration tests driving the real production functions (get_cruise_price / get_new_order_price / main) with a mocked config.history. - .github/workflows/pytest.yml: run the new test file in CI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
abd4f85 to
0fc2900
Compare
The PR jdeath#103 price-history file contains real reservation ids and prices and lands in the repo directory - keep it (and any future .db) out of commits, same hygiene as output.txt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Guard every database touch: on any sqlite/OS error, log one warning and degrade to the no-op object (price checking continues; rows recorded before the failure are kept). Previously a bad historyDb path or corrupt file crashed the script at config load, and a mid-run write failure killed the run. - Arm busy_timeout BEFORE the WAL journal-mode switch: the switch itself needs a lock, and without a timeout a concurrent run fails instantly with "database is locked" on some filesystems (WSL /mnt/c). - Record the "not available for passenger" bail of get_new_order_price as status=not_available_for_passenger - for watchlist items this is the back-in-stock waiting state history queries want a row for. - str-coerce reservation_id on cabin rows to match addon rows. - SAMPLE-config: historyDb commented out, matching the opt-in default. Adds four tests (failure isolation x3, the new hook); suite 189 passed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Did an in-depth audit of this one — hands-on, not just a read-through. Overall: high-quality PR, worth merging once one design gap is closed (fixes submitted as C2Tech-sys#1, which flows into this PR when merged there). What held up under audit:
Findings (fixed in the linked PR):
Minor, noted but not all patched: cabin rows stored Nice work @C2Tech-sys — the immediate-commit/WAL design and the runs table are exactly right for a crash-prone-network cron script. |
|
I just got off Icon today and had some time to check while waiting for our flight home. It looks good, just needed a few tweaks. I'll do some more in depth testing tomorrow and the rest of the week when I'm back at my normal computer. |
…e-history PriceHistory hardening: never crash the run, record the unavailable path
|
Merged your fixes into the PR — thank you, that's a better failure model than I had, and the busy_timeout ordering is a catch I'd never have hit on my filesystem. Appreciate the mutation-testing pass; glad the suite earned its keep. It's now running in production on our household instance against real bookings, so I'll have real-world data on it this week. Happy to fold in anything else your testing turns up. |
|
Thanks. I'll merge this when everyone is happy. I will do a release when get #105 non-USD cabin checking working again. |
|
Real-world update from our production instance: the history layer has been recording twice-daily runs against live bookings all week without a hiccup. Side benefit of that data: it surfaced the TA-booking pricing bug jdeath just merged in #108 — a cabin that "wasn't for sale" for days turned out to be a |
|
Heads up that this PR now gates a follow-up, in case that's useful for prioritising it. Running multi-account in production surfaced a failure mode worth fixing: I have that fixed and running here: the failing account is skipped (naming whether it was the login or the post-login profile fetch that failed — thanks to the error body now logged by #109), the run continues through the remaining accounts, and it exits with a distinct code The reason it isn't a PR yet is that it records the partial outcome through this PR's history hook ( |
|
It probably wait to be together. I really want to get #105 solved first before adding this, just to avoid any conflict. |
|
Heads up before you touch the conflict: #105 landing put this PR into CONFLICTING (3 hunks in CheckRoyalCaribbeanPrice.py — the history schema/class vs the new CheckinPaymentTracker, the record_addon hook vs the new |
…istory Reconciles PR jdeath#103's opt-in SQLite price-history layer with jdeath#105's GTY category handling, CheckinPaymentTracker refactor, and market-country helper, now both on this branch: - Top-of-file: kept both new classes side by side - PriceHistory (ours) followed by CheckinPaymentTracker (theirs); neither references the other so ordering is cosmetic only. - Add-on pricing path: kept our config.history.record_addon(...) call immediately before their `return watch_tracker_record`, so the history write still happens before the function returns. - Run start in main(): kept jdeath#105's payment_tracker/collected_watch_rows init and added config.history.start_run() alongside them; dropped our old checkin_payment_rows.clear()/watch_price_rows.clear() calls since jdeath#105 removed those module-level lists entirely in favor of the tracker (keeping them would NameError). All 14 config.history.* call sites from 69e3aa4 (5 record_cabin_fare, 5 record_addon, 1 start_run, 3 finish_run) verified present and intact after the merge. Full suite: 205 passed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Brought up to date with |
|
thanks |
What
An opt-in price-history sink. Set
historyDb: "price_history.db"inconfig.yamland every run appends what it saw to a local SQLite file: cabin fare and every add-on / watchlist item, with paid vs. current price, currency, per-night flag + nights, the rebook decision, and whether a notification fired. Runs are tracked too (start/finish/status, includingerrorwhen a run dies mid-way), so "cheapest this has been" / trend views become possible on top of the existing alerts.Zero behavior change when unset
historyDbunset →PriceHistoryis a no-op object: no file is created, no SQL runs, every method returns immediately. All 156 existing tests pass unmodified;test_alert_matrix.pyin particular is untouched, so the alert/notify decision tree is byte-identical.Design notes
sqlite3only — no new dependency, nothing added torequirements.txt, no Dockerfile change (class is inline in the script so the Docker image picks it up as-is).runsrow withfinished_at IS NULLmeans the process exited without finalizing (e.g.sys.exitfrom a login failure).get_cruise_price()/get_new_order_price()plus run start/finish inmain(). The existing alert branches only gain a couple of locals (rebook_decision,notified); no restructuring.docker-compose.ymlgets a./data:/app/datavolume;docs/config.mddocuments the option and the (tiny) growth rate.Tests
test_price_history.py— 14 tests: no-op guarantee (unit + throughload_config_objects), schema creation, run lifecycle, round-trips, append-only across runs, hook coverage for price-drop / not-for-sale / no-fare-data / no-longer-for-sale, and the error-finalize path. Added to.github/workflows/pytest.yml.Independent of #102 (docs only).
🤖 Generated with Claude Code