Spaces:
Sleeping
Sleeping
Download storage/cache.py from Shivam4096/Financial2API: direct link, hf CLI and curl.
- Browser
- Download file 5.37 kB
-
https://huggingface.co/spaces/Shivam4096/Financial2API/resolve/main/storage/cache.py
- Command line
-
hf download hf://spaces/Shivam4096/Financial2API/storage/cache.py
-
curl -L -o cache.py https://huggingface.co/spaces/Shivam4096/Financial2API/resolve/main/storage/cache.py
5.37 kB
| """ | |
| storage/cache.py β SQLite-backed search history and financial data cache. | |
| """ | |
| import sqlite3 | |
| import json | |
| import datetime | |
| from pathlib import Path | |
| from typing import Optional, List, Dict, Any | |
| import sys | |
| sys.path.insert(0, str(Path(__file__).parent.parent)) | |
| from config import DB_PATH, CACHE_TTL_DAYS | |
| # ββ Database setup ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def _connect() -> sqlite3.Connection: | |
| conn = sqlite3.connect(DB_PATH) | |
| conn.row_factory = sqlite3.Row | |
| return conn | |
| def init_db() -> None: | |
| """Create tables if they don't exist.""" | |
| with _connect() as conn: | |
| conn.executescript(""" | |
| CREATE TABLE IF NOT EXISTS searches ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| company_name TEXT NOT NULL, | |
| ticker TEXT, | |
| exchange TEXT, | |
| searched_at TEXT NOT NULL, | |
| models_used TEXT, | |
| output_docx TEXT, | |
| output_xlsx TEXT, | |
| status TEXT DEFAULT 'completed' | |
| ); | |
| CREATE TABLE IF NOT EXISTS financial_cache ( | |
| ticker TEXT NOT NULL, | |
| data_type TEXT NOT NULL, | |
| fetched_at TEXT NOT NULL, | |
| expires_at TEXT NOT NULL, | |
| raw_json TEXT NOT NULL, | |
| PRIMARY KEY (ticker, data_type) | |
| ); | |
| CREATE INDEX IF NOT EXISTS idx_searches_ticker ON searches(ticker); | |
| CREATE INDEX IF NOT EXISTS idx_cache_ticker ON financial_cache(ticker); | |
| """) | |
| # ββ Search history ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def add_search( | |
| company_name: str, | |
| ticker: str, | |
| exchange: str, | |
| models_used: List[str], | |
| output_docx: str = "", | |
| output_xlsx: str = "", | |
| status: str = "completed", | |
| ) -> int: | |
| """Record a completed analysis in search history. Returns row id.""" | |
| now = datetime.datetime.now().isoformat(timespec="seconds") | |
| with _connect() as conn: | |
| cur = conn.execute( | |
| """INSERT INTO searches | |
| (company_name, ticker, exchange, searched_at, models_used, output_docx, output_xlsx, status) | |
| VALUES (?, ?, ?, ?, ?, ?, ?, ?)""", | |
| (company_name, ticker, exchange, now, json.dumps(models_used), output_docx, output_xlsx, status), | |
| ) | |
| return cur.lastrowid | |
| def update_search_outputs(row_id: int, output_docx: str, output_xlsx: str, status: str = "completed") -> None: | |
| with _connect() as conn: | |
| conn.execute( | |
| "UPDATE searches SET output_docx=?, output_xlsx=?, status=? WHERE id=?", | |
| (output_docx, output_xlsx, status, row_id), | |
| ) | |
| def get_history(limit: int = 50) -> List[Dict]: | |
| """Return recent searches, newest first.""" | |
| with _connect() as conn: | |
| rows = conn.execute( | |
| "SELECT * FROM searches ORDER BY searched_at DESC LIMIT ?", (limit,) | |
| ).fetchall() | |
| return [dict(r) for r in rows] | |
| def delete_history_entry(row_id: int) -> None: | |
| with _connect() as conn: | |
| conn.execute("DELETE FROM searches WHERE id=?", (row_id,)) | |
| # ββ Financial data cache ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def get_cached(ticker: str, data_type: str) -> Optional[Any]: | |
| """Return cached JSON data if still valid, else None.""" | |
| now = datetime.datetime.now().isoformat() | |
| with _connect() as conn: | |
| row = conn.execute( | |
| "SELECT raw_json, expires_at FROM financial_cache WHERE ticker=? AND data_type=?", | |
| (ticker.upper(), data_type), | |
| ).fetchone() | |
| if row is None: | |
| return None | |
| if row["expires_at"] < now: | |
| # Expired | |
| return None | |
| return json.loads(row["raw_json"]) | |
| def set_cache(ticker: str, data_type: str, data: Any) -> None: | |
| """Store data in cache with TTL.""" | |
| now = datetime.datetime.now() | |
| expires = (now + datetime.timedelta(days=CACHE_TTL_DAYS)).isoformat() | |
| with _connect() as conn: | |
| conn.execute( | |
| """INSERT OR REPLACE INTO financial_cache | |
| (ticker, data_type, fetched_at, expires_at, raw_json) | |
| VALUES (?, ?, ?, ?, ?)""", | |
| (ticker.upper(), data_type, now.isoformat(), expires, json.dumps(data)), | |
| ) | |
| def clear_cache(ticker: str) -> None: | |
| """Remove all cached data for a ticker (force refresh).""" | |
| with _connect() as conn: | |
| conn.execute("DELETE FROM financial_cache WHERE ticker=?", (ticker.upper(),)) | |
| def cache_stats() -> Dict: | |
| """Return some stats for diagnostics.""" | |
| with _connect() as conn: | |
| total_searches = conn.execute("SELECT COUNT(*) FROM searches").fetchone()[0] | |
| cached_tickers = conn.execute( | |
| "SELECT COUNT(DISTINCT ticker) FROM financial_cache" | |
| ).fetchone()[0] | |
| return {"total_searches": total_searches, "cached_tickers": cached_tickers} | |
| # Initialise on import | |
| init_db() | |