Files
ev-charger-as/backend/routers/improvements.py
2026-06-02 19:34:36 +09:00

136 lines
5.8 KiB
Python

import json
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form, Body
from sqlalchemy.orm import Session
from sqlalchemy import desc, text, func
from typing import List, Optional
from datetime import datetime
from database import get_db
import models
from auth import require_admin, require_manufacturer, get_current_user
from utils import save_upload
router = APIRouter(prefix="/api/improvements", tags=["improvements"])
def _fmt(imp: models.Improvement):
return {
"id": imp.id, "title": imp.title, "category": imp.category,
"description": imp.description, "priority": imp.priority,
"part_name": imp.part_name, "status": imp.status,
"manufacturer_id": imp.manufacturer_id,
"manufacturer_name": imp.manufacturer.name if imp.manufacturer else None,
"manufacturer_company": None,
"created_by_name": imp.creator.name if imp.creator else None,
"sw_deploy_target": str(imp.sw_deploy_target) if imp.sw_deploy_target else None,
"sw_deployed_at": str(imp.sw_deployed_at) if imp.sw_deployed_at else None,
"manufacturer_memo": imp.manufacturer_memo,
"created_at": imp.created_at.isoformat(),
"report_ids": [ir.report_id for ir in imp.report_links],
"report_count": len(imp.report_links),
"attachments": [{"path": a.file_path, "name": a.file_name} for a in imp.attachments],
"logs": [{"old_status": l.old_status, "new_status": l.new_status, "memo": l.memo,
"changed_at": l.changed_at.isoformat(),
"by": l.changer.name if l.changer else None} for l in imp.logs],
}
@router.get("")
def list_improvements(
status: Optional[str] = None, manufacturer_id: Optional[int] = None,
db: Session = Depends(get_db),
current_user: models.User = Depends(get_current_user)
):
q = db.query(models.Improvement).order_by(desc(models.Improvement.created_at))
if current_user.role == "manufacturer":
q = q.filter(models.Improvement.manufacturer_id == current_user.id)
if status: q = q.filter(models.Improvement.status == status)
if manufacturer_id: q = q.filter(models.Improvement.manufacturer_id == manufacturer_id)
return [_fmt(imp) for imp in q.all()]
@router.get("/{imp_id}")
def get_improvement(imp_id: int, db: Session = Depends(get_db),
current_user: models.User = Depends(get_current_user)):
imp = db.query(models.Improvement).filter_by(id=imp_id).first()
if not imp: raise HTTPException(404)
if current_user.role == "manufacturer" and imp.manufacturer_id != current_user.id:
raise HTTPException(403)
result = _fmt(imp)
rids = [ir.report_id for ir in imp.report_links]
if rids:
seq_subq = db.query(
models.Report.id.label("rid"),
func.row_number().over(
order_by=[models.Report.reported_at.asc(), models.Report.id.asc()]
).label("seq")
).subquery()
seqs = {row.rid: row.seq for row in
db.query(seq_subq.c.rid, seq_subq.c.seq)
.filter(seq_subq.c.rid.in_(rids)).all()}
result["report_links"] = [{"id": rid, "seq": seqs.get(rid, rid)} for rid in rids]
else:
result["report_links"] = []
return result
@router.post("")
async def create_improvement(
title: str = Form(...), category: str = Form(...),
description: str = Form(...), priority: str = Form("normal"),
part_name: str = Form(""), manufacturer_id: int = Form(...),
report_ids: str = Form("[]"),
sw_deploy_target: Optional[str] = Form(None),
attachments: List[UploadFile] = File(default=[]),
db: Session = Depends(get_db),
current_user: models.User = Depends(require_admin)
):
imp = models.Improvement(
title=title, category=category, description=description,
priority=priority, part_name=part_name or None,
manufacturer_id=manufacturer_id, created_by=current_user.id,
sw_deploy_target=sw_deploy_target or None,
)
db.add(imp); db.commit(); db.refresh(imp)
for rid in json.loads(report_ids):
db.add(models.ImprovementReport(improvement_id=imp.id, report_id=int(rid)))
for f in attachments:
if f.filename:
path = save_upload(f, f"improvements/{imp.id}")
db.add(models.ImprovementAttachment(improvement_id=imp.id, file_path=path, file_name=f.filename))
db.add(models.ImprovementLog(improvement_id=imp.id, changed_by=current_user.id,
old_status=None, new_status="registered", memo="개선항목 등록"))
db.commit()
return {"id": imp.id}
@router.delete("/bulk")
def bulk_delete_improvements(
ids: List[int] = Body(...),
db: Session = Depends(get_db),
_=Depends(require_admin)
):
if not ids:
raise HTTPException(400, "삭제할 항목을 선택하세요.")
result = db.execute(text("DELETE FROM improvements WHERE id = ANY(:ids)"), {"ids": ids})
db.commit()
return {"deleted": result.rowcount}
@router.patch("/{imp_id}/status")
def update_status(
imp_id: int, status: str = Form(...), memo: str = Form(""),
sw_deployed_at: Optional[str] = Form(None),
manufacturer_memo: str = Form(""),
db: Session = Depends(get_db),
current_user: models.User = Depends(get_current_user)
):
imp = db.query(models.Improvement).filter_by(id=imp_id).first()
if not imp: raise HTTPException(404)
if current_user.role == "manufacturer" and imp.manufacturer_id != current_user.id:
raise HTTPException(403)
old_status = imp.status
imp.status = status
if sw_deployed_at: imp.sw_deployed_at = sw_deployed_at
if manufacturer_memo: imp.manufacturer_memo = manufacturer_memo
db.add(models.ImprovementLog(improvement_id=imp.id, changed_by=current_user.id,
old_status=old_status, new_status=status, memo=memo))
db.commit()
return {"ok": True}