From a405748042ceaf5d2cb79dbfb9fad0db19e25860 Mon Sep 17 00:00:00 2001 From: haneulai Date: Mon, 15 Jun 2026 16:07:32 +0900 Subject: [PATCH] feat: implement hybrid daily stats calibration using fetch_history_daily --- crawler/daily_summary.py | 65 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/crawler/daily_summary.py b/crawler/daily_summary.py index d7b92ee..1da75f1 100644 --- a/crawler/daily_summary.py +++ b/crawler/daily_summary.py @@ -13,6 +13,29 @@ except ImportError: import pandas as pd from database import get_supabase_client +from config import get_all_plants + +def get_history_crawler(plant_type: str): + """발전소 타입별 과거 데이터 크롤러 함수 반환""" + try: + if plant_type == 'nrems': + from crawlers.nrems import fetch_history_daily + return fetch_history_daily + elif plant_type == 'kremc': + from crawlers.kremc import fetch_history_daily + return fetch_history_daily + elif plant_type == 'hyundai': + from crawlers.hyundai import fetch_history_daily + return fetch_history_daily + elif plant_type == 'sun_wms': + from crawlers.sun_wms import fetch_history_daily + return fetch_history_daily + elif plant_type == 'cmsolar': + from crawlers.cmsolar import fetch_history_daily + return fetch_history_daily + except Exception as e: + print(f" ⚠️ 크롤러 모듈 임포트 실패 ({plant_type}): {e}") + return None def get_plant_capacities(client) -> dict: @@ -47,6 +70,17 @@ def calculate_daily_stats(date_str: str = None): # 1. 용량 정보 조회 capacities = get_plant_capacities(client) + # 발전소 정보 빌드 (하이브리드 크롤링용) + all_plants = get_all_plants() + plant_info_map = {} + for p in all_plants: + is_split = p.get('options', {}).get('is_split', False) + if is_split: + plant_info_map['nrems-01'] = p + plant_info_map['nrems-02'] = p + else: + plant_info_map[p.get('id', '')] = p + # 2. 해당일 로그 조회 (KST 날짜 범위를 UTC로 변환하여 쿼리) kst = timezone(timedelta(hours=9)) start_kst = datetime.strptime(f"{date_str} 00:00:00", "%Y-%m-%d %H:%M:%S").replace(tzinfo=kst) @@ -82,6 +116,37 @@ def calculate_daily_stats(date_str: str = None): # 최대 출력 peak_kw = group['current_kw'].max() if len(group) > 0 else 0 + # 하이브리드 보정 로직 적용 (원 모니터링 사이트 통계 크롤링) + plant_info = plant_info_map.get(plant_id) + if plant_info: + plant_type = plant_info.get('type') + is_split = plant_info.get('options', {}).get('is_split', False) + history_crawler = get_history_crawler(plant_type) + if history_crawler: + try: + print(f" 📡 [{plant_id}] 원본 사이트 통계 크롤링 시도 ({date_str}) ...") + # fetch_history_daily는 리스트 반환: [{'plant_id': '...', 'date': '...', 'generation_kwh': ...}] + history_data = history_crawler(plant_info, date_str, date_str) + if history_data: + # nrems-01, nrems-02 분할 및 단일 매칭 처리 + matched = [item for item in history_data if item.get('plant_id') == plant_id] + if not matched and len(history_data) == 1 and not is_split: + matched = history_data + + if matched: + original_val = matched[0].get('generation_kwh', 0) + if original_val > 0: + print(f" ➔ 🟢 보정 성공: 기존 집계 {total_generation:.1f} kWh ➔ 원본 {original_val:.1f} kWh") + total_generation = original_val + else: + print(f" ➔ ⚠️ 원본 값이 0이므로 보정을 건너뜁니다.") + else: + print(f" ➔ ⚠️ 원본 데이터 중 {plant_id}와 일치하는 항목을 찾지 못했습니다.") + else: + print(f" ➔ ⚠️ 원본 데이터가 비어 있습니다.") + except Exception as ex: + print(f" ➔ ❌ 원본 사이트 크롤링 실패 (Fallback 기존 집계 적용): {ex}") + # 이용률 시간 = 발전량 / 용량 capacity = capacities.get(plant_id, 99.0) generation_hours = round(total_generation / capacity, 2) if capacity > 0 else 0