Add initial files for Audio Dashboard Management System
- Created .env.example for environment variable configuration. - Added docker-compose.yml for service orchestration. - Implemented frequency response extraction in extract_frequency_response.py and convert_to_frequency_db.py. - Generated output files: frequency_response_detailed.json, frequency_response_points.json, frequency_response.csv, and frequency_response_curve.png. - Included sample measurement data for FiiO FA19 in CSV format.
This commit is contained in:
@@ -36,3 +36,4 @@ wheels/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
logs/
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Create logs directory
|
||||
RUN mkdir -p /app/logs
|
||||
|
||||
# Install dependencies
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Expose port
|
||||
EXPOSE 8000
|
||||
|
||||
# Run the application
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
+5
-3
@@ -14,13 +14,15 @@ DATABASE_NAME = os.getenv("DATABASE_NAME", "audio")
|
||||
DATABASE_USER = os.getenv("DATABASE_USER", "root")
|
||||
DATABASE_PASSWORD = os.getenv("DATABASE_PASSWORD", "root123")
|
||||
|
||||
# Create database URL (same format as working code)
|
||||
# Create database URL (only charset parameter)
|
||||
DATABASE_URL = f"mysql+pymysql://{DATABASE_USER}:{DATABASE_PASSWORD}@{DATABASE_HOST}:{DATABASE_PORT}/{DATABASE_NAME}?charset=utf8mb4"
|
||||
|
||||
# Create engine (same config as working code)
|
||||
# Create engine with additional connection arguments
|
||||
# Use connect_args for parameters that shouldn't be in the URL
|
||||
engine = create_engine(
|
||||
DATABASE_URL,
|
||||
echo=os.getenv("DEBUG", "False") == "True"
|
||||
echo=os.getenv("DEBUG", "False") == "True",
|
||||
connect_args={}
|
||||
)
|
||||
|
||||
# Create session factory
|
||||
|
||||
+16
-2
@@ -1,6 +1,7 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import os
|
||||
import logging
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from database import engine, Base
|
||||
@@ -9,12 +10,25 @@ from routes import brands_router, models_router
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.StreamHandler(), # 输出到控制台
|
||||
logging.FileHandler('logs/app.log', encoding='utf-8') # 输出到文件
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Create database tables (only if tables don't exist)
|
||||
try:
|
||||
logger.info("Creating database tables...")
|
||||
Base.metadata.create_all(bind=engine)
|
||||
logger.info("Database tables created successfully")
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not create tables: {e}")
|
||||
print("Continuing without table creation...")
|
||||
logger.error(f"Warning: Could not create tables: {e}")
|
||||
logger.error("Continuing without table creation...")
|
||||
|
||||
# Create FastAPI app
|
||||
app = FastAPI(
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
fastapi==0.104.1
|
||||
sqlalchemy==2.0.23
|
||||
pymysql==1.1.0
|
||||
pymysql==1.1.1
|
||||
cryptography==41.0.7
|
||||
pydantic==2.5.2
|
||||
uvicorn==0.24.0
|
||||
python-dotenv==1.0.0
|
||||
requests==2.31.0
|
||||
python-multipart==0.0.6
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List, Optional
|
||||
import logging
|
||||
from database import get_db
|
||||
from models.brand import Brand
|
||||
from schemas import BrandCreate, BrandUpdate, BrandResponse
|
||||
from response import ApiResponse, PageData
|
||||
|
||||
# Configure logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/api/brands", tags=["brands"])
|
||||
|
||||
|
||||
@@ -18,6 +22,7 @@ def get_brands(
|
||||
):
|
||||
"""获取所有品牌列表(支持按名称模糊查询)"""
|
||||
try:
|
||||
logger.info(f"Getting brands: skip={skip}, limit={limit}, name={name}")
|
||||
query = db.query(Brand)
|
||||
if name:
|
||||
query = query.filter(Brand.name.like(f"%{name}%"))
|
||||
@@ -25,7 +30,10 @@ def get_brands(
|
||||
total = query.count()
|
||||
brands = query.offset(skip).limit(limit).all()
|
||||
|
||||
logger.info(f"Found {len(brands)} brands, total={total}")
|
||||
|
||||
if not brands:
|
||||
logger.warning("No brands found")
|
||||
return ApiResponse(code=2, msg="empty", data=None)
|
||||
|
||||
# 转换为字典列表
|
||||
@@ -33,6 +41,7 @@ def get_brands(
|
||||
|
||||
return ApiResponse(code=1, msg="success", data={"items": brands_data, "total": total, "skip": skip, "limit": limit})
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting brands: {str(e)}", exc_info=True)
|
||||
return ApiResponse(code=0, msg="error", data=None)
|
||||
|
||||
|
||||
@@ -40,11 +49,15 @@ def get_brands(
|
||||
def get_brand(brand_id: int, db: Session = Depends(get_db)):
|
||||
"""获取单个品牌"""
|
||||
try:
|
||||
logger.info(f"Getting brand: id={brand_id}")
|
||||
brand = db.query(Brand).filter(Brand.id == brand_id).first()
|
||||
if not brand:
|
||||
logger.warning(f"Brand not found: id={brand_id}")
|
||||
return ApiResponse(code=2, msg="empty", data=None)
|
||||
logger.info(f"Brand found: {brand.to_dict()}")
|
||||
return ApiResponse(code=1, msg="success", data=brand.to_dict())
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting brand {brand_id}: {str(e)}", exc_info=True)
|
||||
return ApiResponse(code=0, msg="error", data=None)
|
||||
|
||||
|
||||
@@ -52,17 +65,21 @@ def get_brand(brand_id: int, db: Session = Depends(get_db)):
|
||||
def create_brand(brand: BrandCreate, db: Session = Depends(get_db)):
|
||||
"""创建新品牌"""
|
||||
try:
|
||||
logger.info(f"Creating brand: name={brand.name}")
|
||||
# 检查是否已存在
|
||||
existing = db.query(Brand).filter(Brand.name == brand.name).first()
|
||||
if existing:
|
||||
logger.warning(f"Brand already exists: name={brand.name}")
|
||||
return ApiResponse.error(msg="品牌名称已存在", code=0)
|
||||
|
||||
db_brand = Brand(name=brand.name)
|
||||
db.add(db_brand)
|
||||
db.commit()
|
||||
db.refresh(db_brand)
|
||||
logger.info(f"Brand created successfully: id={db_brand.id}, name={db_brand.name}")
|
||||
return ApiResponse.success(data=db_brand.to_dict(), msg="品牌创建成功")
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating brand: {str(e)}", exc_info=True)
|
||||
db.rollback()
|
||||
return ApiResponse.error(msg=f"创建失败:{str(e)}", code=0)
|
||||
|
||||
@@ -71,21 +88,26 @@ def create_brand(brand: BrandCreate, db: Session = Depends(get_db)):
|
||||
def update_brand(brand_id: int, brand: BrandUpdate, db: Session = Depends(get_db)):
|
||||
"""更新品牌"""
|
||||
try:
|
||||
logger.info(f"Updating brand: id={brand_id}, data={brand.dict()}")
|
||||
db_brand = db.query(Brand).filter(Brand.id == brand_id).first()
|
||||
if not db_brand:
|
||||
logger.warning(f"Brand not found: id={brand_id}")
|
||||
return ApiResponse.no_data(msg="品牌不存在")
|
||||
|
||||
# 如果更新名称,检查是否冲突
|
||||
if brand.name and brand.name != db_brand.name:
|
||||
existing = db.query(Brand).filter(Brand.name == brand.name).first()
|
||||
if existing:
|
||||
logger.warning(f"Brand name already exists: name={brand.name}")
|
||||
return ApiResponse.error(msg="品牌名称已存在", code=0)
|
||||
db_brand.name = brand.name
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_brand)
|
||||
logger.info(f"Brand updated successfully: id={db_brand.id}")
|
||||
return ApiResponse.success(data=db_brand.to_dict(), msg="品牌更新成功")
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating brand {brand_id}: {str(e)}", exc_info=True)
|
||||
db.rollback()
|
||||
return ApiResponse.error(msg=f"更新失败:{str(e)}", code=0)
|
||||
|
||||
@@ -94,13 +116,17 @@ def update_brand(brand_id: int, brand: BrandUpdate, db: Session = Depends(get_db
|
||||
def delete_brand(brand_id: int, db: Session = Depends(get_db)):
|
||||
"""删除品牌"""
|
||||
try:
|
||||
logger.info(f"Deleting brand: id={brand_id}")
|
||||
db_brand = db.query(Brand).filter(Brand.id == brand_id).first()
|
||||
if not db_brand:
|
||||
logger.warning(f"Brand not found: id={brand_id}")
|
||||
return ApiResponse.no_data(msg="品牌不存在")
|
||||
|
||||
db.delete(db_brand)
|
||||
db.commit()
|
||||
logger.info(f"Brand deleted successfully: id={brand_id}")
|
||||
return ApiResponse.success(msg="删除成功")
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting brand {brand_id}: {str(e)}", exc_info=True)
|
||||
db.rollback()
|
||||
return ApiResponse.error(msg=f"删除失败:{str(e)}", code=0)
|
||||
|
||||
+74
-11
@@ -1,12 +1,18 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Body
|
||||
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"])
|
||||
|
||||
@@ -15,6 +21,10 @@ 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(__file__).parent.parent.parent / "autoeq" / "measurements"
|
||||
ALLOWED_EXTENSIONS = {'.csv', '.txt', '.json'}
|
||||
|
||||
|
||||
@router.get("/", response_model=ApiResponse)
|
||||
def get_models(
|
||||
@@ -26,6 +36,7 @@ def get_models(
|
||||
):
|
||||
"""获取所有型号列表(支持按品牌名称和型号名称模糊查询)"""
|
||||
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}%"))
|
||||
@@ -35,7 +46,10 @@ def get_models(
|
||||
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)
|
||||
|
||||
# 转换为字典列表
|
||||
@@ -43,6 +57,7 @@ def get_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)
|
||||
|
||||
|
||||
@@ -50,39 +65,78 @@ def get_models(
|
||||
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(model: ModelCreate, db: Session = Depends(get_db)):
|
||||
"""创建新型号"""
|
||||
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 == model.brand_name,
|
||||
Model.name == model.name
|
||||
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}")
|
||||
# 验证文件扩展名
|
||||
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
|
||||
save_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 保存文件(保留原文件名)
|
||||
file_path = save_dir / measurement_file.filename
|
||||
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=model.brand_name,
|
||||
name=model.name,
|
||||
form=model.form,
|
||||
rig=model.rig,
|
||||
source=model.source,
|
||||
eq_key=model.eq_key
|
||||
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)
|
||||
|
||||
@@ -91,8 +145,10 @@ def create_model(model: ModelCreate, db: Session = Depends(get_db)):
|
||||
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)
|
||||
|
||||
# 如果更新品牌或型号名称,检查是否冲突
|
||||
@@ -106,6 +162,7 @@ def update_model(model_id: int, model: ModelUpdate, db: Session = Depends(get_db
|
||||
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)
|
||||
|
||||
# 更新字段
|
||||
@@ -124,8 +181,10 @@ def update_model(model_id: int, model: ModelUpdate, db: Session = Depends(get_db
|
||||
|
||||
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)
|
||||
|
||||
@@ -134,14 +193,18 @@ def update_model(model_id: int, model: ModelUpdate, db: Session = Depends(get_db
|
||||
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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user