feat: 세이버메트릭스 기반 프로젝트 자산 가치 분석 시스템 고도화 (AVI/VCI 도입)

- analysis_service.py: AVI 및 VCI(자산 기여도) 산출 로직 구현
- prediction_service.py: 정체 프로젝트 AI 예보 최적화
- js/analysis.js: 전략 매트릭스 및 VCI 등급 시스템 시각화
- templates/analysis.html: UI 용어 최신화 및 스타일 통합
- ANALYSIS_REPORT.md: 분석 지표 공식 및 가이드라인 상세 기술
This commit is contained in:
2026-03-24 17:54:01 +09:00
parent be3210463f
commit dff3305da1
11 changed files with 481 additions and 322 deletions

View File

@@ -53,19 +53,45 @@ class SOIPredictionService:
@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)
# 데이터가 너무 적으면 무조건 보수적 감쇄 (14일 기준 약 -2.1점)
if not history or len(history) < 3:
return round(max(0, min(100, current_soi - (0.15 * 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)
# [정밀 정체 분석]
# 1. 파일 수 변화 확인 (최근 5개 샘플)
recent_counts = [int(h['file_count'] or 0) for h in history[-5:]]
is_hard_stagnant = len(set(recent_counts)) <= 1 # 파일 수 변동이 전혀 없음
# 예측 로직: 현재값 + 모멘텀 - 자연 감쇄
decay_constant = 0.05
# 2. 최근 로그 상태 확인
last_log = history[-1]['recent_log']
is_no_activity = last_log is None or last_log == "데이터 없음" or "폴더자동삭제" in last_log
# [모멘텀 산출 로직 개편]
if is_hard_stagnant:
# 파일 변화가 없다면 아무리 로그가 있어도 '유지 관리'일 뿐 '성장'이 아님
# 오히려 시간이 갈수록 기술 부채와 데이터 노후화가 진행된다고 판단 (강력 패널티)
momentum_factor = -2.5 if is_no_activity else -1.0
else:
# 실질적인 파일 수 변화(Velocity)가 있을 때만 긍정적 모멘텀 검토
v_gain = features['velocity'] * 0.5
d_gain = features['density'] * 0.8
momentum_factor = v_gain + d_gain - 0.5 # 기본적으로 하향 압력 부여
# 예측 로직: 현재값 + 모멘텀 - (시간에 따른 자연 부식)
# 정체 시 momentum_factor가 -1.0~-2.5이므로 감쇄가 매우 빠름
decay_constant = 0.08
predicted = current_val + momentum_factor - (decay_constant * days_ahead)
# [최종 방어 로직]
# 실질적 파일 증가(velocity > 0)가 포착되지 않았다면 예보는 현재값보다 클 수 없음
if features['velocity'] <= 0 and predicted > current_val:
predicted = current_val - 1.5 # 강제 하락
# 사망 선고 (AVI가 이미 낮고 정체 중이면 0에 수렴하도록 가속)
if current_val < 20 and is_hard_stagnant:
predicted = max(0, predicted - 5.0)
return round(max(0, min(100, predicted)), 1)