From 39c792889585116e261c458e3b06da44a23e9a2a Mon Sep 17 00:00:00 2001 From: haneulai Date: Tue, 27 Jan 2026 15:34:20 +0900 Subject: [PATCH] feat: Add year parameter to stats API and extend year range --- app/routers/stats.py | 72 +++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/app/routers/stats.py b/app/routers/stats.py index 76ab501..137e70e 100644 --- a/app/routers/stats.py +++ b/app/routers/stats.py @@ -20,6 +20,7 @@ router = APIRouter( async def get_plant_stats( plant_id: str, period: Literal["day", "month", "year"] = Query("day", description="통계 기간"), + year: int = Query(None, description="특정 연도 (월별/연도별 조회 시)"), db: Client = Depends(get_db) ) -> dict: """ @@ -30,7 +31,8 @@ async def get_plant_stats( Args: plant_id: 발전소 ID - period: 'day' (최근 30일), 'month' (최근 12개월), 'year' (최근 5년) + period: 'day' (최근 30일), 'month' (최근 12개월 또는 특정 연도), 'year' (최근 연도들) + year: 특정 연도 (옵션, period='month' 시 해당 연도의 월별 데이터 반환) Returns: 차트 라이브러리 친화적 포맷 [{"label": "...", "value": ...}, ...] @@ -46,37 +48,54 @@ async def get_plant_stats( start_date = today.replace(day=1) date_filter = start_date.isoformat() elif period == "month": - start_date = today.replace(day=1) - timedelta(days=365) + # year 파라미터가 있으면 해당 연도 1월~12월, 없으면 올해 + target_year = year if year else today.year + start_date = datetime(target_year, 1, 1).date() + # 종료일: 해당 연도 12월 31일 또는 오늘 중 작은 값 + end_date = min(datetime(target_year, 12, 31).date(), today) date_filter = start_date.isoformat() else: # year - start_date = datetime(today.year - 5, 1, 1).date() + # year 파라미터가 있으면 해당 연도부터, 없으면 최근 10년 + if year: + start_year = year + else: + start_year = today.year - 9 # 10년치 데이터 (올해 포함) + start_date = datetime(start_year, 1, 1).date() + end_date = today date_filter = start_date.strftime("%Y-%m-%d") stats_query = db.table("daily_stats") \ .select("date, total_generation") \ .eq("plant_id", plant_id) \ - .gte("date", date_filter) \ - .lte("date", today_str) \ - .order("date", desc=False) + .gte("date", date_filter) + + # period별 종료일 필터 추가 + if period in ["month", "year"]: + stats_query = stats_query.lte("date", end_date.isoformat() if period == "month" else today_str) + else: + stats_query = stats_query.lte("date", today_str) + + stats_query = stats_query.order("date", desc=False) stats_result = stats_query.execute() # 데이터 맵핑 {날짜: 발전량} data_map = {row["date"]: row["total_generation"] or 0 for row in stats_result.data} - # 2. 오늘 실시간 데이터 조회 (solar_logs) - # 오늘의 가장 마지막 기록 1건만 조회 (성능 최적화) - logs_result = db.table("solar_logs") \ - .select("today_kwh, created_at") \ - .eq("plant_id", plant_id) \ - .gte("created_at", f"{today_str}T00:00:00") \ - .order("created_at", desc=True) \ - .limit(1) \ - .execute() - + # 2. 오늘 실시간 데이터 조회 (solar_logs) - 오늘이 조회 범위에 포함될 때만 today_generation = 0.0 - if logs_result.data: - today_generation = logs_result.data[0].get("today_kwh", 0.0) + if period == "day" or (period == "month" and (not year or year == today.year)) or period == "year": + # 오늘의 가장 마지막 기록 1건만 조회 (성능 최적화) + logs_result = db.table("solar_logs") \ + .select("today_kwh, created_at") \ + .eq("plant_id", plant_id) \ + .gte("created_at", f"{today_str}T00:00:00") \ + .order("created_at", desc=True) \ + .limit(1) \ + .execute() + + if logs_result.data: + today_generation = logs_result.data[0].get("today_kwh", 0.0) # 3. 데이터 병합 (오늘 데이터 갱신/추가) # solar_logs 값이 있으면 무조건 daily_stats 값보다 우선 (실시간성) @@ -105,12 +124,17 @@ async def get_plant_stats( month_key = date_str[:7] monthly[month_key] = monthly.get(month_key, 0) + val - sorted_keys = sorted(monthly.keys()) - data = [ - {"label": k, "value": round(monthly[k], 2)} - for k in sorted_keys - if k >= start_date.strftime("%Y-%m") - ] + # 특정 연도의 1~12월 데이터 생성 (누락된 월은 0) + target_year = year if year else today.year + for month in range(1, 13): + month_key = f"{target_year}-{month:02d}" + # 미래 월은 제외 + if datetime.strptime(month_key, "%Y-%m").date() > today.replace(day=1): + break + data.append({ + "label": month_key, + "value": round(monthly.get(month_key, 0), 2) + }) elif period == "year": # 연도별 집계