mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-10-02 16:45:15 -07:00
chore(backend): repair test suite and bring ruff to green
The suite hadn't run green since the routes refactor: - test_profile_duplicate_names.py imported the pre-refactor module layout and broke collection; now imports backend.services.profiles - tests/conftest.py puts the repo root and backend dir on sys.path so files collect standalone instead of depending on run order - test_cors.py tested a hand-copied mirror of the origin list that had drifted from app.py (missing http://tauri.localhost); it now builds the app via the real create_app() factory - test_progress.py simulated a 1KB download, below the tracker's 1MB reporting threshold; simulation raised to 5MB - slow/timeout markers registered in pyproject Ruff: ~900 violations auto-fixed (typing modernization, import sorting, unused imports, whitespace). The remaining rules are baselined in pyproject.toml with per-rule counts to burn down, plus per-file carve-outs for deliberate env-before-import ordering. ruff check is now clean; suite is 134 passed, 2 skipped.
This commit is contained in:
+54
-46
@@ -2,17 +2,25 @@
|
||||
Generation history management module.
|
||||
"""
|
||||
|
||||
from typing import List, Optional, Tuple
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import or_
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ..models import GenerationRequest, GenerationResponse, HistoryQuery, HistoryResponse, HistoryListResponse, GenerationVersionResponse, EffectConfig
|
||||
from ..database import Generation as DBGeneration, GenerationVersion as DBGenerationVersion, VoiceProfile as DBVoiceProfile
|
||||
from .. import config
|
||||
from ..database import (
|
||||
Generation as DBGeneration,
|
||||
GenerationVersion as DBGenerationVersion,
|
||||
VoiceProfile as DBVoiceProfile,
|
||||
)
|
||||
from ..models import (
|
||||
EffectConfig,
|
||||
GenerationResponse,
|
||||
GenerationVersionResponse,
|
||||
HistoryListResponse,
|
||||
HistoryQuery,
|
||||
HistoryResponse,
|
||||
)
|
||||
|
||||
|
||||
def _get_versions_for_generation(generation_id: str, db: Session) -> tuple:
|
||||
@@ -58,13 +66,13 @@ async def create_generation(
|
||||
language: str,
|
||||
audio_path: str,
|
||||
duration: float,
|
||||
seed: Optional[int],
|
||||
seed: int | None,
|
||||
db: Session,
|
||||
instruct: Optional[str] = None,
|
||||
generation_id: Optional[str] = None,
|
||||
instruct: str | None = None,
|
||||
generation_id: str | None = None,
|
||||
status: str = "completed",
|
||||
engine: Optional[str] = "qwen",
|
||||
model_size: Optional[str] = None,
|
||||
engine: str | None = "qwen",
|
||||
model_size: str | None = None,
|
||||
source: str = "manual",
|
||||
) -> GenerationResponse:
|
||||
"""
|
||||
@@ -118,10 +126,10 @@ async def update_generation_status(
|
||||
generation_id: str,
|
||||
status: str,
|
||||
db: Session,
|
||||
audio_path: Optional[str] = None,
|
||||
duration: Optional[float] = None,
|
||||
error: Optional[str] = None,
|
||||
) -> Optional[GenerationResponse]:
|
||||
audio_path: str | None = None,
|
||||
duration: float | None = None,
|
||||
error: str | None = None,
|
||||
) -> GenerationResponse | None:
|
||||
"""Update the status of a generation (used by async generation flow)."""
|
||||
generation = db.query(DBGeneration).filter_by(id=generation_id).first()
|
||||
if not generation:
|
||||
@@ -143,21 +151,21 @@ async def update_generation_status(
|
||||
async def get_generation(
|
||||
generation_id: str,
|
||||
db: Session,
|
||||
) -> Optional[GenerationResponse]:
|
||||
) -> GenerationResponse | None:
|
||||
"""
|
||||
Get a generation by ID.
|
||||
|
||||
|
||||
Args:
|
||||
generation_id: Generation ID
|
||||
db: Database session
|
||||
|
||||
|
||||
Returns:
|
||||
Generation or None if not found
|
||||
"""
|
||||
generation = db.query(DBGeneration).filter_by(id=generation_id).first()
|
||||
if not generation:
|
||||
return None
|
||||
|
||||
|
||||
return GenerationResponse.model_validate(generation)
|
||||
|
||||
|
||||
@@ -167,11 +175,11 @@ async def list_generations(
|
||||
) -> HistoryListResponse:
|
||||
"""
|
||||
List generations with optional filters.
|
||||
|
||||
|
||||
Args:
|
||||
query: Query parameters (filters, pagination)
|
||||
db: Database session
|
||||
|
||||
|
||||
Returns:
|
||||
HistoryListResponse with items and total count
|
||||
"""
|
||||
@@ -183,28 +191,28 @@ async def list_generations(
|
||||
DBVoiceProfile,
|
||||
DBGeneration.profile_id == DBVoiceProfile.id
|
||||
)
|
||||
|
||||
|
||||
# Apply profile filter
|
||||
if query.profile_id:
|
||||
q = q.filter(DBGeneration.profile_id == query.profile_id)
|
||||
|
||||
|
||||
# Apply search filter (searches in text content)
|
||||
if query.search:
|
||||
search_pattern = f"%{query.search}%"
|
||||
q = q.filter(DBGeneration.text.like(search_pattern))
|
||||
|
||||
|
||||
# Get total count before pagination
|
||||
total_count = q.count()
|
||||
|
||||
|
||||
# Apply ordering (newest first)
|
||||
q = q.order_by(DBGeneration.created_at.desc())
|
||||
|
||||
|
||||
# Apply pagination
|
||||
q = q.offset(query.offset).limit(query.limit)
|
||||
|
||||
|
||||
# Execute query
|
||||
results = q.all()
|
||||
|
||||
|
||||
# Convert to HistoryResponse with profile_name
|
||||
items = []
|
||||
for generation, profile_name in results:
|
||||
@@ -228,7 +236,7 @@ async def list_generations(
|
||||
versions=versions,
|
||||
active_version_id=active_version_id,
|
||||
))
|
||||
|
||||
|
||||
return HistoryListResponse(
|
||||
items=items,
|
||||
total=total_count,
|
||||
@@ -241,11 +249,11 @@ async def delete_generation(
|
||||
) -> bool:
|
||||
"""
|
||||
Delete a generation.
|
||||
|
||||
|
||||
Args:
|
||||
generation_id: Generation ID
|
||||
db: Database session
|
||||
|
||||
|
||||
Returns:
|
||||
True if deleted, False if not found
|
||||
"""
|
||||
@@ -266,7 +274,7 @@ async def delete_generation(
|
||||
# Delete from database
|
||||
db.delete(generation)
|
||||
db.commit()
|
||||
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -313,16 +321,16 @@ async def delete_generations_by_profile(
|
||||
) -> int:
|
||||
"""
|
||||
Delete all generations for a profile.
|
||||
|
||||
|
||||
Args:
|
||||
profile_id: Profile ID
|
||||
db: Database session
|
||||
|
||||
|
||||
Returns:
|
||||
Number of generations deleted
|
||||
"""
|
||||
generations = db.query(DBGeneration).filter_by(profile_id=profile_id).all()
|
||||
|
||||
|
||||
count = 0
|
||||
for generation in generations:
|
||||
# Delete associated version files and rows first
|
||||
@@ -333,38 +341,38 @@ async def delete_generations_by_profile(
|
||||
audio_path = config.resolve_storage_path(generation.audio_path)
|
||||
if audio_path is not None and audio_path.exists():
|
||||
audio_path.unlink()
|
||||
|
||||
|
||||
# Delete from database
|
||||
db.delete(generation)
|
||||
count += 1
|
||||
|
||||
|
||||
db.commit()
|
||||
|
||||
|
||||
return count
|
||||
|
||||
|
||||
async def get_generation_stats(db: Session) -> dict:
|
||||
"""
|
||||
Get generation statistics.
|
||||
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
|
||||
|
||||
Returns:
|
||||
Statistics dictionary
|
||||
"""
|
||||
from sqlalchemy import func
|
||||
|
||||
|
||||
total = db.query(func.count(DBGeneration.id)).scalar()
|
||||
|
||||
|
||||
total_duration = db.query(func.sum(DBGeneration.duration)).scalar() or 0
|
||||
|
||||
|
||||
# Get generations by profile
|
||||
by_profile = db.query(
|
||||
DBGeneration.profile_id,
|
||||
func.count(DBGeneration.id).label('count')
|
||||
).group_by(DBGeneration.profile_id).all()
|
||||
|
||||
|
||||
return {
|
||||
"total_generations": total,
|
||||
"total_duration_seconds": total_duration,
|
||||
|
||||
Reference in New Issue
Block a user