feat: implement P-WAR system analysis and inquiries sorting functionality

This commit is contained in:
2026-03-19 17:59:50 +09:00
parent faa7c8e221
commit 0953f49db1
10 changed files with 797 additions and 25 deletions

View File

@@ -76,6 +76,10 @@ async def get_mail_test(request: Request):
async def get_inquiries_page(request: Request):
return templates.TemplateResponse("inquiries.html", {"request": request})
@app.get("/analysis")
async def get_analysis_page(request: Request):
return templates.TemplateResponse("analysis.html", {"request": request})
class InquiryReplyRequest(BaseModel):
reply: str
status: str
@@ -251,6 +255,95 @@ async def stop_sync():
crawl_stop_event.set()
return {"success": True}
@app.get("/api/analysis/p-war")
async def get_p_war_analysis():
"""P-WAR(Project Performance Above Replacement) 분석 API - 실제 평균 기반"""
try:
with get_db_connection() as conn:
with conn.cursor() as cursor:
cursor.execute(DashboardQueries.GET_LAST_CRAWL_DATE)
last_date = cursor.fetchone()['last_date']
cursor.execute(DashboardQueries.GET_PROJECT_LIST, (last_date,))
projects = cursor.fetchall()
cursor.execute("SELECT project_nm, COUNT(*) as cnt FROM inquiries WHERE status != '완료' GROUP BY project_nm")
inquiry_risks = {row['project_nm']: row['cnt'] for row in cursor.fetchall()}
import math
temp_data = []
total_files = 0
total_stagnant = 0
total_risk = 0
count = len(projects)
if count == 0: return []
# 1. 1차 순회: 전체 합계 계산 (평균 산출용)
for p in projects:
file_count = int(p['file_count']) if p['file_count'] else 0
log = p['recent_log']
days_stagnant = 10
if log and log != "데이터 없음":
match = re.search(r'(\d{4})\.(\d{2})\.(\d{2})', log)
if match:
log_date = datetime.strptime(match.group(0), "%Y.%m.%d").date()
days_stagnant = (last_date - log_date).days
risk_count = inquiry_risks.get(p['project_nm'], 0)
total_files += file_count
total_stagnant += days_stagnant
total_risk += risk_count
temp_data.append((p, file_count, days_stagnant, risk_count))
# 2. 시스템 실제 평균(Mean) 산출
avg_files = total_files / count
avg_stagnant = 5 # 사용자 요청에 따라 방치 기준을 5일로 강제 고정 (엄격한 판정)
avg_risk = total_risk / count
# 3. 평균 수준의 프로젝트 가치(V_avg) 정의
v_rep = ( (1 / (1 + avg_stagnant)) * math.log10(avg_files + 1) ) - (avg_risk * 0.5)
results = []
# 4. 2차 순회: P-WAR 산출 (개별 가치 - 평균 가치)
for p, f_cnt, d_stg, r_cnt in temp_data:
name = p['short_nm'] or p['project_nm']
log = p['recent_log'] or ""
is_auto_delete = "폴더자동삭제" in log.replace(" ", "")
activity_factor = 1 / (1 + d_stg)
scale_factor = math.log10(f_cnt + 1)
v_project = (activity_factor * scale_factor) - (r_cnt * 0.5)
# [추가] 폴더 자동 삭제 페널티 부여 (실질적 관리 부재)
if is_auto_delete:
v_project -= 1.5
p_war = v_project - v_rep
results.append({
"project_nm": name,
"file_count": f_cnt,
"days_stagnant": d_stg,
"risk_count": r_cnt,
"p_war": round(p_war, 3),
"is_auto_delete": is_auto_delete,
"master": p['master'],
"dept": p['department'],
"avg_info": {
"avg_files": round(avg_files, 1),
"avg_stagnant": round(avg_stagnant, 1),
"avg_risk": round(avg_risk, 2)
}
})
results.sort(key=lambda x: x['p_war'])
return results
except Exception as e:
return {"error": str(e)}
@app.get("/attachments")
async def get_attachments():
path = "sample"