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:
+59
-1
@@ -9,7 +9,7 @@ from sqlalchemy.orm import selectinload
|
||||
from app.deps import CurrentUser, DB
|
||||
from app.models.evms import EVMSSnapshot
|
||||
from app.models.project import Project
|
||||
from app.services.evms_service import compute_evms
|
||||
from app.services.evms_service import compute_evms, predict_delay, compute_progress_claim
|
||||
|
||||
router = APIRouter(prefix="/projects/{project_id}/evms", tags=["EVMS"])
|
||||
|
||||
@@ -117,6 +117,64 @@ async def list_snapshots(
|
||||
return r.scalars().all()
|
||||
|
||||
|
||||
@router.get("/delay-forecast")
|
||||
async def delay_forecast(
|
||||
project_id: uuid.UUID, db: DB, current_user: CurrentUser
|
||||
):
|
||||
"""공기 지연 AI 예측 (최근 EVMS 스냅샷 기반)"""
|
||||
r = await db.execute(
|
||||
select(EVMSSnapshot)
|
||||
.where(EVMSSnapshot.project_id == project_id)
|
||||
.order_by(EVMSSnapshot.snapshot_date.desc())
|
||||
.limit(1)
|
||||
)
|
||||
snap = r.scalar_one_or_none()
|
||||
if not snap:
|
||||
raise HTTPException(status_code=404, detail="EVMS 스냅샷이 없습니다")
|
||||
|
||||
project = await _get_project_or_404(project_id, db)
|
||||
planned_end = project.end_date
|
||||
if planned_end and not isinstance(planned_end, date):
|
||||
from datetime import date as ddate
|
||||
planned_end = ddate.fromisoformat(str(planned_end))
|
||||
|
||||
forecast = await predict_delay(
|
||||
db, project_id,
|
||||
spi=snap.spi,
|
||||
planned_end=planned_end,
|
||||
snapshot_date=snap.snapshot_date,
|
||||
)
|
||||
forecast["spi"] = snap.spi
|
||||
forecast["cpi"] = snap.cpi
|
||||
forecast["snapshot_date"] = str(snap.snapshot_date)
|
||||
return forecast
|
||||
|
||||
|
||||
@router.get("/progress-claim")
|
||||
async def progress_claim(
|
||||
project_id: uuid.UUID,
|
||||
already_claimed_pct: float = 0.0,
|
||||
db: DB = None,
|
||||
current_user: CurrentUser = None,
|
||||
):
|
||||
"""기성청구 가능 금액 산출"""
|
||||
r = await db.execute(
|
||||
select(EVMSSnapshot)
|
||||
.where(EVMSSnapshot.project_id == project_id)
|
||||
.order_by(EVMSSnapshot.snapshot_date.desc())
|
||||
.limit(1)
|
||||
)
|
||||
snap = r.scalar_one_or_none()
|
||||
if not snap:
|
||||
raise HTTPException(status_code=404, detail="EVMS 스냅샷이 없습니다. /compute 먼저 실행하세요.")
|
||||
|
||||
return await compute_progress_claim(
|
||||
total_budget=snap.total_budget or 0,
|
||||
actual_progress=snap.actual_progress or 0,
|
||||
already_claimed_pct=already_claimed_pct,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/latest", response_model=EVMSResponse)
|
||||
async def latest_snapshot(
|
||||
project_id: uuid.UUID, db: DB, current_user: CurrentUser
|
||||
|
||||
Reference in New Issue
Block a user