112 lines
4.0 KiB
Python
112 lines
4.0 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from backend.data.contracts import DataUsage
|
|
from backend.data.datahub import DatahubAwareTushareClient, DatahubBridge, DatahubClient, DatahubSettings
|
|
from backend.data.datahub.ifind_proxy import HubIfindProxy
|
|
from backend.data.datahub.realtime_proxy import HubRealtimeProxy
|
|
from backend.data.policy import DataSourcePolicy
|
|
from backend.data.providers import IfindProvider
|
|
from backend.data.quality import DataQualityGate, QualityEvidence, QualityReport
|
|
from backend.features.market.charts import MarketChartClient
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class DataGateway:
|
|
policy: DataSourcePolicy
|
|
quality: DataQualityGate
|
|
ifind_provider: IfindProvider
|
|
chart_data: MarketChartClient
|
|
realtime_observer: HubRealtimeProxy
|
|
datahub: DatahubBridge
|
|
|
|
@property
|
|
def ifind(self) -> HubIfindProxy:
|
|
return self.ifind_provider.client
|
|
|
|
def tushare(
|
|
self,
|
|
dataset_id: str = "",
|
|
usage: DataUsage = "calculation",
|
|
) -> DatahubAwareTushareClient:
|
|
if dataset_id:
|
|
self.policy.assert_allowed(dataset_id, "tushare", usage)
|
|
return DatahubAwareTushareClient(self.datahub)
|
|
|
|
def dataset_status(self, trade_date: str) -> list[dict[str, Any]] | None:
|
|
return self.datahub.dataset_status(trade_date)
|
|
|
|
def batches(self, trade_date: str, dataset: str = "") -> list[dict[str, Any]] | None:
|
|
return self.datahub.batches(trade_date, dataset)
|
|
|
|
def datahub_status(self) -> dict[str, Any]:
|
|
from backend.data.datahub.route_state import DATASET_LABELS, LEDGER
|
|
from backend.data.datahub.settings import DATASETS
|
|
|
|
settings = self.datahub.settings
|
|
flags = []
|
|
enabled = 0
|
|
for name in DATASETS:
|
|
read = bool(settings.flags(name).read)
|
|
if read:
|
|
enabled += 1
|
|
flags.append({"dataset": name, "label": DATASET_LABELS.get(name, name), "read": read})
|
|
routes = LEDGER.snapshot()
|
|
fallbacks = [item for item in routes if item.get("route") == "legacy"]
|
|
return {
|
|
"configured": bool(settings.token and settings.base_url),
|
|
"base_url": settings.base_url,
|
|
"enabled_reads": enabled,
|
|
"total_reads": len(DATASETS),
|
|
"flags": flags,
|
|
"routes": routes,
|
|
"fallback_count": len(fallbacks),
|
|
"fallback_labels": [str(item.get("label") or item.get("dataset")) for item in fallbacks],
|
|
}
|
|
|
|
def assert_source(self, dataset_id: str, provider_id: str, usage: DataUsage) -> None:
|
|
self.policy.assert_allowed(dataset_id, provider_id, usage)
|
|
|
|
def provider_chain(self, dataset_id: str, usage: DataUsage) -> tuple[str, ...]:
|
|
dataset = self.policy.dataset(dataset_id)
|
|
allowed = []
|
|
for provider_id in dataset.providers:
|
|
try:
|
|
self.policy.assert_allowed(dataset_id, provider_id, usage)
|
|
except Exception:
|
|
continue
|
|
allowed.append(provider_id)
|
|
if not allowed:
|
|
raise RuntimeError(f"No permitted provider for {dataset_id} ({usage})")
|
|
return tuple(allowed)
|
|
|
|
def require_quality(
|
|
self,
|
|
evidence: QualityEvidence,
|
|
usage: DataUsage,
|
|
as_of: str | datetime | None = None,
|
|
) -> QualityReport:
|
|
return self.quality.require(evidence, usage, as_of)
|
|
|
|
|
|
def build_data_gateway(
|
|
credentials: dict[str, object],
|
|
datahub_settings: DatahubSettings | None = None,
|
|
) -> DataGateway:
|
|
policy = DataSourcePolicy.load()
|
|
settings = datahub_settings or DatahubSettings.load(credentials=credentials)
|
|
datahub_client = DatahubClient(settings)
|
|
datahub = DatahubBridge(settings, datahub_client)
|
|
ifind = HubIfindProxy(datahub)
|
|
return DataGateway(
|
|
policy=policy,
|
|
quality=DataQualityGate.load(policy),
|
|
ifind_provider=IfindProvider(ifind),
|
|
chart_data=MarketChartClient(datahub),
|
|
realtime_observer=HubRealtimeProxy(datahub),
|
|
datahub=datahub,
|
|
)
|