Implement initial scan tracking for one-time setup
- 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.
This commit is contained in:
159
src/server/database/system_settings_service.py
Normal file
159
src/server/database/system_settings_service.py
Normal file
@@ -0,0 +1,159 @@
|
||||
"""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")
|
||||
Reference in New Issue
Block a user