Files
All-in-one-Financial-Analysis/atlas-terminal/server/db/pg_cache_repo.py
T
shawnkim1997andClaude Opus 4.6 b2acda81ee feat: add Atlas Terminal — Next.js 14 + FastAPI full-stack migration
Complete migration from Streamlit to Next.js 14 App Router + FastAPI backend.

Frontend (Next.js 14):
- 10 pages: Overview, Research, Valuation, Technical, Markets, Earnings, News, Portfolio, Filings, Settings
- Terminal Noir dark theme with custom Tailwind config
- TradingView Lightweight Charts for candlestick/volume
- Valuation: DCF, Sensitivity Matrix, Monte Carlo, Tornado, Reverse DCF
- Financial Statements table with YoY growth badges and margin rows
- SEC EDGAR inline filing viewer with section tabs
- News split-view with iframe article embedding
- Technical Analysis with RSI, MACD, Bollinger, Fibonacci, Moving Averages
- Earnings beat/miss visualization
- AI Copilot chat panel with Gemini integration

Backend (FastAPI):
- 13 routers: market_data, financials, valuation, technical, earnings, insider, edgar, news, portfolio, analysis, chat, estimates, fx
- Services: DCF engine, Monte Carlo simulation, sensitivity analysis, risk metrics, SEC parser, technical indicators
- yfinance + yahooquery data sources with fallback pattern
- SQLite caching layer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 02:10:10 +00:00

63 lines
1.9 KiB
Python

"""
PostgreSQL cache repository — persistent cache with TTL.
"""
import json
from typing import Optional, Any
from datetime import datetime, timezone, timedelta
from server.db.pg_database import get_pg_pool
async def pg_cache_get(key: str) -> Optional[Any]:
"""Get a cached value. Returns None if expired or not found."""
pool = await get_pg_pool()
if not pool:
return None
async with pool.acquire() as conn:
row = await conn.fetchrow(
"SELECT value FROM cache WHERE key = $1 AND expires_at > NOW()",
key,
)
if row and row["value"] is not None:
return row["value"] # JSONB auto-deserializes
return None
async def pg_cache_set(key: str, value: Any, ttl_seconds: int = 86400) -> None:
"""Set a cache value with TTL."""
pool = await get_pg_pool()
if not pool:
return
expires = datetime.now(timezone.utc) + timedelta(seconds=ttl_seconds)
async with pool.acquire() as conn:
await conn.execute(
"""INSERT INTO cache (key, value, expires_at)
VALUES ($1, $2::jsonb, $3)
ON CONFLICT (key) DO UPDATE SET value = $2::jsonb, expires_at = $3""",
key, json.dumps(value), expires,
)
async def pg_cache_delete(key: str) -> None:
"""Delete a specific cache entry."""
pool = await get_pg_pool()
if not pool:
return
async with pool.acquire() as conn:
await conn.execute("DELETE FROM cache WHERE key = $1", key)
async def pg_cache_cleanup() -> int:
"""Remove expired cache entries. Returns count of deleted rows."""
pool = await get_pg_pool()
if not pool:
return 0
async with pool.acquire() as conn:
result = await conn.execute(
"DELETE FROM cache WHERE expires_at < NOW()"
)
# Parse "DELETE N" result
try:
return int(result.split()[-1])
except (IndexError, ValueError):
return 0