- Add SystemSettings model to track setup completion status - Create SystemSettingsService for managing setup flags - Modify fastapi_app startup to check and set initial_scan_completed flag - Anime folder scanning now only runs on first startup - Update DATABASE.md with new system_settings table documentation - Add unit test for SystemSettingsService functionality This ensures expensive one-time operations like scanning the entire anime directory only occur during initial setup, not on every application restart.
160 lines
5.2 KiB
Python
160 lines
5.2 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 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")
|