- Add migration_legacy_files_completed flag to SystemSettings model - Create legacy_file_migration service to migrate series from key/data files - Integrate legacy migration into initialization_service startup flow - Add integration tests for legacy file migration - Update DATABASE.md documentation with migration details - Fix various test and service issues (nfo_repair, tmdb_client, download_service) - Add test_database_schema unit tests
190 lines
6.3 KiB
Python
190 lines
6.3 KiB
Python
"""System settings service for managing application-level configuration.
|
|
|
|
This module provides services for managing system-wide settings and state,
|
|
including tracking initial setup completion status.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timezone
|
|
from typing import Optional
|
|
|
|
import structlog
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from src.server.database.models import SystemSettings
|
|
|
|
logger = structlog.get_logger(__name__)
|
|
|
|
|
|
class SystemSettingsService:
|
|
"""Service for managing system settings."""
|
|
|
|
@staticmethod
|
|
async def get_or_create(db: AsyncSession) -> SystemSettings:
|
|
"""Get the system settings record, creating it if it doesn't exist.
|
|
|
|
Only one system settings record should exist in the database.
|
|
|
|
Args:
|
|
db: Database session
|
|
|
|
Returns:
|
|
SystemSettings instance
|
|
"""
|
|
# Try to get existing settings
|
|
stmt = select(SystemSettings).limit(1)
|
|
result = await db.execute(stmt)
|
|
settings = result.scalar_one_or_none()
|
|
|
|
if settings is None:
|
|
# Create new settings with defaults
|
|
settings = SystemSettings(
|
|
initial_scan_completed=False,
|
|
initial_nfo_scan_completed=False,
|
|
initial_media_scan_completed=False,
|
|
)
|
|
db.add(settings)
|
|
await db.commit()
|
|
await db.refresh(settings)
|
|
logger.info("Created new system settings record")
|
|
|
|
return settings
|
|
|
|
@staticmethod
|
|
async def is_initial_scan_completed(db: AsyncSession) -> bool:
|
|
"""Check if the initial anime folder scan has been completed.
|
|
|
|
Args:
|
|
db: Database session
|
|
|
|
Returns:
|
|
True if initial scan is completed, False otherwise
|
|
"""
|
|
settings = await SystemSettingsService.get_or_create(db)
|
|
return settings.initial_scan_completed
|
|
|
|
@staticmethod
|
|
async def mark_initial_scan_completed(
|
|
db: AsyncSession,
|
|
timestamp: Optional[datetime] = None
|
|
) -> None:
|
|
"""Mark the initial anime folder scan as completed.
|
|
|
|
Args:
|
|
db: Database session
|
|
timestamp: Optional timestamp to set, defaults to current time
|
|
"""
|
|
settings = await SystemSettingsService.get_or_create(db)
|
|
settings.initial_scan_completed = True
|
|
settings.last_scan_timestamp = timestamp or datetime.now(timezone.utc)
|
|
await db.commit()
|
|
logger.info("Marked initial scan as completed")
|
|
|
|
@staticmethod
|
|
async def is_initial_nfo_scan_completed(db: AsyncSession) -> bool:
|
|
"""Check if the initial NFO scan has been completed.
|
|
|
|
Args:
|
|
db: Database session
|
|
|
|
Returns:
|
|
True if initial NFO scan is completed, False otherwise
|
|
"""
|
|
settings = await SystemSettingsService.get_or_create(db)
|
|
return settings.initial_nfo_scan_completed
|
|
|
|
@staticmethod
|
|
async def mark_initial_nfo_scan_completed(
|
|
db: AsyncSession,
|
|
timestamp: Optional[datetime] = None
|
|
) -> None:
|
|
"""Mark the initial NFO scan as completed.
|
|
|
|
Args:
|
|
db: Database session
|
|
timestamp: Optional timestamp to set, defaults to current time
|
|
"""
|
|
settings = await SystemSettingsService.get_or_create(db)
|
|
settings.initial_nfo_scan_completed = True
|
|
if timestamp:
|
|
settings.last_scan_timestamp = timestamp
|
|
await db.commit()
|
|
logger.info("Marked initial NFO scan as completed")
|
|
|
|
@staticmethod
|
|
async def is_initial_media_scan_completed(db: AsyncSession) -> bool:
|
|
"""Check if the initial media scan has been completed.
|
|
|
|
Args:
|
|
db: Database session
|
|
|
|
Returns:
|
|
True if initial media scan is completed, False otherwise
|
|
"""
|
|
settings = await SystemSettingsService.get_or_create(db)
|
|
return settings.initial_media_scan_completed
|
|
|
|
@staticmethod
|
|
async def is_migration_legacy_files_completed(db: AsyncSession) -> bool:
|
|
"""Check if legacy key/data file migration has been completed.
|
|
|
|
Args:
|
|
db: Database session
|
|
|
|
Returns:
|
|
True if legacy migration is completed, False otherwise
|
|
"""
|
|
settings = await SystemSettingsService.get_or_create(db)
|
|
return settings.migration_legacy_files_completed
|
|
|
|
@staticmethod
|
|
async def mark_migration_legacy_files_completed(
|
|
db: AsyncSession,
|
|
timestamp: Optional[datetime] = None
|
|
) -> None:
|
|
"""Mark the legacy key/data file migration as completed.
|
|
|
|
Args:
|
|
db: Database session
|
|
timestamp: Optional timestamp to set, defaults to current time
|
|
"""
|
|
settings = await SystemSettingsService.get_or_create(db)
|
|
settings.migration_legacy_files_completed = True
|
|
settings.last_scan_timestamp = timestamp or datetime.now(timezone.utc)
|
|
await db.commit()
|
|
logger.info("Marked legacy files migration as completed")
|
|
|
|
@staticmethod
|
|
async def mark_initial_media_scan_completed(
|
|
db: AsyncSession,
|
|
timestamp: Optional[datetime] = None
|
|
) -> None:
|
|
"""Mark the initial media scan as completed.
|
|
|
|
Args:
|
|
db: Database session
|
|
timestamp: Optional timestamp to set, defaults to current time
|
|
"""
|
|
settings = await SystemSettingsService.get_or_create(db)
|
|
settings.initial_media_scan_completed = True
|
|
if timestamp:
|
|
settings.last_scan_timestamp = timestamp
|
|
await db.commit()
|
|
logger.info("Marked initial media scan as completed")
|
|
|
|
@staticmethod
|
|
async def reset_all_scans(db: AsyncSession) -> None:
|
|
"""Reset all scan completion flags (for testing or re-setup).
|
|
|
|
Args:
|
|
db: Database session
|
|
"""
|
|
settings = await SystemSettingsService.get_or_create(db)
|
|
settings.initial_scan_completed = False
|
|
settings.initial_nfo_scan_completed = False
|
|
settings.initial_media_scan_completed = False
|
|
settings.last_scan_timestamp = None
|
|
await db.commit()
|
|
logger.info("Reset all scan completion flags")
|