import numpy as np from datetime import datetime class SOIPredictionService: """학습형 시계열 예측 및 피처 추출 엔진""" @staticmethod def get_historical_soi(cursor, project_id): """DB에서 프로젝트의 과거 SOI 히스토리를 시퀀스로 추출""" cursor.execute(""" SELECT crawl_date, file_count, recent_log FROM projects_history WHERE project_id = %s ORDER BY crawl_date ASC """, (project_id,)) return cursor.fetchall() @staticmethod def extract_vitality_features(history): """딥러닝 학습을 위한 4대 핵심 피처 추출 (Feature Engineering)""" if len(history) < 2: return {"velocity": 0, "acceleration": 0, "consistency": 0.5, "density": 0.1} # 실제 데이터 구조에 맞게 보정 counts = [] for h in history: try: val = int(h['file_count']) if h['file_count'] is not None else 0 counts.append(val) except: counts.append(0) # 1. 활동 속도 (Velocity) velocity = np.diff(counts).mean() if len(counts) > 1 else 0 # 2. 활동 가속도 (Acceleration): 최근 활동이 빨라지는지 느려지는지 acceleration = np.diff(np.diff(counts)).mean() if len(counts) > 2 else 0 # 3. 로그 밀도 (Density): 전체 기간 대비 실제 로그 발생 비율 logs = [h['recent_log'] for h in history if h['recent_log'] and h['recent_log'] != "데이터 없음"] density = len(logs) / len(history) if len(history) > 0 else 0 # 4. 관리 일관성 (Consistency): 업데이트 간격의 표준편차 (낮을수록 좋음) # (현재 데이터는 일일 크롤링이므로 로그 텍스트 변화 시점을 기준으로 간격 계산 가능) return { "velocity": float(velocity), "acceleration": float(acceleration), "density": float(density), "sample_count": len(history) } @staticmethod def predict_future_soi(current_soi, history, days_ahead=14): """기존 점수와 시계열 피처를 결합하여 미래 점수 예측""" if not history or len(history) < 2: return round(max(0, min(100, current_soi - (0.05 * days_ahead))), 1) features = SOIPredictionService.extract_vitality_features(history) # 기준점을 현재의 실제 SOI 점수로 설정 (핵심 수정) current_val = float(current_soi) # 활동 모멘텀 계산: 파일 증가 속도와 로그 밀도 반영 momentum_factor = (features['velocity'] * 0.2) + (features['density'] * 2.0) # 예측 로직: 현재값 + 모멘텀 - 자연 감쇄 decay_constant = 0.05 predicted = current_val + momentum_factor - (decay_constant * days_ahead) return round(max(0, min(100, predicted)), 1)