Files
dashboard/backend/routes/models.py
T
yangy abd44fe467 Refactor Docker setup and remove unused files
- Updated docker-compose.yml to rename backend and frontend container names for clarity.
- Added volume mappings for frontend and backend services to persist data.
- Removed obsolete files related to frequency response extraction, including Python scripts and output data files.
- Adjusted backend port configuration in main.py to align with new service architecture.
- Enhanced logging for file upload paths in models.py for better traceability.
2026-03-20 09:31:24 +08:00

285 lines
11 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query, Body, UploadFile, File, Form
from sqlalchemy.orm import Session
from typing import List, Optional
import logging
from database import get_db
from models.model import Model
from schemas import ModelCreate, ModelUpdate, ModelResponse
from response import ApiResponse, PageData
import requests
import os
import shutil
from pathlib import Path
# Configure logging
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/models", tags=["models"])
# Meilisearch 配置(从环境变量读取)
MEILISEARCH_URL = os.getenv("MEILISEARCH_URL", "http://localhost:7700")
MEILISEARCH_API_KEY = os.getenv("MEILISEARCH_API_KEY", "")
MEILISEARCH_INDEX = os.getenv("MEILISEARCH_INDEX", "models")
# 文件上传配置
UPLOAD_FOLDER = Path("/data/project/autoeq/measurements")
ALLOWED_EXTENSIONS = {'.csv', '.txt', '.json'}
# 记录上传路径配置
logger.info(f"UPLOAD_FOLDER configured as: {UPLOAD_FOLDER}")
logger.info(f"UPLOAD_FOLDER absolute path: {UPLOAD_FOLDER.absolute()}")
@router.get("/", response_model=ApiResponse)
def get_models(
skip: int = Query(0, ge=0, description="跳过记录数"),
limit: int = Query(100, ge=1, le=1000, description="返回记录数"),
brand_name: Optional[str] = Query(None, description="按品牌名称模糊查询"),
name: Optional[str] = Query(None, description="按型号名称模糊查询"),
db: Session = Depends(get_db)
):
"""获取所有型号列表(支持按品牌名称和型号名称模糊查询)"""
try:
logger.info(f"Getting models: skip={skip}, limit={limit}, brand_name={brand_name}, name={name}")
query = db.query(Model)
if brand_name:
query = query.filter(Model.brand_name.like(f"%{brand_name}%"))
if name:
query = query.filter(Model.name.like(f"%{name}%"))
total = query.count()
models = query.offset(skip).limit(limit).all()
logger.info(f"Found {len(models)} models, total={total}")
if not models:
logger.warning("No models found")
return ApiResponse(code=2, msg="empty", data=None)
# 转换为字典列表
models_data = [model.to_dict() for model in models]
return ApiResponse(code=1, msg="success", data={"items": models_data, "total": total, "skip": skip, "limit": limit})
except Exception as e:
logger.error(f"Error getting models: {str(e)}", exc_info=True)
return ApiResponse(code=0, msg="error", data=None)
@router.get("/{model_id}", response_model=ApiResponse)
def get_model(model_id: int, db: Session = Depends(get_db)):
"""获取单个型号"""
try:
logger.info(f"Getting model: id={model_id}")
model = db.query(Model).filter(Model.id == model_id).first()
if not model:
logger.warning(f"Model not found: id={model_id}")
return ApiResponse(code=2, msg="empty", data=None)
logger.info(f"Model found: {model.to_dict()}")
return ApiResponse(code=1, msg="success", data=model.to_dict())
except Exception as e:
logger.error(f"Error getting model {model_id}: {str(e)}", exc_info=True)
return ApiResponse(code=0, msg="error", data=None)
@router.post("/", response_model=ApiResponse)
def create_model(
brand_name: str = Form(...),
name: str = Form(...),
form: str = Form(None),
rig: str = Form(None),
source: str = Form(None),
eq_key: str = Form(None),
measurement_file: UploadFile = File(None),
db: Session = Depends(get_db)
):
"""创建新型号(支持文件上传)"""
try:
logger.info(f"Creating model: brand_name={brand_name}, name={name}, form={form}, source={source}")
# 检查是否已存在
existing = db.query(Model).filter(
Model.brand_name == brand_name,
Model.name == name
).first()
if existing:
logger.warning(f"Model already exists: brand_name={brand_name}, name={name}")
return ApiResponse(code=0, msg="该品牌下型号名称已存在", data=None)
# 处理文件上传
measurement_filename = None
if measurement_file and measurement_file.filename:
logger.info(f"Uploading measurement file: {measurement_file.filename}")
logger.info(f"UPLOAD_FOLDER is: {UPLOAD_FOLDER}")
# 验证文件扩展名
file_ext = os.path.splitext(measurement_file.filename)[1].lower()
if file_ext not in ALLOWED_EXTENSIONS:
logger.error(f"Unsupported file format: {file_ext}")
return ApiResponse(code=0, msg=f"不支持的文件格式:{file_ext}", data=None)
# 创建保存路径:autoeq/measurements/{source}/data/{form}/{filename}
save_dir = UPLOAD_FOLDER / source / "data" / form
logger.info(f"Creating directory: {save_dir}")
save_dir.mkdir(parents=True, exist_ok=True)
# 保存文件(保留原文件名)
file_path = save_dir / measurement_file.filename
logger.info(f"Saving file to: {file_path}")
with open(file_path, "wb") as buffer:
shutil.copyfileobj(measurement_file.file, buffer)
measurement_filename = measurement_file.filename
logger.info(f"File saved: {file_path}")
db_model = Model(
brand_name=brand_name,
name=name,
form=form,
rig=rig,
source=source,
eq_key=eq_key
)
db.add(db_model)
db.commit()
db.refresh(db_model)
logger.info(f"Model created successfully: id={db_model.id}")
return ApiResponse(code=1, msg="success", data=db_model.to_dict())
except Exception as e:
logger.error(f"Error creating model: {str(e)}", exc_info=True)
db.rollback()
return ApiResponse(code=0, msg="error", data=None)
@router.put("/{model_id}", response_model=ApiResponse)
def update_model(model_id: int, model: ModelUpdate, db: Session = Depends(get_db)):
"""更新型号"""
try:
logger.info(f"Updating model: id={model_id}, data={model.dict()}")
db_model = db.query(Model).filter(Model.id == model_id).first()
if not db_model:
logger.warning(f"Model not found: id={model_id}")
return ApiResponse(code=2, msg="empty", data=None)
# 如果更新品牌或型号名称,检查是否冲突
if model.brand_name or model.name:
new_brand_name = model.brand_name or db_model.brand_name
new_name = model.name or db_model.name
if new_brand_name != db_model.brand_name or new_name != db_model.name:
existing = db.query(Model).filter(
Model.brand_name == new_brand_name,
Model.name == new_name
).first()
if existing:
logger.warning(f"Model already exists: brand_name={new_brand_name}, name={new_name}")
return ApiResponse(code=0, msg="该品牌下型号名称已存在", data=None)
# 更新字段
if model.brand_name:
db_model.brand_name = model.brand_name
if model.name:
db_model.name = model.name
if model.form is not None:
db_model.form = model.form
if model.rig is not None:
db_model.rig = model.rig
if model.source is not None:
db_model.source = model.source
if model.eq_key is not None:
db_model.eq_key = model.eq_key
db.commit()
db.refresh(db_model)
logger.info(f"Model updated successfully: id={db_model.id}")
return ApiResponse(code=1, msg="success", data=db_model.to_dict())
except Exception as e:
logger.error(f"Error updating model {model_id}: {str(e)}", exc_info=True)
db.rollback()
return ApiResponse(code=0, msg="error", data=None)
@router.delete("/{model_id}", response_model=ApiResponse)
def delete_model(model_id: int, db: Session = Depends(get_db)):
"""删除型号"""
try:
logger.info(f"Deleting model: id={model_id}")
db_model = db.query(Model).filter(Model.id == model_id).first()
if not db_model:
logger.warning(f"Model not found: id={model_id}")
return ApiResponse(code=2, msg="empty", data=None)
db.delete(db_model)
db.commit()
logger.info(f"Model deleted successfully: id={model_id}")
return ApiResponse(code=1, msg="success", data=None)
except Exception as e:
logger.error(f"Error deleting model {model_id}: {str(e)}", exc_info=True)
db.rollback()
return ApiResponse(code=0, msg="error", data=None)
@router.post("/push-to-search", response_model=ApiResponse)
def push_to_search(
model_ids: List[int] = Body(..., embed=True, description="型号 ID 列表"),
db: Session = Depends(get_db)
):
"""推送型号数据到 Meilisearch"""
try:
if not model_ids:
return ApiResponse(code=0, msg="请选择要推送的型号", data=None)
# 查询选中的型号
models = db.query(Model).filter(Model.id.in_(model_ids)).all()
if not models:
return ApiResponse(code=0, msg="未找到选中的型号数据", data=None)
# 准备推送数据
push_data = []
for model in models:
model_dict = {
'id': model.id,
'brand_name': model.brand_name,
'name': model.name,
'rig': model.rig,
'form': model.form,
'source': model.source
}
# 只包含有值的字段
push_data.append({k: v for k, v in model_dict.items() if v is not None})
# 推送到 Meilisearch
headers = {
"Authorization": f"Bearer {MEILISEARCH_API_KEY}",
"Content-Type": "application/json"
}
# 如果索引不存在,先创建索引
try:
# 更新或添加文档
response = requests.post(
f"{MEILISEARCH_URL}/indexes/{MEILISEARCH_INDEX}/documents",
json=push_data,
headers=headers,
timeout=30
)
if response.status_code not in [200, 202]:
return ApiResponse(code=0, msg=f"推送到 Meilisearch 失败:{response.text}", data=None)
task_info = response.json()
return ApiResponse(
code=1,
msg="success",
data={
"pushed_count": len(push_data),
"task_uid": task_info.get("taskUid"),
"models": push_data
}
)
except requests.exceptions.RequestException as e:
return ApiResponse(code=0, msg=f"连接 Meilisearch 失败:{str(e)}", data=None)
except Exception as e:
return ApiResponse(code=0, msg="error", data=None)