from __future__ import annotations from datetime import date, datetime, time from typing import Any from zoneinfo import ZoneInfo from backend.data.contracts import ProviderResult SHANGHAI = ZoneInfo("Asia/Shanghai") def auction_phase(requested: str, trade_date: str, clock: datetime) -> str: if requested != clock.date().isoformat() or trade_date != clock.date().isoformat(): return "archive" local = clock.time().replace(tzinfo=None) if local < time(9, 15): return "pending" if local < time(9, 25): return "observing" if local < time(9, 30): return "selection" return "finalized" def decorate( payload: dict[str, Any], requested: str, phase: str, carried_forward: bool, message: str, current_available: bool = True, ) -> dict[str, Any]: return { **payload, "requested_date": requested, "phase": phase, "carried_forward": carried_forward, "message": message or str(payload.get("message") or ""), "current_available": current_available, } def standard(payload: dict[str, Any], requested: str, message: str = "") -> dict[str, Any]: trade_date = str(payload.get("trade_date") or "") return { **payload, "requested_date": requested, "carried_forward": trade_date != requested, "message": message or str(payload.get("message") or ""), } def empty_standard(requested: str, trade_date: str, message: str) -> dict[str, Any]: return { "requested_date": requested, "trade_date": trade_date, "observed_at": None, "state": None, "carried_forward": trade_date != requested, "message": message, "summary": {}, "items": [], "combined": [], "ths": [], "dc": [], } def empty_auction( requested: str, trade_date: str, phase: str, message: str ) -> dict[str, Any]: return { **empty_standard(requested, trade_date, message), "phase": phase, "current_available": False, "expectations": {"超预期": 0, "符合预期": 0, "低于预期": 0}, "themes": {"carry": [], "new_themes": []}, "amount_history": [], "focus_rows": [], "one_price_rows": [], "rows": [], "watchlist_rows": [], "watchlist_ready": False, } def result(value: Any) -> ProviderResult | None: return value if isinstance(value, ProviderResult) else None def rows(value: Any) -> tuple[dict[str, Any], ...]: provider_result = result(value) return provider_result.rows if provider_result else () def serialized_rows(value: Any) -> list[dict[str, Any]] | None: provider_result = result(value) return [dict(row) for row in provider_result.rows] if provider_result else None def tuple_or_none(value: Any) -> tuple[dict[str, Any], ...] | None: if value is None: return None return tuple(dict(row) for row in value) def valid_date(value: str) -> str: try: return date.fromisoformat(value).isoformat() except ValueError as exc: raise ValueError("日期格式无效") from exc def clock(value: datetime | None) -> datetime: current = value or datetime.now(SHANGHAI) return ( current.replace(tzinfo=SHANGHAI) if current.tzinfo is None else current.astimezone(SHANGHAI) ) def number(value: Any, default: float = 0.0) -> float: try: parsed = float(value) return parsed if parsed == parsed else default except (TypeError, ValueError): return default