修复 Polymarket 清理后的导入错误:analysis_service 移除 city_payloads market_scan 依赖
This commit is contained in:
+16
-16
@@ -32,8 +32,7 @@ from src.data_collection.city_time import get_city_utc_offset_seconds
|
|||||||
from src.database.runtime_state import IntradayPathSnapshotRepository
|
from src.database.runtime_state import IntradayPathSnapshotRepository
|
||||||
from web.services.city_payloads import (
|
from web.services.city_payloads import (
|
||||||
build_city_detail_payload as _city_payload_detail,
|
build_city_detail_payload as _city_payload_detail,
|
||||||
build_city_market_scan_payload as _city_payload_market_scan,
|
build_city_summary_payload as _city_payload_summary
|
||||||
build_city_summary_payload as _city_payload_summary,
|
|
||||||
)
|
)
|
||||||
from web.services.observation_freshness import (
|
from web.services.observation_freshness import (
|
||||||
build_observation_freshness as _build_observation_freshness,
|
build_observation_freshness as _build_observation_freshness,
|
||||||
@@ -2054,20 +2053,6 @@ def _build_city_summary_payload(data: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
return _city_payload_summary(data)
|
return _city_payload_summary(data)
|
||||||
|
|
||||||
|
|
||||||
def _build_city_market_scan_payload(
|
|
||||||
data: Dict[str, Any],
|
|
||||||
market_slug: Optional[str] = None,
|
|
||||||
target_date: Optional[str] = None,
|
|
||||||
lite: bool = False,
|
|
||||||
scan_filters: Optional[Dict[str, Any]] = None,
|
|
||||||
) -> Dict[str, Any]:
|
|
||||||
return _city_payload_market_scan(
|
|
||||||
data,
|
|
||||||
market_slug=market_slug,
|
|
||||||
target_date=target_date,
|
|
||||||
lite=lite,
|
|
||||||
scan_filters=scan_filters,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _build_city_detail_payload(
|
def _build_city_detail_payload(
|
||||||
@@ -2086,3 +2071,18 @@ def _build_city_detail_payload(
|
|||||||
# ──────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────
|
||||||
# Routes
|
# Routes
|
||||||
# ──────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _build_city_market_scan_payload(
|
||||||
|
data,
|
||||||
|
market_slug=None,
|
||||||
|
target_date=None,
|
||||||
|
lite=False,
|
||||||
|
scan_filters=None,
|
||||||
|
):
|
||||||
|
local_date = str(data.get("local_date") or "").strip()
|
||||||
|
return {
|
||||||
|
"market_scan": {"available": False},
|
||||||
|
"selected_date": target_date or local_date,
|
||||||
|
"fetched_at": data.get("updated_at"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from fastapi import APIRouter, BackgroundTasks, Query, Request
|
|||||||
from web.services.city_api import (
|
from web.services.city_api import (
|
||||||
get_city_detail_aggregate_payload,
|
get_city_detail_aggregate_payload,
|
||||||
get_city_detail_payload,
|
get_city_detail_payload,
|
||||||
get_city_market_scan_payload,
|
|
||||||
get_city_summary_payload,
|
get_city_summary_payload,
|
||||||
list_cities_payload,
|
list_cities_payload,
|
||||||
)
|
)
|
||||||
@@ -152,65 +151,6 @@ async def city_detail_aggregate(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/city/{name}/market-scan")
|
|
||||||
async def city_market_scan(
|
|
||||||
request: Request,
|
|
||||||
background_tasks: BackgroundTasks,
|
|
||||||
name: str,
|
|
||||||
force_refresh: bool = False,
|
|
||||||
market_slug: Optional[str] = None,
|
|
||||||
target_date: Optional[str] = None,
|
|
||||||
lite: bool = False,
|
|
||||||
):
|
|
||||||
return await get_city_market_scan_payload(
|
|
||||||
request,
|
|
||||||
background_tasks,
|
|
||||||
name,
|
|
||||||
force_refresh=force_refresh,
|
|
||||||
market_slug=market_slug,
|
|
||||||
target_date=target_date,
|
|
||||||
lite=lite,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/city/{name}/holders")
|
|
||||||
async def city_holders(
|
|
||||||
name: str,
|
|
||||||
limit: int = 10,
|
|
||||||
):
|
|
||||||
"""Return top token holders for a city's primary Polymarket market."""
|
|
||||||
from web.services.city_payloads import _get_polymarket_layer
|
|
||||||
from web.analysis_service import _analyze
|
|
||||||
|
|
||||||
layer = _get_polymarket_layer()
|
|
||||||
if not layer.enabled:
|
|
||||||
return {"holders": [], "available": False}
|
|
||||||
|
|
||||||
data = _analyze(name, force_refresh=False, include_llm_commentary=False, detail_mode="market")
|
|
||||||
local_date = str(data.get("local_date") or "").strip()
|
|
||||||
if not local_date:
|
|
||||||
return {"holders": [], "available": False}
|
|
||||||
|
|
||||||
scan = layer.build_market_scan(
|
|
||||||
city=name,
|
|
||||||
target_date=local_date,
|
|
||||||
include_related_buckets=False,
|
|
||||||
)
|
|
||||||
if not scan.get("available"):
|
|
||||||
return {"holders": [], "available": False}
|
|
||||||
|
|
||||||
condition_id = scan.get("selected_condition_id")
|
|
||||||
if not condition_id:
|
|
||||||
return {"holders": [], "available": False}
|
|
||||||
|
|
||||||
holders = layer.get_market_holders(condition_id, limit=limit)
|
|
||||||
return {
|
|
||||||
"holders": holders,
|
|
||||||
"available": True,
|
|
||||||
"condition_id": condition_id,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/city/{name}/realtime-stream")
|
@router.get("/api/city/{name}/realtime-stream")
|
||||||
async def city_realtime_stream(name: str):
|
async def city_realtime_stream(name: str):
|
||||||
"""Return a rolling window of recent temperature readings + market
|
"""Return a rolling window of recent temperature readings + market
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from datetime import datetime, timedelta
|
|||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
from web.core import CITIES
|
from web.core import CITIES
|
||||||
from web.analysis_service import _analyze, _build_city_market_scan_payload
|
from web.analysis_service import _analyze
|
||||||
from web.scan_city_ai_helpers import _safe_float
|
from web.scan_city_ai_helpers import _safe_float
|
||||||
from web.scan_terminal_ai_compact import _build_metar_decision_context
|
from web.scan_terminal_ai_compact import _build_metar_decision_context
|
||||||
from web.scan_terminal_filters import (
|
from web.scan_terminal_filters import (
|
||||||
@@ -139,65 +139,7 @@ def _scan_city_terminal_rows(
|
|||||||
*,
|
*,
|
||||||
force_refresh: bool = False,
|
force_refresh: bool = False,
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
# Quick mode: skip Polymarket matching, return cached analysis rows only
|
return _scan_city_terminal_rows_quick(city, filters, force_refresh=force_refresh)
|
||||||
if filters.get("skip_polymarket"):
|
|
||||||
return _scan_city_terminal_rows_quick(city, filters, force_refresh=force_refresh)
|
|
||||||
|
|
||||||
# Try cached analysis first; force-refresh if probability distribution is missing
|
|
||||||
data = _analyze(
|
|
||||||
city,
|
|
||||||
force_refresh=force_refresh,
|
|
||||||
include_llm_commentary=False,
|
|
||||||
detail_mode="market",
|
|
||||||
)
|
|
||||||
probs = data.get("probabilities") or {}
|
|
||||||
if not probs.get("distribution") and not force_refresh:
|
|
||||||
data = _analyze(
|
|
||||||
city,
|
|
||||||
force_refresh=True,
|
|
||||||
include_llm_commentary=False,
|
|
||||||
detail_mode="market",
|
|
||||||
)
|
|
||||||
target_dates = _resolve_time_range_dates(data, filters["time_range"])
|
|
||||||
rows: List[Dict[str, Any]] = []
|
|
||||||
primary_scores: List[float] = []
|
|
||||||
candidate_total = 0
|
|
||||||
|
|
||||||
for target_date in target_dates:
|
|
||||||
payload = _build_city_market_scan_payload(
|
|
||||||
data,
|
|
||||||
market_slug=None,
|
|
||||||
target_date=target_date,
|
|
||||||
lite=True,
|
|
||||||
scan_filters=filters,
|
|
||||||
)
|
|
||||||
scan = payload.get("market_scan") or {}
|
|
||||||
candidate_total += int(scan.get("candidate_count") or 0)
|
|
||||||
raw_rows = scan.get("scan_rows")
|
|
||||||
if not isinstance(raw_rows, list) or not raw_rows:
|
|
||||||
raw_rows = [scan.get("primary_signal")] if isinstance(scan.get("primary_signal"), dict) else []
|
|
||||||
if not raw_rows:
|
|
||||||
continue
|
|
||||||
for raw_row in raw_rows:
|
|
||||||
if not isinstance(raw_row, dict) or not raw_row:
|
|
||||||
continue
|
|
||||||
row = _build_terminal_row(
|
|
||||||
city=city,
|
|
||||||
data=data,
|
|
||||||
scan=scan,
|
|
||||||
row=raw_row,
|
|
||||||
)
|
|
||||||
rows.append(row)
|
|
||||||
score = _safe_float(row.get("final_score"))
|
|
||||||
if score is not None and row.get("is_primary_signal"):
|
|
||||||
primary_scores.append(score)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"city": city,
|
|
||||||
"rows": rows,
|
|
||||||
"candidate_total": candidate_total,
|
|
||||||
"primary_scores": primary_scores,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _scan_city_terminal_rows_quick(
|
def _scan_city_terminal_rows_quick(
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
from fastapi import BackgroundTasks, HTTPException, Request
|
from fastapi import HTTPException, Request
|
||||||
from fastapi.concurrency import run_in_threadpool
|
from fastapi.concurrency import run_in_threadpool
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
@@ -165,67 +165,4 @@ async def get_city_detail_aggregate_payload(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def get_city_market_scan_payload(
|
|
||||||
request: Request,
|
|
||||||
background_tasks: BackgroundTasks,
|
|
||||||
name: str,
|
|
||||||
*,
|
|
||||||
force_refresh: bool = False,
|
|
||||||
market_slug: Optional[str] = None,
|
|
||||||
target_date: Optional[str] = None,
|
|
||||||
lite: bool = False,
|
|
||||||
) -> Dict[str, Any]:
|
|
||||||
legacy_routes._assert_entitlement(request)
|
|
||||||
city = legacy_routes._normalize_city_or_404(name)
|
|
||||||
if force_refresh:
|
|
||||||
data = await run_in_threadpool(legacy_routes._refresh_city_market_cache, city, True)
|
|
||||||
cached_scan = legacy_routes._get_cached_market_scan_payload(
|
|
||||||
data,
|
|
||||||
market_slug=market_slug,
|
|
||||||
target_date=target_date,
|
|
||||||
lite=lite,
|
|
||||||
)
|
|
||||||
if cached_scan is not None:
|
|
||||||
return cached_scan
|
|
||||||
else:
|
|
||||||
cached_entry = await run_in_threadpool(legacy_routes._CACHE_DB.get_city_cache, "market", city)
|
|
||||||
if cached_entry:
|
|
||||||
data = cached_entry.get("payload") or {}
|
|
||||||
cached_scan = legacy_routes._get_cached_market_scan_payload(
|
|
||||||
data,
|
|
||||||
market_slug=market_slug,
|
|
||||||
target_date=target_date,
|
|
||||||
lite=lite,
|
|
||||||
)
|
|
||||||
if cached_scan is not None:
|
|
||||||
if not legacy_routes._market_analysis_cache_is_fresh(cached_entry):
|
|
||||||
legacy_routes._schedule_cache_refresh(background_tasks, kind="market", city=city)
|
|
||||||
return cached_scan
|
|
||||||
if legacy_routes._market_analysis_cache_is_fresh(cached_entry):
|
|
||||||
return await run_in_threadpool(
|
|
||||||
legacy_routes._refresh_market_scan_payload_from_cached_analysis,
|
|
||||||
city,
|
|
||||||
data,
|
|
||||||
market_slug=market_slug,
|
|
||||||
target_date=target_date,
|
|
||||||
lite=lite,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
legacy_routes._schedule_cache_refresh(background_tasks, kind="market", city=city)
|
|
||||||
else:
|
|
||||||
data = await run_in_threadpool(legacy_routes._refresh_city_market_cache, city, False)
|
|
||||||
cached_scan = legacy_routes._get_cached_market_scan_payload(
|
|
||||||
data,
|
|
||||||
market_slug=market_slug,
|
|
||||||
target_date=target_date,
|
|
||||||
lite=lite,
|
|
||||||
)
|
|
||||||
if cached_scan is not None:
|
|
||||||
return cached_scan
|
|
||||||
return await run_in_threadpool(
|
|
||||||
legacy_routes._build_city_market_scan_payload,
|
|
||||||
data,
|
|
||||||
market_slug,
|
|
||||||
target_date,
|
|
||||||
lite,
|
|
||||||
)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user