feat: add comparison stats api (/plants/stats/comparison)
This commit is contained in:
parent
0663458fc7
commit
f139b2abb4
|
|
@ -5,8 +5,10 @@
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, Depends, Query
|
from fastapi import APIRouter, HTTPException, Depends, Query
|
||||||
from supabase import Client
|
from supabase import Client
|
||||||
from typing import List, Literal
|
from typing import List, Literal, Optional
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
|
import calendar
|
||||||
|
import re
|
||||||
|
|
||||||
from app.core.database import get_db
|
from app.core.database import get_db
|
||||||
|
|
||||||
|
|
@ -16,6 +18,165 @@ router = APIRouter(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/stats/comparison")
|
||||||
|
async def get_all_plants_comparison(
|
||||||
|
period: Literal["day", "month", "year"] = Query("day", description="통계 기간"),
|
||||||
|
date: Optional[str] = Query(None, description="날짜 (YYYY-MM-DD)"),
|
||||||
|
year: Optional[int] = Query(None, description="연도"),
|
||||||
|
month: Optional[int] = Query(None, description="월"),
|
||||||
|
db: Client = Depends(get_db)
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
전체 발전소 발전량 비교 통계 조회
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 1. 모든 발전소 기본 정보 조회 (이름, 용량)
|
||||||
|
plants_res = db.table("plants").select("id, name, capacity").execute()
|
||||||
|
plants = {p['id']: p for p in plants_res.data}
|
||||||
|
|
||||||
|
# 결과 초기화
|
||||||
|
result_data = []
|
||||||
|
|
||||||
|
# 날짜 파라미터 처리
|
||||||
|
# KST 시간대 고려
|
||||||
|
kst_timezone = timezone(timedelta(hours=9))
|
||||||
|
now_kst = datetime.now(kst_timezone)
|
||||||
|
today = now_kst.date()
|
||||||
|
|
||||||
|
target_date = None
|
||||||
|
if date:
|
||||||
|
try:
|
||||||
|
target_date = datetime.strptime(date, "%Y-%m-%d").date()
|
||||||
|
except ValueError:
|
||||||
|
target_date = today
|
||||||
|
else:
|
||||||
|
target_date = today
|
||||||
|
|
||||||
|
target_year = year if year else target_date.year
|
||||||
|
target_month = month if month else target_date.month
|
||||||
|
|
||||||
|
# 데이터 조회 로직
|
||||||
|
stats_map = {} # plant_id -> generation
|
||||||
|
|
||||||
|
if period == "day":
|
||||||
|
date_str = target_date.isoformat()
|
||||||
|
|
||||||
|
# (A) 오늘 날짜인 경우: solar_logs 최신값 (실시간)
|
||||||
|
# 서버 시간대와 클라이언트 요청 날짜 일치 여부 확인
|
||||||
|
if target_date == today:
|
||||||
|
# 오늘 00:00:00 (KST) 이후 데이터 조회
|
||||||
|
start_dt = f"{date_str}T00:00:00"
|
||||||
|
|
||||||
|
# solar_logs에서 오늘 생성된 데이터 조회
|
||||||
|
logs_res = db.table("solar_logs") \
|
||||||
|
.select("plant_id, today_kwh") \
|
||||||
|
.gte("created_at", start_dt) \
|
||||||
|
.order("created_at", desc=True) \
|
||||||
|
.execute()
|
||||||
|
|
||||||
|
# plant_id별로 첫 번째(최신) 값만 취함
|
||||||
|
seen_plants = set()
|
||||||
|
for log in logs_res.data:
|
||||||
|
pid = log['plant_id']
|
||||||
|
if pid not in seen_plants:
|
||||||
|
val = log.get('today_kwh', 0)
|
||||||
|
if val is None: val = 0
|
||||||
|
stats_map[pid] = val
|
||||||
|
seen_plants.add(pid)
|
||||||
|
else:
|
||||||
|
# 과거: daily_stats 조회
|
||||||
|
daily_res = db.table("daily_stats") \
|
||||||
|
.select("plant_id, total_generation") \
|
||||||
|
.eq("date", date_str) \
|
||||||
|
.execute()
|
||||||
|
|
||||||
|
for row in daily_res.data:
|
||||||
|
val = row.get('total_generation', 0)
|
||||||
|
if val is None: val = 0
|
||||||
|
stats_map[row['plant_id']] = val
|
||||||
|
|
||||||
|
elif period == "month":
|
||||||
|
# 월간: monthly_stats 조회 (해당 월)
|
||||||
|
month_str = f"{target_year}-{target_month:02d}"
|
||||||
|
|
||||||
|
monthly_res = db.table("monthly_stats") \
|
||||||
|
.select("plant_id, total_generation") \
|
||||||
|
.eq("month", month_str) \
|
||||||
|
.execute()
|
||||||
|
|
||||||
|
for row in monthly_res.data:
|
||||||
|
val = row.get('total_generation', 0)
|
||||||
|
if val is None: val = 0
|
||||||
|
stats_map[row['plant_id']] = val
|
||||||
|
|
||||||
|
elif period == "year":
|
||||||
|
# 연간: monthly_stats 조회 (해당 연도 전체 합산)
|
||||||
|
start_month = f"{target_year}-01"
|
||||||
|
end_month = f"{target_year}-12"
|
||||||
|
|
||||||
|
monthly_res = db.table("monthly_stats") \
|
||||||
|
.select("plant_id, total_generation") \
|
||||||
|
.gte("month", start_month) \
|
||||||
|
.lte("month", end_month) \
|
||||||
|
.execute()
|
||||||
|
|
||||||
|
# 합산
|
||||||
|
for row in monthly_res.data:
|
||||||
|
pid = row['plant_id']
|
||||||
|
val = row.get('total_generation', 0)
|
||||||
|
if val is None: val = 0
|
||||||
|
stats_map[pid] = stats_map.get(pid, 0) + val
|
||||||
|
|
||||||
|
# 결과 조합
|
||||||
|
for pid, p_info in plants.items():
|
||||||
|
gen = stats_map.get(pid, 0) or 0
|
||||||
|
cap = p_info.get('capacity', 0) or 0
|
||||||
|
|
||||||
|
# 발전시간 계산
|
||||||
|
gen_hours = 0
|
||||||
|
if cap > 0:
|
||||||
|
if period == "day":
|
||||||
|
gen_hours = gen / cap
|
||||||
|
elif period == "month":
|
||||||
|
# 해당 월의 일수 (일평균 발전시간)
|
||||||
|
days_in_month = calendar.monthrange(target_year, target_month)[1]
|
||||||
|
gen_hours = (gen / cap) / days_in_month
|
||||||
|
elif period == "year":
|
||||||
|
# 연간 일평균 발전시간 (/365)
|
||||||
|
gen_hours = (gen / cap) / 365
|
||||||
|
|
||||||
|
result_data.append({
|
||||||
|
"plant_id": pid,
|
||||||
|
"plant_name": p_info['name'],
|
||||||
|
"capacity": cap,
|
||||||
|
"generation": round(gen, 2),
|
||||||
|
"generation_hours": round(gen_hours, 2)
|
||||||
|
})
|
||||||
|
|
||||||
|
# 발전소 이름 순 정렬 (호기 추출)
|
||||||
|
def sort_key(item):
|
||||||
|
match = re.search(r'(\d+)호기', item['plant_name'])
|
||||||
|
if match:
|
||||||
|
return int(match.group(1))
|
||||||
|
return 999
|
||||||
|
|
||||||
|
result_data.sort(key=sort_key)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "success",
|
||||||
|
"period": period,
|
||||||
|
"target_date": target_date.isoformat(),
|
||||||
|
"data": result_data,
|
||||||
|
"count": len(result_data)
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=500,
|
||||||
|
detail=f"전체 통계 조회 실패: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{plant_id}/stats")
|
@router.get("/{plant_id}/stats")
|
||||||
async def get_plant_stats(
|
async def get_plant_stats(
|
||||||
plant_id: str,
|
plant_id: str,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user