Spaces:
Runtime error
Runtime error
File size: 7,927 Bytes
1f3eae2 6c99e86 17f5def 6c99e86 1f3eae2 7b79a45 1f3eae2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | """
Two-level DuckDB cache.
Level 1 β full route result (72-hour TTL, keyed by country+src+dst+vehicle_class hash).
Level 2 β per-plaza rate (72-hour TTL, keyed by plaza_id+vehicle_class).
OSM plaza reference table (refreshed every 7 days per country).
API usage log for TollGuru quota tracking.
"""
import duckdb
import json
import hashlib
import threading
from datetime import datetime, timedelta
from typing import Optional, List, Dict
import config
_lock = threading.Lock()
def _conn() -> duckdb.DuckDBPyConnection:
return duckdb.connect(config.DB_PATH)
def init_db():
with _lock:
con = _conn()
con.execute("""
CREATE TABLE IF NOT EXISTS osm_plazas (
plaza_id VARCHAR PRIMARY KEY,
plaza_name VARCHAR,
highway_number VARCHAR,
latitude DOUBLE,
longitude DOUBLE,
country VARCHAR,
last_updated TIMESTAMP
)
""")
con.execute("""
CREATE TABLE IF NOT EXISTS plaza_rate_cache (
plaza_id VARCHAR,
vehicle_class VARCHAR,
rate DOUBLE,
fasttag_rate DOUBLE,
cash_rate DOUBLE,
currency VARCHAR,
fetched_at TIMESTAMP,
source VARCHAR,
country VARCHAR,
PRIMARY KEY (plaza_id, vehicle_class)
)
""")
con.execute("""
CREATE TABLE IF NOT EXISTS route_cache (
route_key VARCHAR PRIMARY KEY,
country VARCHAR,
source_location VARCHAR,
destination_location VARCHAR,
vehicle_class VARCHAR,
full_result_json VARCHAR,
cached_at TIMESTAMP
)
""")
con.execute("""
CREATE TABLE IF NOT EXISTS api_usage_log (
id INTEGER,
service VARCHAR,
event_type VARCHAR,
event_time TIMESTAMP,
details VARCHAR
)
""")
con.execute("""
CREATE SEQUENCE IF NOT EXISTS api_log_seq START 1
""")
con.close()
# Seed static plaza data so the app works even before Overpass responds
try:
from src.seed_data import ensure_seeds_in_db
ensure_seeds_in_db("India")
ensure_seeds_in_db("United States")
except Exception:
pass
# ββ Route cache ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def make_route_key(country: str, source: str, destination: str, vehicle_class: str) -> str:
from src.seed_data import SEED_DATA_VERSION
raw = (
f"{country.lower()}|{source.lower().strip()}|"
f"{destination.lower().strip()}|{vehicle_class.lower()}|{SEED_DATA_VERSION}"
)
return hashlib.md5(raw.encode()).hexdigest()
def get_route_cache(route_key: str) -> Optional[Dict]:
cutoff = datetime.utcnow() - timedelta(hours=config.ROUTE_CACHE_TTL_HOURS)
con = _conn()
row = con.execute(
"SELECT full_result_json FROM route_cache WHERE route_key = ? AND cached_at > ?",
[route_key, cutoff],
).fetchone()
con.close()
return json.loads(row[0]) if row else None
def set_route_cache(
route_key: str,
country: str,
source: str,
destination: str,
vehicle_class: str,
result: Dict,
):
con = _conn()
con.execute(
"""INSERT OR REPLACE INTO route_cache
(route_key, country, source_location, destination_location,
vehicle_class, full_result_json, cached_at)
VALUES (?,?,?,?,?,?,?)""",
[route_key, country, source, destination, vehicle_class,
json.dumps(result, default=str), datetime.utcnow()],
)
con.close()
# ββ Plaza rate cache (Level 2) ββββββββββββββββββββββββββββββββββββββββββββββββ
def get_plaza_rate(plaza_id: str, vehicle_class: str) -> Optional[Dict]:
cutoff = datetime.utcnow() - timedelta(hours=config.PLAZA_CACHE_TTL_HOURS)
con = _conn()
row = con.execute(
"""SELECT rate, fasttag_rate, cash_rate, currency, source
FROM plaza_rate_cache
WHERE plaza_id = ? AND vehicle_class = ? AND fetched_at > ?""",
[plaza_id, vehicle_class, cutoff],
).fetchone()
con.close()
if row:
return {"rate": row[0], "fasttag_rate": row[1], "cash_rate": row[2],
"currency": row[3], "source": row[4]}
return None
def set_plaza_rate(
plaza_id: str,
vehicle_class: str,
rate: float,
fasttag_rate: float,
cash_rate: float,
currency: str,
source: str,
country: str,
):
con = _conn()
con.execute(
"""INSERT OR REPLACE INTO plaza_rate_cache
(plaza_id, vehicle_class, rate, fasttag_rate, cash_rate,
currency, fetched_at, source, country)
VALUES (?,?,?,?,?,?,?,?,?)""",
[plaza_id, vehicle_class, rate, fasttag_rate, cash_rate,
currency, datetime.utcnow(), source, country],
)
con.close()
# ββ OSM plaza reference βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def get_osm_plazas(country: str) -> List[Dict]:
con = _conn()
rows = con.execute(
"""SELECT plaza_id, plaza_name, highway_number, latitude, longitude
FROM osm_plazas WHERE country = ?""",
[country],
).fetchall()
con.close()
return [
{"plaza_id": r[0], "plaza_name": r[1], "highway_number": r[2],
"latitude": r[3], "longitude": r[4]}
for r in rows
]
def get_osm_last_updated(country: str) -> Optional[datetime]:
con = _conn()
row = con.execute(
"SELECT MAX(last_updated) FROM osm_plazas WHERE country = ?", [country]
).fetchone()
con.close()
return row[0] if (row and row[0]) else None
def upsert_osm_plazas(plazas: List[Dict], country: str):
con = _conn()
con.execute("DELETE FROM osm_plazas WHERE country = ?", [country])
now = datetime.utcnow()
for p in plazas:
con.execute(
"""INSERT INTO osm_plazas
(plaza_id, plaza_name, highway_number, latitude, longitude, country, last_updated)
VALUES (?,?,?,?,?,?,?)""",
[p["plaza_id"], p["plaza_name"], p["highway_number"],
p["latitude"], p["longitude"], country, now],
)
con.close()
# ββ API usage log βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def log_api_event(service: str, event_type: str, details: str = ""):
con = _conn()
con.execute(
"""INSERT INTO api_usage_log (id, service, event_type, event_time, details)
VALUES (nextval('api_log_seq'),?,?,?,?)""",
[service, event_type, datetime.utcnow(), details],
)
con.close()
def is_tollguru_limited() -> bool:
"""True if TollGuru was rate-limited at any point this calendar month."""
first_of_month = datetime.utcnow().replace(day=1, hour=0, minute=0, second=0, microsecond=0)
con = _conn()
row = con.execute(
"""SELECT COUNT(*) FROM api_usage_log
WHERE service = 'tollguru' AND event_type = 'rate_limited' AND event_time >= ?""",
[first_of_month],
).fetchone()
con.close()
return (row[0] > 0) if row else False
|