Spaces:
Configuration error
Configuration error
Download persistence.py from fabrit/assets-tracker-api: direct link, hf CLI and curl.
- Browser
- Download file 2.72 kB
-
https://huggingface.co/spaces/fabrit/assets-tracker-api/resolve/main/persistence.py
- Command line
-
hf download hf://spaces/fabrit/assets-tracker-api/persistence.py
-
curl -L -o persistence.py https://huggingface.co/spaces/fabrit/assets-tracker-api/resolve/main/persistence.py
2.72 kB
| """ | |
| persistence.py — Save and restore alert engine state to/from a JSON file. | |
| Called from main.py on startup (load) and graceful shutdown (save), | |
| so tracked asset ranges survive bot restarts. | |
| """ | |
| import json | |
| import os | |
| from typing import TYPE_CHECKING | |
| import config | |
| from logger import get_logger | |
| if TYPE_CHECKING: | |
| from alert_engine import AlertEngine | |
| log = get_logger("persistence") | |
| def save_state(engine: "AlertEngine") -> None: | |
| """ | |
| Serialise the alert engine's tracked assets to PERSISTENCE_FILE. | |
| Writes atomically via a temp file to avoid corruption on crash. | |
| """ | |
| details = engine.get_asset_details() | |
| payload = { | |
| "version": 1, | |
| "assets": [ | |
| { | |
| "name": d["name"], | |
| "min_price": d["min"], | |
| "max_price": d["max"], | |
| } | |
| for d in details | |
| ], | |
| } | |
| tmp_path = config.PERSISTENCE_FILE + ".tmp" | |
| try: | |
| with open(tmp_path, "w", encoding="utf-8") as f: | |
| json.dump(payload, f, indent=2) | |
| os.replace(tmp_path, config.PERSISTENCE_FILE) | |
| log.info("State saved → %s (%d assets)", config.PERSISTENCE_FILE, len(details)) | |
| except OSError as exc: | |
| log.error("Failed to save state: %s", exc) | |
| finally: | |
| # Clean up temp file if replace failed | |
| if os.path.exists(tmp_path): | |
| try: | |
| os.remove(tmp_path) | |
| except OSError: | |
| pass | |
| def load_state(engine: "AlertEngine") -> int: | |
| """ | |
| Load tracked assets from PERSISTENCE_FILE into the alert engine. | |
| Returns the number of assets restored. | |
| Silently skips the file if it doesn't exist or is malformed. | |
| """ | |
| if not os.path.exists(config.PERSISTENCE_FILE): | |
| log.info("No persistence file found at %s — starting fresh", config.PERSISTENCE_FILE) | |
| return 0 | |
| try: | |
| with open(config.PERSISTENCE_FILE, "r", encoding="utf-8") as f: | |
| payload = json.load(f) | |
| assets = payload.get("assets", []) | |
| restored = 0 | |
| for entry in assets: | |
| name = entry.get("name", "").upper() | |
| min_p = float(entry["min_price"]) | |
| max_p = float(entry["max_price"]) | |
| if name and min_p < max_p: | |
| engine.add_asset(name, min_p, max_p) | |
| restored += 1 | |
| else: | |
| log.warning("Skipping invalid entry in state file: %s", entry) | |
| log.info("Restored %d asset(s) from %s", restored, config.PERSISTENCE_FILE) | |
| return restored | |
| except (json.JSONDecodeError, KeyError, ValueError, OSError) as exc: | |
| log.error("Failed to load state from %s: %s", config.PERSISTENCE_FILE, exc) | |
| return 0 | |