초기 커밋 - EV AS 관리 시스템
This commit is contained in:
224
backend/routers/export.py.bak
Normal file
224
backend/routers/export.py.bak
Normal file
@@ -0,0 +1,224 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import desc
|
||||
from io import BytesIO
|
||||
from datetime import datetime
|
||||
import openpyxl
|
||||
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
||||
from database import get_db
|
||||
import models
|
||||
from auth import require_admin
|
||||
|
||||
router = APIRouter(prefix="/api/export", tags=["export"])
|
||||
|
||||
NAVY = "0B1E3D"
|
||||
ACCENT = "00B4D8"
|
||||
LIGHT = "D6EAF8"
|
||||
|
||||
def style_header(ws, headers, row=1):
|
||||
for col, h in enumerate(headers, 1):
|
||||
cell = ws.cell(row=row, column=col, value=h)
|
||||
cell.font = Font(bold=True, color="FFFFFF", size=11)
|
||||
cell.fill = PatternFill("solid", fgColor=NAVY)
|
||||
cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
|
||||
bd = Side(style="thin", color="AAAAAA")
|
||||
cell.border = Border(left=bd, right=bd, top=bd, bottom=bd)
|
||||
ws.row_dimensions[row].height = 20
|
||||
|
||||
def style_row(ws, row_num, num_cols, even=True):
|
||||
bd = Side(style="thin", color="DDDDDD")
|
||||
for col in range(1, num_cols + 1):
|
||||
cell = ws.cell(row=row_num, column=col)
|
||||
if even:
|
||||
cell.fill = PatternFill("solid", fgColor="F4F7FB")
|
||||
cell.border = Border(left=bd, right=bd, top=bd, bottom=bd)
|
||||
cell.alignment = Alignment(vertical="center", wrap_text=True)
|
||||
|
||||
def fmt_dt(dt):
|
||||
return dt.strftime("%Y-%m-%d %H:%M") if dt else ""
|
||||
|
||||
def fmt_d(d):
|
||||
return str(d) if d else ""
|
||||
|
||||
def elapsed(start, end):
|
||||
if not start or not end: return ""
|
||||
diff = end - start
|
||||
total = int(diff.total_seconds())
|
||||
h, m = divmod(total // 60, 60)
|
||||
return f"{h}시간 {m}분"
|
||||
|
||||
@router.get("/reports")
|
||||
def export_reports(db: Session = Depends(get_db), _=Depends(require_admin)):
|
||||
wb = openpyxl.Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "AS신고목록"
|
||||
ws.freeze_panes = "A2"
|
||||
|
||||
headers = [
|
||||
"접수번호","충전기ID","충전기종류","충전기명","충전소명","CPO명","설치일",
|
||||
"신고위치(위도)","신고위치(경도)","문제유형","에러코드","상세설명",
|
||||
"신고자연락처","문제발생시각","신고일시","처리상태",
|
||||
"담당정비사","정비사소속","조치유형","조치내용",
|
||||
"조치시작","조치완료","작업시간","신고→완료소요시간",
|
||||
"문제원인(관리자)","비고","출장비부담주체","출장비금액","출장비상태",
|
||||
"처리담당자","처리일시","연결개선항목번호"
|
||||
]
|
||||
style_header(ws, headers)
|
||||
|
||||
col_widths = [10,14,14,14,18,14,12,12,12,22,12,24,14,16,16,12,
|
||||
12,14,16,24,16,16,12,18,24,24,16,12,12,12,16,18]
|
||||
for i, w in enumerate(col_widths, 1):
|
||||
ws.column_dimensions[ws.cell(1, i).column_letter].width = w
|
||||
|
||||
reports = db.query(models.Report).order_by(desc(models.Report.reported_at)).all()
|
||||
for row_num, r in enumerate(reports, 2):
|
||||
c = r.charger
|
||||
repair, cost = None, None
|
||||
if r.repair_links:
|
||||
repair = r.repair_links[0].repair
|
||||
cost = repair.cost if repair else None
|
||||
|
||||
imp_ids = [ir.improvement_id for ir in
|
||||
db.query(models.ImprovementReport).filter_by(report_id=r.id).all()]
|
||||
|
||||
row_data = [
|
||||
r.id,
|
||||
r.charger_id,
|
||||
c.charger_type.name if c and c.charger_type else "",
|
||||
c.name if c else "",
|
||||
c.station_name if c else "",
|
||||
c.cpo_name if c else "",
|
||||
fmt_d(c.installed_at) if c else "",
|
||||
r.gps_lat or "",
|
||||
r.gps_lng or "",
|
||||
", ".join(r.issue_types) if r.issue_types else "",
|
||||
r.error_code or "",
|
||||
r.issue_detail or "",
|
||||
r.contact or "",
|
||||
fmt_dt(r.occurred_at),
|
||||
fmt_dt(r.reported_at),
|
||||
r.status,
|
||||
repair.mechanic.name if repair and repair.mechanic else "",
|
||||
repair.mechanic.company if repair and repair.mechanic else "",
|
||||
", ".join(repair.repair_types) if repair and repair.repair_types else "",
|
||||
repair.description if repair else "",
|
||||
fmt_dt(repair.started_at) if repair else "",
|
||||
fmt_dt(repair.completed_at) if repair else "",
|
||||
elapsed(repair.started_at, repair.completed_at) if repair else "",
|
||||
elapsed(r.occurred_at or r.reported_at, repair.completed_at if repair else None),
|
||||
cost.root_cause if cost else "",
|
||||
cost.admin_note if cost else "",
|
||||
cost.cost_party_type if cost else "",
|
||||
cost.cost_amount if cost else "",
|
||||
cost.cost_status if cost else "",
|
||||
cost.reviewer.name if cost and cost.reviewer else "",
|
||||
fmt_dt(cost.reviewed_at) if cost else "",
|
||||
", ".join(str(i) for i in imp_ids) if imp_ids else "",
|
||||
]
|
||||
for col, val in enumerate(row_data, 1):
|
||||
ws.cell(row=row_num, column=col, value=val)
|
||||
style_row(ws, row_num, len(headers), row_num % 2 == 0)
|
||||
ws.row_dimensions[row_num].height = 16
|
||||
|
||||
buf = BytesIO()
|
||||
wb.save(buf)
|
||||
buf.seek(0)
|
||||
fname = f"AS신고목록_{datetime.now().strftime('%Y%m%d_%H%M')}.xlsx"
|
||||
return StreamingResponse(buf, media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
headers={"Content-Disposition": f"attachment; filename*=UTF-8''{fname}"})
|
||||
|
||||
@router.get("/costs")
|
||||
def export_costs(db: Session = Depends(get_db), _=Depends(require_admin)):
|
||||
wb = openpyxl.Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "출장비목록"
|
||||
ws.freeze_panes = "A2"
|
||||
|
||||
headers = ["신고번호","충전기ID","충전기종류","충전소명","조치완료일",
|
||||
"정비사","소속","문제원인","비고",
|
||||
"출장비부담주체","제조사명","금액(원)","처리상태",
|
||||
"처리담당자","처리일시"]
|
||||
style_header(ws, headers)
|
||||
for i, w in enumerate([10,14,14,18,16,12,14,24,24,16,16,12,12,12,16], 1):
|
||||
ws.column_dimensions[ws.cell(1, i).column_letter].width = w
|
||||
|
||||
costs = db.query(models.RepairCost).join(models.Repair).order_by(
|
||||
desc(models.RepairCost.reviewed_at)).all()
|
||||
for row_num, cost in enumerate(costs, 2):
|
||||
repair = cost.repair
|
||||
rids = [rr.report_id for rr in repair.report_links]
|
||||
charger_id, station_name, charger_type = "", "", ""
|
||||
if rids:
|
||||
r = db.query(models.Report).filter_by(id=rids[0]).first()
|
||||
if r and r.charger:
|
||||
charger_id = r.charger_id
|
||||
station_name = r.charger.station_name
|
||||
charger_type = r.charger.charger_type.name if r.charger.charger_type else ""
|
||||
row_data = [
|
||||
", ".join(str(i) for i in rids),
|
||||
charger_id, charger_type, station_name,
|
||||
fmt_dt(repair.completed_at),
|
||||
repair.mechanic.name if repair.mechanic else "",
|
||||
repair.mechanic.company if repair.mechanic else "",
|
||||
cost.root_cause or "", cost.admin_note or "",
|
||||
cost.cost_party_type or "",
|
||||
cost.manufacturer.company if cost.manufacturer else (cost.cost_party_custom or ""),
|
||||
cost.cost_amount or 0, cost.cost_status or "",
|
||||
cost.reviewer.name if cost.reviewer else "",
|
||||
fmt_dt(cost.reviewed_at),
|
||||
]
|
||||
for col, val in enumerate(row_data, 1):
|
||||
ws.cell(row=row_num, column=col, value=val)
|
||||
style_row(ws, row_num, len(headers), row_num % 2 == 0)
|
||||
ws.row_dimensions[row_num].height = 16
|
||||
|
||||
buf = BytesIO()
|
||||
wb.save(buf)
|
||||
buf.seek(0)
|
||||
fname = f"출장비목록_{datetime.now().strftime('%Y%m%d_%H%M')}.xlsx"
|
||||
return StreamingResponse(buf, media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
headers={"Content-Disposition": f"attachment; filename*=UTF-8''{fname}"})
|
||||
|
||||
@router.get("/improvements")
|
||||
def export_improvements(db: Session = Depends(get_db), _=Depends(require_admin)):
|
||||
wb = openpyxl.Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "개선항목목록"
|
||||
ws.freeze_panes = "A2"
|
||||
|
||||
headers = ["번호","제목","분류","우선순위","개선내용","관련부품",
|
||||
"담당제조사","담당자","연락처","연결AS건수","연결AS번호",
|
||||
"진행상태","SW배포목표일","SW실제배포일","제조사메모",
|
||||
"등록관리자","등록일시"]
|
||||
style_header(ws, headers)
|
||||
for i, w in enumerate([8,24,10,10,30,14,16,12,14,10,18,12,14,14,24,12,16], 1):
|
||||
ws.column_dimensions[ws.cell(1, i).column_letter].width = w
|
||||
|
||||
imps = db.query(models.Improvement).order_by(desc(models.Improvement.created_at)).all()
|
||||
for row_num, imp in enumerate(imps, 2):
|
||||
rids = [ir.report_id for ir in imp.report_links]
|
||||
row_data = [
|
||||
imp.id, imp.title, imp.category, imp.priority, imp.description,
|
||||
imp.part_name or "",
|
||||
imp.manufacturer.company if imp.manufacturer else "",
|
||||
imp.manufacturer.name if imp.manufacturer else "",
|
||||
imp.manufacturer.phone if imp.manufacturer else "",
|
||||
len(rids), ", ".join(str(i) for i in rids),
|
||||
imp.status,
|
||||
fmt_d(imp.sw_deploy_target), fmt_d(imp.sw_deployed_at),
|
||||
imp.manufacturer_memo or "",
|
||||
imp.creator.name if imp.creator else "",
|
||||
fmt_dt(imp.created_at),
|
||||
]
|
||||
for col, val in enumerate(row_data, 1):
|
||||
ws.cell(row=row_num, column=col, value=val)
|
||||
style_row(ws, row_num, len(headers), row_num % 2 == 0)
|
||||
ws.row_dimensions[row_num].height = 16
|
||||
|
||||
buf = BytesIO()
|
||||
wb.save(buf)
|
||||
buf.seek(0)
|
||||
fname = f"개선항목목록_{datetime.now().strftime('%Y%m%d_%H%M')}.xlsx"
|
||||
return StreamingResponse(buf, media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
headers={"Content-Disposition": f"attachment; filename*=UTF-8''{fname}"})
|
||||
Reference in New Issue
Block a user