Spaces:
Running
Running
File size: 6,433 Bytes
cc7330c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | import uvicorn
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import FileResponse, JSONResponse
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
from typing import List, Optional, Dict, Any
import os
import uuid
from matplotlib import font_manager
from . import core, ppt
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# --- Pydantic 모델 ---
class OptionsRequest(BaseModel):
job: Optional[str] = None
bm: Optional[str] = None
sales: Optional[str] = None
class SearchParamsModel(BaseModel):
job: str
bm: str
sales: str
emp: str
userBase: str
userValue: int
class DiagnosisRequest(BaseModel):
params: SearchParamsModel
answers: List[int]
# Get BASE_DIR for the project root (one level up from this 'core' folder)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# --- 폰트 설치 확인 (디버깅용) ---
font_path = os.path.join(BASE_DIR, "core", "data", "Paperlogy-4Regular.ttf")
if os.path.exists(font_path):
font_name = font_manager.FontProperties(fname=font_path).get_name()
print(f"Loaded font: {font_name}")
else:
print(f"Warning: Font file not found at {font_path}")
# --- 엔드포인트 ---
@app.post("/api/options")
def get_options_endpoint(req: OptionsRequest):
return core.get_step_options(core.conds_df, job=req.job, bm=req.bm, sales=req.sales)
@app.post("/api/survey")
def get_survey_items_endpoint(req: SearchParamsModel):
levels = core.get_levels_by_wage(core.avg_df, req.job, req.bm, req.sales, req.emp, req.userBase, req.userValue)
if not levels: levels = ["L3", "L4"]
bars_indicator, _ = core.make_bars_table(core.bars_df, req.job, levels)
return bars_indicator.to_dict(orient='records')
@app.post("/api/diagnosis")
def post_diagnosis_endpoint(req: DiagnosisRequest):
p = req.params
levels = core.get_levels_by_wage(core.avg_df, p.job, p.bm, p.sales, p.emp, p.userBase, p.userValue)
if not levels: levels = ["L3", "L4"]
_, ppt_tables = core.make_bars_table(core.bars_df, p.job, levels)
levels_def = core.get_levels_definition(p.job, levels, core.job_def)
final_lv, level_out = core.judge_level(req.answers, core.factors, levels, levels_def)
head_msg, text1, text2, pct, c3, table3 = core.judge_wage(core.raw_df, p.job, p.bm, p.sales, p.emp, final_lv, p.userBase, p.userValue)
table1, chart1 = core.format_table(ppt_tables[0], core.factors, req.answers, cut=2)
table2, chart2 = core.format_table(ppt_tables[1], core.factors, req.answers, cut=4)
level_out.columns = ['left', 'right']
level_info = {
"left": level_out['left'].to_dict(),
"right": level_out['right'].to_dict(),
}
for chart in [chart1, chart2]:
chart.columns = ['subject', 'target', 'user']
subjects = [
'기술 전문성', '도메인 이해', '데이터 리터러시', '고객중심 사고',
'문제해결능력', '애자일 실행', '협업', '책임감', '영향범위', '리더십'
]
chart['subject'] = subjects
wage_records = table3.to_dict(orient='records')
for rec in wage_records:
rec['description'] = text1
level_chart = {
"left": chart1.to_dict(orient='records'),
"right": chart2.to_dict(orient='records')
}
info_pool = [p.job, final_lv, p.userBase, p.bm, p.sales, p.emp]
result_data = {
"headMessage": head_msg.split('\n'),
"levelInfo": level_info,
"levelChart": level_chart,
"percentile": pct,
"wageOutput": wage_records,
"info": [x for x in info_pool if x != '전체']
}
return result_data
@app.post("/api/report")
def download_report(req: DiagnosisRequest):
p = req.params
levels = core.get_levels_by_wage(core.avg_df, p.job, p.bm, p.sales, p.emp, p.userBase, p.userValue)
if not levels: levels = ["L3", "L4"]
_, ppt_tables = core.make_bars_table(core.bars_df, p.job, levels)
levels_def = core.get_levels_definition(p.job, levels, core.job_def)
final_lv, lv_out = core.judge_level(req.answers, core.factors, levels, levels_def)
head_msg, blk1, blk2, pct, c3, t3 = core.judge_wage(core.raw_df, p.job, p.bm, p.sales, p.emp, final_lv, p.userBase, p.userValue)
table1, chart1 = core.format_table(ppt_tables[0], core.factors, req.answers, cut=2)
table2, chart2 = core.format_table(ppt_tables[1], core.factors, req.answers, cut=4)
ppt_dataset = {
0 : {"head_message": head_msg, "texts": [blk1, blk2, f"{pct:.1f}%"], "tables": [lv_out.iloc[:2], lv_out.iloc[2:], t3]},
1 : {"title": lv_out.columns[0], "table": [table1]},
2 : {"title": lv_out.columns[1], "table": [table2]}
}
out_path = os.path.join(core.BASE_DIR, "tmp", f"report_{uuid.uuid4().hex[:8]}.pptx")
os.makedirs(os.path.dirname(out_path), exist_ok=True)
ppt.update_ppt_with_data(ppt_dataset, out_path)
return FileResponse(out_path, filename="진단리포트.pptx", media_type="application/vnd.openxmlformats-officedocument.presentationml.presentation")
# --- 정적 파일 서빙 ---
# api.py가 core/ 폴더 안에 있으므로 BASE_DIR는 상위 디렉토리(루트)입니다.
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
OUT_DIR = os.path.join(BASE_DIR, "out")
if os.path.exists(OUT_DIR):
app.mount("/_next", StaticFiles(directory=os.path.join(OUT_DIR, "_next")), name="next_static")
@app.api_route("/{full_path:path}", methods=["GET", "HEAD"])
async def serve_spa(full_path: str):
if full_path.startswith("api"): raise HTTPException(status_code=404)
file_path = os.path.join(OUT_DIR, full_path)
if os.path.isfile(file_path): return FileResponse(file_path)
html_file = os.path.join(OUT_DIR, f"{full_path}.html")
if os.path.isfile(html_file): return FileResponse(html_file)
index_file = os.path.join(file_path, "index.html")
if os.path.isdir(file_path) and os.path.isfile(index_file): return FileResponse(index_file)
return FileResponse(os.path.join(OUT_DIR, "index.html"))
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=7860)
|