197 lines
7.2 KiB
Python
197 lines
7.2 KiB
Python
|
|
from pathlib import Path
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
from app.core.config import (
|
||
|
|
DAILY_STATS_DIR,
|
||
|
|
MINUTE_SNAPSHOTS_DIR,
|
||
|
|
PUSH_RECORDS_DIR,
|
||
|
|
RAW_PAYLOADS_DIR,
|
||
|
|
SOURCE_DIAGNOSTICS_FILE,
|
||
|
|
SYSTEM_CONFIG_FILE,
|
||
|
|
)
|
||
|
|
from app.repositories.json_repository import JsonRepository
|
||
|
|
from app.repositories.mysql_repository import MySQLRepository
|
||
|
|
|
||
|
|
|
||
|
|
class MonitoringRepository:
|
||
|
|
def __init__(self) -> None:
|
||
|
|
self.system_config_repo = JsonRepository(SYSTEM_CONFIG_FILE)
|
||
|
|
self.source_diagnostics_repo = JsonRepository(SOURCE_DIAGNOSTICS_FILE)
|
||
|
|
self.history_repo = JsonRepository(DAILY_STATS_DIR / "summary.json")
|
||
|
|
self.push_records_repo = JsonRepository(PUSH_RECORDS_DIR / "records.json")
|
||
|
|
self._mysql_repository: MySQLRepository | None = None
|
||
|
|
|
||
|
|
def _get_bootstrap_config(self) -> dict[str, Any]:
|
||
|
|
return self.system_config_repo.read({})
|
||
|
|
|
||
|
|
def _should_use_mysql(self) -> bool:
|
||
|
|
config = self._get_bootstrap_config()
|
||
|
|
return bool(
|
||
|
|
config.get("storage_backend") == "mysql"
|
||
|
|
and config.get("mysql_enabled")
|
||
|
|
and config.get("mysql_host")
|
||
|
|
and config.get("mysql_database")
|
||
|
|
and config.get("mysql_username")
|
||
|
|
)
|
||
|
|
|
||
|
|
def _mysql(self) -> MySQLRepository | None:
|
||
|
|
if not self._should_use_mysql():
|
||
|
|
return None
|
||
|
|
if self._mysql_repository is None:
|
||
|
|
self._mysql_repository = MySQLRepository(self._get_bootstrap_config())
|
||
|
|
return self._mysql_repository
|
||
|
|
|
||
|
|
def get_system_config(self) -> dict:
|
||
|
|
mysql = self._mysql()
|
||
|
|
if mysql is None:
|
||
|
|
return self.system_config_repo.read()
|
||
|
|
payload = mysql.read_document("system_config", "default", self.system_config_repo.read({}))
|
||
|
|
if payload:
|
||
|
|
return payload
|
||
|
|
fallback = self.system_config_repo.read({})
|
||
|
|
if fallback:
|
||
|
|
mysql.write_document("system_config", "default", fallback)
|
||
|
|
return fallback
|
||
|
|
|
||
|
|
def save_system_config(self, payload: dict) -> None:
|
||
|
|
self.system_config_repo.write(payload)
|
||
|
|
mysql = self._mysql()
|
||
|
|
if mysql is not None:
|
||
|
|
mysql.write_document("system_config", "default", payload)
|
||
|
|
|
||
|
|
def get_source_diagnostics(self) -> dict:
|
||
|
|
mysql = self._mysql()
|
||
|
|
if mysql is None:
|
||
|
|
return self.source_diagnostics_repo.read()
|
||
|
|
payload = mysql.read_document("source_diagnostics", "default", self.source_diagnostics_repo.read({}))
|
||
|
|
if payload:
|
||
|
|
return payload
|
||
|
|
fallback = self.source_diagnostics_repo.read({})
|
||
|
|
if fallback:
|
||
|
|
mysql.write_document("source_diagnostics", "default", fallback)
|
||
|
|
return fallback
|
||
|
|
|
||
|
|
def save_source_diagnostics(self, payload: dict) -> None:
|
||
|
|
self.source_diagnostics_repo.write(payload)
|
||
|
|
mysql = self._mysql()
|
||
|
|
if mysql is not None:
|
||
|
|
mysql.write_document("source_diagnostics", "default", payload)
|
||
|
|
|
||
|
|
def get_snapshot_by_trade_date(self, trade_date: str) -> dict:
|
||
|
|
mysql = self._mysql()
|
||
|
|
if mysql is not None:
|
||
|
|
payload = mysql.read_document("minute_snapshot", trade_date, {})
|
||
|
|
if payload:
|
||
|
|
return payload
|
||
|
|
path = MINUTE_SNAPSHOTS_DIR / f"{trade_date}.json"
|
||
|
|
return JsonRepository(path).read({})
|
||
|
|
|
||
|
|
def get_latest_snapshot(self) -> dict:
|
||
|
|
mysql = self._mysql()
|
||
|
|
if mysql is not None:
|
||
|
|
rows = mysql.list_documents("minute_snapshot", limit=1)
|
||
|
|
if rows:
|
||
|
|
return rows[0]
|
||
|
|
files = sorted(MINUTE_SNAPSHOTS_DIR.glob("*.json"))
|
||
|
|
if not files:
|
||
|
|
return {}
|
||
|
|
return JsonRepository(files[-1]).read()
|
||
|
|
|
||
|
|
def save_snapshot(self, trade_date: str, payload: dict) -> None:
|
||
|
|
JsonRepository(MINUTE_SNAPSHOTS_DIR / f"{trade_date}.json").write(payload)
|
||
|
|
mysql = self._mysql()
|
||
|
|
if mysql is not None:
|
||
|
|
mysql.write_document("minute_snapshot", trade_date, payload, sort_value=trade_date)
|
||
|
|
|
||
|
|
def get_history(self) -> dict:
|
||
|
|
mysql = self._mysql()
|
||
|
|
if mysql is None:
|
||
|
|
return self.history_repo.read()
|
||
|
|
payload = mysql.read_document("history_summary", "default", self.history_repo.read({}))
|
||
|
|
if payload:
|
||
|
|
return payload
|
||
|
|
fallback = self.history_repo.read({})
|
||
|
|
if fallback:
|
||
|
|
mysql.write_document("history_summary", "default", fallback)
|
||
|
|
return fallback
|
||
|
|
|
||
|
|
def save_history(self, payload: dict) -> None:
|
||
|
|
self.history_repo.write(payload)
|
||
|
|
mysql = self._mysql()
|
||
|
|
if mysql is not None:
|
||
|
|
mysql.write_document("history_summary", "default", payload)
|
||
|
|
|
||
|
|
def get_push_records(self) -> dict:
|
||
|
|
mysql = self._mysql()
|
||
|
|
if mysql is not None:
|
||
|
|
records = mysql.list_documents("push_record")
|
||
|
|
if records:
|
||
|
|
return {"records": records}
|
||
|
|
return self.push_records_repo.read({"records": []})
|
||
|
|
|
||
|
|
def save_push_records(self, payload: dict) -> None:
|
||
|
|
self.push_records_repo.write(payload)
|
||
|
|
mysql = self._mysql()
|
||
|
|
if mysql is not None:
|
||
|
|
for record in payload.get("records", []):
|
||
|
|
mysql.write_document(
|
||
|
|
"push_record",
|
||
|
|
record["id"],
|
||
|
|
record,
|
||
|
|
sort_value=record.get("triggered_at"),
|
||
|
|
)
|
||
|
|
|
||
|
|
def append_push_record(self, record: dict) -> dict:
|
||
|
|
payload = self.get_push_records()
|
||
|
|
records = payload.get("records", [])
|
||
|
|
records.insert(0, record)
|
||
|
|
payload["records"] = records
|
||
|
|
self.save_push_records(payload)
|
||
|
|
return record
|
||
|
|
|
||
|
|
def get_alert_state(self, trade_date: str) -> dict:
|
||
|
|
mysql = self._mysql()
|
||
|
|
if mysql is not None:
|
||
|
|
payload = mysql.read_document("alert_state", trade_date, {})
|
||
|
|
if payload:
|
||
|
|
return payload
|
||
|
|
return {}
|
||
|
|
|
||
|
|
def save_alert_state(self, trade_date: str, payload: dict) -> None:
|
||
|
|
mysql = self._mysql()
|
||
|
|
if mysql is not None:
|
||
|
|
mysql.write_document("alert_state", trade_date, payload, sort_value=trade_date)
|
||
|
|
|
||
|
|
def save_raw_payload(self, name: str, payload: dict) -> Path:
|
||
|
|
path = RAW_PAYLOADS_DIR / f"{name}.json"
|
||
|
|
JsonRepository(path).write(payload)
|
||
|
|
mysql = self._mysql()
|
||
|
|
if mysql is not None:
|
||
|
|
mysql.write_document("raw_payload", name, payload, sort_value=name)
|
||
|
|
return path
|
||
|
|
|
||
|
|
def get_document(self, category: str, doc_key: str, default: dict | None = None) -> dict:
|
||
|
|
mysql = self._mysql()
|
||
|
|
if mysql is not None:
|
||
|
|
payload = mysql.read_document(category, doc_key, default or {})
|
||
|
|
if payload:
|
||
|
|
return payload
|
||
|
|
return default or {}
|
||
|
|
|
||
|
|
def save_document(self, category: str, doc_key: str, payload: dict, *, sort_value: str | None = None) -> None:
|
||
|
|
mysql = self._mysql()
|
||
|
|
if mysql is not None:
|
||
|
|
mysql.write_document(category, doc_key, payload, sort_value=sort_value)
|
||
|
|
|
||
|
|
def list_documents(
|
||
|
|
self,
|
||
|
|
category: str,
|
||
|
|
*,
|
||
|
|
limit: int | None = None,
|
||
|
|
descending: bool = True,
|
||
|
|
) -> list[dict]:
|
||
|
|
mysql = self._mysql()
|
||
|
|
if mysql is None:
|
||
|
|
return []
|
||
|
|
return mysql.list_documents(category, limit=limit, descending=descending)
|