- 품질시험 API: schemas/quality.py + api/quality.py (CRUD, 합격률 요약, 자동 합불 판정) - PDF 생성: WeasyPrint + Jinja2 (작업일보/검측요청서/보고서 템플릿 + /pdf 다운로드 엔드포인트) - RAG 시드 스크립트: scripts/seed_rag.py (PDF/TXT 청킹, 배치 임베딩, CLI) - APScheduler: 날씨 3시간 주기 자동 수집 + 경보 평가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
138 lines
4.5 KiB
Python
138 lines
4.5 KiB
Python
import uuid
|
|
from fastapi import APIRouter, HTTPException, status
|
|
from sqlalchemy import select, func
|
|
from app.deps import CurrentUser, DB
|
|
from app.models.quality import QualityTest, QualityResult
|
|
from app.models.project import Project
|
|
from app.schemas.quality import QualityTestCreate, QualityTestUpdate, QualityTestResponse
|
|
|
|
router = APIRouter(prefix="/projects/{project_id}/quality", tags=["품질시험"])
|
|
|
|
|
|
async def _get_project_or_404(project_id: uuid.UUID, db: DB) -> Project:
|
|
result = await db.execute(select(Project).where(Project.id == project_id))
|
|
p = result.scalar_one_or_none()
|
|
if not p:
|
|
raise HTTPException(status_code=404, detail="프로젝트를 찾을 수 없습니다")
|
|
return p
|
|
|
|
|
|
@router.get("", response_model=list[QualityTestResponse])
|
|
async def list_quality_tests(
|
|
project_id: uuid.UUID,
|
|
db: DB,
|
|
current_user: CurrentUser,
|
|
test_type: str | None = None,
|
|
result: QualityResult | None = None,
|
|
):
|
|
query = select(QualityTest).where(QualityTest.project_id == project_id)
|
|
if test_type:
|
|
query = query.where(QualityTest.test_type == test_type)
|
|
if result:
|
|
query = query.where(QualityTest.result == result)
|
|
query = query.order_by(QualityTest.test_date.desc())
|
|
rows = await db.execute(query)
|
|
return rows.scalars().all()
|
|
|
|
|
|
@router.post("", response_model=QualityTestResponse, status_code=status.HTTP_201_CREATED)
|
|
async def create_quality_test(
|
|
project_id: uuid.UUID,
|
|
data: QualityTestCreate,
|
|
db: DB,
|
|
current_user: CurrentUser,
|
|
):
|
|
await _get_project_or_404(project_id, db)
|
|
test = QualityTest(**data.model_dump(), project_id=project_id)
|
|
db.add(test)
|
|
await db.commit()
|
|
await db.refresh(test)
|
|
return test
|
|
|
|
|
|
@router.get("/summary")
|
|
async def quality_summary(project_id: uuid.UUID, db: DB, current_user: CurrentUser):
|
|
"""프로젝트 품질시험 합격률 요약"""
|
|
total_q = await db.execute(
|
|
select(func.count()).where(QualityTest.project_id == project_id)
|
|
)
|
|
total = total_q.scalar() or 0
|
|
|
|
pass_q = await db.execute(
|
|
select(func.count()).where(
|
|
QualityTest.project_id == project_id,
|
|
QualityTest.result == QualityResult.PASS,
|
|
)
|
|
)
|
|
passed = pass_q.scalar() or 0
|
|
|
|
return {
|
|
"total": total,
|
|
"passed": passed,
|
|
"failed": total - passed,
|
|
"pass_rate": round(passed / total * 100, 1) if total > 0 else None,
|
|
}
|
|
|
|
|
|
@router.get("/{test_id}", response_model=QualityTestResponse)
|
|
async def get_quality_test(
|
|
project_id: uuid.UUID, test_id: uuid.UUID, db: DB, current_user: CurrentUser
|
|
):
|
|
result = await db.execute(
|
|
select(QualityTest).where(
|
|
QualityTest.id == test_id, QualityTest.project_id == project_id
|
|
)
|
|
)
|
|
test = result.scalar_one_or_none()
|
|
if not test:
|
|
raise HTTPException(status_code=404, detail="품질시험 기록을 찾을 수 없습니다")
|
|
return test
|
|
|
|
|
|
@router.put("/{test_id}", response_model=QualityTestResponse)
|
|
async def update_quality_test(
|
|
project_id: uuid.UUID,
|
|
test_id: uuid.UUID,
|
|
data: QualityTestUpdate,
|
|
db: DB,
|
|
current_user: CurrentUser,
|
|
):
|
|
result = await db.execute(
|
|
select(QualityTest).where(
|
|
QualityTest.id == test_id, QualityTest.project_id == project_id
|
|
)
|
|
)
|
|
test = result.scalar_one_or_none()
|
|
if not test:
|
|
raise HTTPException(status_code=404, detail="품질시험 기록을 찾을 수 없습니다")
|
|
|
|
update_data = data.model_dump(exclude_none=True)
|
|
|
|
# 측정값/기준값 변경 시 합격 여부 재계산
|
|
new_measured = update_data.get("measured_value", test.measured_value)
|
|
new_design = update_data.get("design_value", test.design_value)
|
|
if "result" not in update_data and new_design is not None:
|
|
update_data["result"] = QualityResult.PASS if new_measured >= new_design else QualityResult.FAIL
|
|
|
|
for field, value in update_data.items():
|
|
setattr(test, field, value)
|
|
await db.commit()
|
|
await db.refresh(test)
|
|
return test
|
|
|
|
|
|
@router.delete("/{test_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
async def delete_quality_test(
|
|
project_id: uuid.UUID, test_id: uuid.UUID, db: DB, current_user: CurrentUser
|
|
):
|
|
result = await db.execute(
|
|
select(QualityTest).where(
|
|
QualityTest.id == test_id, QualityTest.project_id == project_id
|
|
)
|
|
)
|
|
test = result.scalar_one_or_none()
|
|
if not test:
|
|
raise HTTPException(status_code=404, detail="품질시험 기록을 찾을 수 없습니다")
|
|
await db.delete(test)
|
|
await db.commit()
|