fix: analysis 500s, caching, alert page perf
P0: Fix KeyError in 3 analysis endpoints. geojson.py stores 1d risk as "risk_value" but analysis.py accessed "risk_1d" — always crashed. Backend: Add lru_cache to GeoJSON/CSV/Parquet loaders, date helpers, and district loader. Add try/except and FileNotFoundError guards. Frontend: Debounce riskRange, merge counts into useMemo, stabilize handleGridClick with ref, memoize nearest-grid scan, wrap AlertMap in React.memo, switch useLodGrid from fetch to cachedGet.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
from fastapi import APIRouter, HTTPException, Query
|
||||
from datetime import datetime, timedelta
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
import logging
|
||||
@@ -22,6 +23,12 @@ from models import (
|
||||
router = APIRouter(prefix="/api", tags=["grid"])
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _load_parquet(path: Path) -> "pd.DataFrame":
|
||||
import pandas as pd
|
||||
return pd.read_parquet(path)
|
||||
|
||||
|
||||
@router.get("/history/aggregated", response_model=HistoricalAggregationResponse)
|
||||
async def get_historical_aggregated(
|
||||
start_date: str = Query(..., description="Start date (YYYY-MM-DD)"),
|
||||
@@ -45,7 +52,13 @@ async def get_historical_aggregated(
|
||||
|
||||
import pandas as pd
|
||||
|
||||
cases_df = pd.read_parquet(PROJECT_ROOT / "processed" / "cases_by_district_daily.parquet")
|
||||
try:
|
||||
cases_df = _load_parquet(PROJECT_ROOT / "processed" / "cases_by_district_daily.parquet")
|
||||
except FileNotFoundError:
|
||||
return HistoricalAggregationResponse(
|
||||
aggregations=[], total_records=0,
|
||||
date_range=(start_date, end_date), timestamp=datetime.now().isoformat(),
|
||||
)
|
||||
cases_df['date'] = pd.to_datetime(cases_df['date'])
|
||||
|
||||
filtered_cases = cases_df[
|
||||
@@ -78,7 +91,10 @@ async def get_historical_aggregated(
|
||||
grouped = filtered_cases.copy()
|
||||
grouped['date'] = grouped['date'].dt.strftime('%Y-%m-%d')
|
||||
|
||||
weather_df = pd.read_parquet(PROJECT_ROOT / "processed" / "weather" / "station_daily_2022.parquet")
|
||||
try:
|
||||
weather_df = _load_parquet(PROJECT_ROOT / "processed" / "weather" / "station_daily_2022.parquet")
|
||||
except FileNotFoundError:
|
||||
weather_df = pd.DataFrame(columns=['date', 'AQI', 'PM25', 'PM10'])
|
||||
weather_df['date'] = pd.to_datetime(weather_df['date']).dt.strftime('%Y-%m-%d')
|
||||
|
||||
# Weather data doesn't have district - aggregate by date only
|
||||
@@ -124,12 +140,12 @@ async def get_grids_geojson(
|
||||
import pandas as pd
|
||||
|
||||
try:
|
||||
grid_df = pd.read_parquet(PROJECT_ROOT / "processed" / "grid_100m_index.parquet")
|
||||
grid_df = _load_parquet(PROJECT_ROOT / "processed" / "grid_100m_index.parquet")
|
||||
except FileNotFoundError:
|
||||
return GridGeoJSONResponse(type="FeatureCollection", features=[], timestamp=datetime.now().isoformat())
|
||||
|
||||
try:
|
||||
district_map = pd.read_parquet(PROJECT_ROOT / "processed" / "grid_district_mapping.parquet")
|
||||
district_map = _load_parquet(PROJECT_ROOT / "processed" / "grid_district_mapping.parquet")
|
||||
except FileNotFoundError:
|
||||
return GridGeoJSONResponse(type="FeatureCollection", features=[], timestamp=datetime.now().isoformat())
|
||||
|
||||
@@ -139,7 +155,7 @@ async def get_grids_geojson(
|
||||
merged = merged[merged['district_name'].str.contains(district.replace('区', ''), na=False, regex=False)]
|
||||
|
||||
try:
|
||||
cases_df = pd.read_parquet(PROJECT_ROOT / "processed" / "cases_by_district_daily.parquet")
|
||||
cases_df = _load_parquet(PROJECT_ROOT / "processed" / "cases_by_district_daily.parquet")
|
||||
except FileNotFoundError:
|
||||
return GridGeoJSONResponse(type="FeatureCollection", features=[], timestamp=datetime.now().isoformat())
|
||||
cases_df['date'] = pd.to_datetime(cases_df['date']).dt.strftime('%Y-%m-%d')
|
||||
@@ -313,7 +329,7 @@ async def get_grid_history(
|
||||
"""
|
||||
import pandas as pd
|
||||
|
||||
district_map = pd.read_parquet(PROJECT_ROOT / "processed" / "grid_district_mapping.parquet")
|
||||
district_map = _load_parquet(PROJECT_ROOT / "processed" / "grid_district_mapping.parquet")
|
||||
grid_info = district_map[district_map['grid_id'] == grid_id]
|
||||
|
||||
if len(grid_info) == 0:
|
||||
@@ -321,7 +337,7 @@ async def get_grid_history(
|
||||
|
||||
district = grid_info.iloc[0]['district_name']
|
||||
|
||||
cases_df = pd.read_parquet(PROJECT_ROOT / "processed" / "cases_by_district_daily.parquet")
|
||||
cases_df = _load_parquet(PROJECT_ROOT / "processed" / "cases_by_district_daily.parquet")
|
||||
cases_df['date'] = pd.to_datetime(cases_df['date'])
|
||||
|
||||
end_date = datetime.now()
|
||||
|
||||
Reference in New Issue
Block a user