98 lines
3.5 KiB
Python
98 lines
3.5 KiB
Python
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
|
||
|
|
from sqlalchemy.orm import Session
|
||
|
|
|
||
|
|
from backend.auth import get_current_user
|
||
|
|
from backend.database import get_db
|
||
|
|
from backend.models import ImportHistory, Question
|
||
|
|
from backend.schemas import ImportHistoryOut, QuestionOut
|
||
|
|
from backend.services.excel_service import create_template_bytes, parse_excel_file
|
||
|
|
from backend.services.file_utils import save_upload
|
||
|
|
from backend.services.parser import DMXAPIService, extract_metadata
|
||
|
|
|
||
|
|
router = APIRouter(dependencies=[Depends(get_current_user)])
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("/ai/parse")
|
||
|
|
def parse_by_ai(file: UploadFile = File(...)) -> dict:
|
||
|
|
path = save_upload(file)
|
||
|
|
parser = DMXAPIService()
|
||
|
|
metadata = extract_metadata(file.filename or path.name)
|
||
|
|
questions = parser.parse_file(str(path))
|
||
|
|
preview = []
|
||
|
|
for q in questions:
|
||
|
|
preview.append(
|
||
|
|
{
|
||
|
|
"chapter": metadata["chapter"],
|
||
|
|
"primary_knowledge": "",
|
||
|
|
"secondary_knowledge": metadata["secondary_knowledge"],
|
||
|
|
"question_type": metadata["question_type"],
|
||
|
|
"difficulty": metadata["difficulty"],
|
||
|
|
"stem": q.get("题干", ""),
|
||
|
|
"option_a": q.get("选项A", ""),
|
||
|
|
"option_b": q.get("选项B", ""),
|
||
|
|
"option_c": q.get("选项C", ""),
|
||
|
|
"option_d": q.get("选项D", ""),
|
||
|
|
"answer": q.get("正确答案", ""),
|
||
|
|
"explanation": q.get("解析", ""),
|
||
|
|
"notes": q.get("备注", ""),
|
||
|
|
"source_file": metadata["source_file"],
|
||
|
|
}
|
||
|
|
)
|
||
|
|
return {"filename": file.filename, "preview": preview}
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("/ai/confirm", response_model=list[QuestionOut])
|
||
|
|
def confirm_ai_import(payload: list[dict], db: Session = Depends(get_db)) -> list[QuestionOut]:
|
||
|
|
if not payload:
|
||
|
|
raise HTTPException(status_code=400, detail="没有可导入数据")
|
||
|
|
items = [Question(**item) for item in payload]
|
||
|
|
db.add_all(items)
|
||
|
|
db.add(
|
||
|
|
ImportHistory(
|
||
|
|
filename=items[0].source_file if items else "",
|
||
|
|
method="ai",
|
||
|
|
question_count=len(items),
|
||
|
|
status="success",
|
||
|
|
)
|
||
|
|
)
|
||
|
|
db.commit()
|
||
|
|
for item in items:
|
||
|
|
db.refresh(item)
|
||
|
|
return [QuestionOut.model_validate(item) for item in items]
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("/excel", response_model=list[QuestionOut])
|
||
|
|
def import_excel(file: UploadFile = File(...), db: Session = Depends(get_db)) -> list[QuestionOut]:
|
||
|
|
path = save_upload(file)
|
||
|
|
if Path(path).suffix.lower() not in [".xlsx", ".xlsm", ".xltx", ".xltm"]:
|
||
|
|
raise HTTPException(status_code=400, detail="仅支持 Excel 文件")
|
||
|
|
rows = parse_excel_file(Path(path))
|
||
|
|
items = [Question(**row) for row in rows]
|
||
|
|
db.add_all(items)
|
||
|
|
db.add(
|
||
|
|
ImportHistory(
|
||
|
|
filename=file.filename or "",
|
||
|
|
method="excel",
|
||
|
|
question_count=len(items),
|
||
|
|
status="success",
|
||
|
|
)
|
||
|
|
)
|
||
|
|
db.commit()
|
||
|
|
for item in items:
|
||
|
|
db.refresh(item)
|
||
|
|
return [QuestionOut.model_validate(item) for item in items]
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/template")
|
||
|
|
def download_template() -> dict:
|
||
|
|
content = create_template_bytes()
|
||
|
|
return {"filename": "question_template.xlsx", "content_base64": content.hex()}
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/history", response_model=list[ImportHistoryOut])
|
||
|
|
def import_history(db: Session = Depends(get_db)) -> list[ImportHistoryOut]:
|
||
|
|
rows = db.query(ImportHistory).order_by(ImportHistory.created_at.desc()).limit(100).all()
|
||
|
|
return [ImportHistoryOut.model_validate(r) for r in rows]
|