新增 GET /api/cities/model-range 端点:返回 7 城 DEB 预测和模型区间
This commit is contained in:
+74
-2
@@ -1,8 +1,8 @@
|
|||||||
"""City and city-analysis API routes."""
|
"""City and city-analysis API routes."""
|
||||||
|
|
||||||
from typing import Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
from fastapi import APIRouter, BackgroundTasks, Request
|
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,
|
||||||
@@ -14,12 +14,84 @@ from web.services.city_api import (
|
|||||||
|
|
||||||
router = APIRouter(tags=["city"])
|
router = APIRouter(tags=["city"])
|
||||||
|
|
||||||
|
_MODEL_RANGE_CITIES: List[str] = [
|
||||||
|
"beijing",
|
||||||
|
"shanghai",
|
||||||
|
"guangzhou",
|
||||||
|
"chengdu",
|
||||||
|
"chongqing",
|
||||||
|
"qingdao",
|
||||||
|
"wuhan",
|
||||||
|
]
|
||||||
|
|
||||||
|
_MODEL_RANGE_NAMES: Dict[str, str] = {
|
||||||
|
"beijing": "北京 (ZBAA)",
|
||||||
|
"shanghai": "上海 (ZSPD)",
|
||||||
|
"guangzhou": "广州 (ZGGG)",
|
||||||
|
"chengdu": "成都 (ZUUU)",
|
||||||
|
"chongqing": "重庆 (ZUCK)",
|
||||||
|
"qingdao": "青岛 (ZSQD)",
|
||||||
|
"wuhan": "武汉 (ZHHH)",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/cities")
|
@router.get("/api/cities")
|
||||||
async def list_cities(request: Request):
|
async def list_cities(request: Request):
|
||||||
return await list_cities_payload(request)
|
return await list_cities_payload(request)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/api/cities/model-range")
|
||||||
|
async def cities_model_range(
|
||||||
|
request: Request,
|
||||||
|
force_refresh: bool = Query(False),
|
||||||
|
):
|
||||||
|
"""Return DEB prediction and model range for monitored Chinese cities."""
|
||||||
|
from web.app import _analyze
|
||||||
|
|
||||||
|
rows: List[Dict[str, Any]] = []
|
||||||
|
for city in _MODEL_RANGE_CITIES:
|
||||||
|
try:
|
||||||
|
result = _analyze(city, force_refresh=force_refresh)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
|
||||||
|
deb = result.get("deb") if isinstance(result, dict) else None
|
||||||
|
deb_pred = deb.get("prediction") if isinstance(deb, dict) else None
|
||||||
|
|
||||||
|
models = result.get("multi_model") if isinstance(result, dict) else {}
|
||||||
|
model_min: Optional[float] = None
|
||||||
|
model_max: Optional[float] = None
|
||||||
|
spread: Optional[float] = None
|
||||||
|
spread_label: str = ""
|
||||||
|
|
||||||
|
if isinstance(models, dict):
|
||||||
|
vals = sorted([v for v in models.values() if isinstance(v, (int, float))])
|
||||||
|
if len(vals) >= 2:
|
||||||
|
model_min = vals[0]
|
||||||
|
model_max = vals[-1]
|
||||||
|
spread = model_max - model_min
|
||||||
|
if spread <= 2.0:
|
||||||
|
spread_label = "低分歧"
|
||||||
|
elif spread <= 4.0:
|
||||||
|
spread_label = "中等分歧"
|
||||||
|
else:
|
||||||
|
spread_label = "高分歧"
|
||||||
|
|
||||||
|
rows.append(
|
||||||
|
{
|
||||||
|
"id": city,
|
||||||
|
"name": _MODEL_RANGE_NAMES.get(city, city),
|
||||||
|
"deb": round(deb_pred, 1) if deb_pred is not None else None,
|
||||||
|
"model_min": round(model_min, 1) if model_min is not None else None,
|
||||||
|
"model_max": round(model_max, 1) if model_max is not None else None,
|
||||||
|
"spread": round(spread, 1) if spread is not None else None,
|
||||||
|
"spread_label": spread_label,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return {"cities": rows}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/city/{name}")
|
@router.get("/api/city/{name}")
|
||||||
async def city_detail(
|
async def city_detail(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|||||||
Reference in New Issue
Block a user