feat: Phase 3 구현 — 완전 자동화, 준공도서, Vision L3, 발주처 포털
EVMS 완전 자동화: - 공기 지연 AI 예측 (SPI 기반 준공일 예측) - 기성청구 가능 금액 자동 산출 - 매일 자정 EVMS 스냅샷 자동 생성 (APScheduler) - 매일 07:00 GONGSA 아침 브리핑 자동 생성 준공도서 패키지: - 준공 요약 + 품질시험 목록 + 검측 이력 + 인허가 현황 → ZIP 번들 - 준공 준비 체크리스트 API - 4종 HTML 템플릿 (WeasyPrint PDF 출력) Vision AI Level 3: - 설계 도면 vs 현장 사진 비교 보조 판독 (Claude Vision) - 철근 배근, 거푸집 치수 1차 분석 설계도서 파싱: - PDF 이미지/텍스트에서 공종·수량·규격 자동 추출 - Pandoc HWP 출력 지원 발주처 전용 포털: - 토큰 기반 읽기 전용 API - 공사 현황 대시보드, 공정률 추이 차트 에이전트 협업 고도화: - 협업 시나리오 (concrete_pour, excavation, weekly_report) - GONGSA→PUMJIL→ANJEON 순차 처리 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
"""
|
||||
EVMS (Earned Value Management System) 계산 서비스
|
||||
PV, EV, AC, SPI, CPI 산출
|
||||
EVMS (Earned Value Management System) — Phase 3 완전 자동화
|
||||
PV, EV, AC, SPI, CPI 산출 + 공정 지연 예측 AI + 기성청구 자동 알림
|
||||
"""
|
||||
from datetime import date
|
||||
from datetime import date, timedelta
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
@@ -10,6 +10,62 @@ from app.models.task import Task
|
||||
from app.models.project import Project
|
||||
|
||||
|
||||
async def predict_delay(
|
||||
db: AsyncSession,
|
||||
project_id,
|
||||
spi: float,
|
||||
planned_end: date | None,
|
||||
snapshot_date: date,
|
||||
) -> dict:
|
||||
"""
|
||||
SPI 기반 공기 지연 예측.
|
||||
spi < 1 이면 지연, 남은 기간을 SPI로 나눠 예상 준공일 계산.
|
||||
"""
|
||||
if not planned_end or spi is None or spi <= 0:
|
||||
return {"delay_days": None, "predicted_end": None, "status": "예측 불가"}
|
||||
|
||||
remaining_days = (planned_end - snapshot_date).days
|
||||
if remaining_days <= 0:
|
||||
return {"delay_days": 0, "predicted_end": str(planned_end), "status": "준공 예정일 경과"}
|
||||
|
||||
predicted_remaining = remaining_days / spi
|
||||
predicted_end = snapshot_date + timedelta(days=int(predicted_remaining))
|
||||
delay_days = (predicted_end - planned_end).days
|
||||
|
||||
if delay_days > 0:
|
||||
status = f"{delay_days}일 지연 예상"
|
||||
elif delay_days < -3:
|
||||
status = f"{abs(delay_days)}일 조기 준공 예상"
|
||||
else:
|
||||
status = "정상 진행"
|
||||
|
||||
return {
|
||||
"delay_days": delay_days,
|
||||
"predicted_end": str(predicted_end),
|
||||
"status": status,
|
||||
}
|
||||
|
||||
|
||||
async def compute_progress_claim(
|
||||
total_budget: float,
|
||||
actual_progress: float,
|
||||
already_claimed_pct: float = 0.0,
|
||||
) -> dict:
|
||||
"""
|
||||
기성청구 가능 금액 산출.
|
||||
기성청구 가능 금액 = 총예산 × (실제 공정률 - 기청구 공정률)
|
||||
"""
|
||||
claimable_pct = max(0.0, actual_progress - already_claimed_pct)
|
||||
claimable_amount = total_budget * (claimable_pct / 100)
|
||||
return {
|
||||
"actual_progress": actual_progress,
|
||||
"already_claimed_pct": already_claimed_pct,
|
||||
"claimable_pct": round(claimable_pct, 1),
|
||||
"claimable_amount": round(claimable_amount, 0),
|
||||
"claimable_amount_formatted": f"{claimable_amount:,.0f}원",
|
||||
}
|
||||
|
||||
|
||||
def _clamp(v: float, lo: float, hi: float) -> float:
|
||||
return max(lo, min(hi, v))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user