Rename sync_series_from_data_files to sync_legacy_series_to_db

- Rename function to reflect its legacy status
- Add deprecation warning log on execution
- Update all callers (initialization_service, api/config, fastapi_app)
- Update tests to use new name
- Add deprecation notice to DEVELOPMENT.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-05-26 18:45:22 +02:00
parent 53d6da5dac
commit ceb6a2aeb4
8 changed files with 39 additions and 37 deletions

View File

@@ -284,9 +284,9 @@ async def update_directory(
try: try:
import structlog import structlog
from src.server.services.anime_service import sync_series_from_data_files from src.server.services.anime_service import sync_legacy_series_to_db
logger = structlog.get_logger(__name__) logger = structlog.get_logger(__name__)
sync_count = await sync_series_from_data_files(directory, logger) sync_count = await sync_legacy_series_to_db(directory, logger)
logger.info( logger.info(
"Directory updated: synced series from data files", "Directory updated: synced series from data files",
directory=directory, directory=directory,

View File

@@ -38,7 +38,6 @@ from src.server.controllers.page_controller import router as page_router
from src.server.middleware.auth import AuthMiddleware from src.server.middleware.auth import AuthMiddleware
from src.server.middleware.error_handler import register_exception_handlers from src.server.middleware.error_handler import register_exception_handlers
from src.server.middleware.setup_redirect import SetupRedirectMiddleware from src.server.middleware.setup_redirect import SetupRedirectMiddleware
from src.server.services.anime_service import sync_series_from_data_files
from src.server.services.progress_service import get_progress_service from src.server.services.progress_service import get_progress_service
from src.server.services.websocket_service import get_websocket_service from src.server.services.websocket_service import get_websocket_service

View File

@@ -1554,19 +1554,17 @@ def get_anime_service(series_app: SeriesApp) -> AnimeService:
return AnimeService(series_app) return AnimeService(series_app)
async def sync_series_from_data_files( async def sync_legacy_series_to_db(
anime_directory: str, anime_directory: str,
log_instance=None # pylint: disable=unused-argument log_instance=None # pylint: disable=unused-argument
) -> int: ) -> int:
""" """
Sync series from data files to the database. One-time legacy sync: import any series from 'data' files
not already in the database.
Scans the anime directory for data files and adds any new series Deprecated: Series are now loaded directly from the database.
to the database. Existing series are skipped (no duplicates). This function remains for backwards compatibility with legacy
file-based data during migration.
This function is typically called during application startup to ensure
series metadata stored in filesystem data files is available in the
database.
Args: Args:
anime_directory: Path to the anime directory with data files anime_directory: Path to the anime directory with data files
@@ -1578,6 +1576,11 @@ async def sync_series_from_data_files(
""" """
# Always use structlog for structured logging with keyword arguments # Always use structlog for structured logging with keyword arguments
log = structlog.get_logger(__name__) log = structlog.get_logger(__name__)
log.warning(
"sync_legacy_series_to_db is deprecated. "
"Series are now loaded directly from database."
)
try: try:
from src.server.database.connection import get_db_session from src.server.database.connection import get_db_session

View File

@@ -6,7 +6,7 @@ from typing import Callable, Optional
import structlog import structlog
from src.config.settings import settings from src.config.settings import settings
from src.server.services.anime_service import sync_series_from_data_files from src.server.services.anime_service import sync_legacy_series_to_db
from src.server.services.legacy_file_migration import migrate_series_from_files_to_db from src.server.services.legacy_file_migration import migrate_series_from_files_to_db
logger = structlog.get_logger(__name__) logger = structlog.get_logger(__name__)
@@ -170,7 +170,7 @@ async def _sync_anime_folders(progress_service=None) -> int:
metadata={"step_id": "series_sync"} metadata={"step_id": "series_sync"}
) )
sync_count = await sync_series_from_data_files(settings.anime_directory) sync_count = await sync_legacy_series_to_db(settings.anime_directory)
logger.info("Data file sync complete. Added %d series.", sync_count) logger.info("Data file sync complete. Added %d series.", sync_count)
if progress_service: if progress_service:

View File

@@ -111,17 +111,17 @@ class TestGetAllSeriesFromDataFiles:
class TestSyncSeriesToDatabase: class TestSyncSeriesToDatabase:
"""Test sync_series_from_data_files function from anime_service.""" """Test sync_legacy_series_to_db function from anime_service."""
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_sync_with_empty_directory(self): async def test_sync_with_empty_directory(self):
"""Test sync with empty anime directory.""" """Test sync with empty anime directory."""
from src.server.services.anime_service import sync_series_from_data_files from src.server.services.anime_service import sync_legacy_series_to_db
with tempfile.TemporaryDirectory() as tmp_dir: with tempfile.TemporaryDirectory() as tmp_dir:
with patch('src.core.SeriesApp.Loaders'), \ with patch('src.core.SeriesApp.Loaders'), \
patch('src.core.SeriesApp.SerieScanner'): patch('src.core.SeriesApp.SerieScanner'):
count = await sync_series_from_data_files(tmp_dir) count = await sync_legacy_series_to_db(tmp_dir)
assert count == 0 assert count == 0
# Function should complete successfully with no series # Function should complete successfully with no series
@@ -134,7 +134,7 @@ class TestSyncSeriesToDatabase:
from files and the sync function attempts to add them to the DB. from files and the sync function attempts to add them to the DB.
The actual DB interaction is tested in test_add_to_db_creates_record. The actual DB interaction is tested in test_add_to_db_creates_record.
""" """
from src.server.services.anime_service import sync_series_from_data_files from src.server.services.anime_service import sync_legacy_series_to_db
with tempfile.TemporaryDirectory() as tmp_dir: with tempfile.TemporaryDirectory() as tmp_dir:
# Create test data files # Create test data files
@@ -160,7 +160,7 @@ class TestSyncSeriesToDatabase:
patch('src.core.SeriesApp.SerieScanner'): patch('src.core.SeriesApp.SerieScanner'):
# The function should return 0 because DB isn't available # The function should return 0 because DB isn't available
# but should not crash # but should not crash
count = await sync_series_from_data_files(tmp_dir) count = await sync_legacy_series_to_db(tmp_dir)
# Since no real DB, it will fail gracefully # Since no real DB, it will fail gracefully
# Function returns 0 when DB operations fail # Function returns 0 when DB operations fail
@@ -170,7 +170,7 @@ class TestSyncSeriesToDatabase:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_sync_handles_exceptions_gracefully(self): async def test_sync_handles_exceptions_gracefully(self):
"""Test that sync handles exceptions without crashing.""" """Test that sync handles exceptions without crashing."""
from src.server.services.anime_service import sync_series_from_data_files from src.server.services.anime_service import sync_legacy_series_to_db
# Make SeriesApp raise an exception during initialization # Make SeriesApp raise an exception during initialization
with patch('src.core.SeriesApp.Loaders'), \ with patch('src.core.SeriesApp.Loaders'), \
@@ -179,7 +179,7 @@ class TestSyncSeriesToDatabase:
'src.core.SeriesApp.SerieList', 'src.core.SeriesApp.SerieList',
side_effect=Exception("Test error") side_effect=Exception("Test error")
): ):
count = await sync_series_from_data_files("/fake/path") count = await sync_legacy_series_to_db("/fake/path")
assert count == 0 assert count == 0
# Function should complete without crashing # Function should complete without crashing

View File

@@ -21,7 +21,7 @@ class TestInitializationWorkflow:
async def test_perform_initial_setup_with_mocked_dependencies(self): async def test_perform_initial_setup_with_mocked_dependencies(self):
"""Test initial setup completes with minimal mocking.""" """Test initial setup completes with minimal mocking."""
# Mock only the external dependencies # Mock only the external dependencies
with patch('src.server.services.anime_service.sync_series_from_data_files') as mock_sync: with patch('src.server.services.anime_service.sync_legacy_series_to_db') as mock_sync:
mock_sync.return_value = 0 # No series to sync mock_sync.return_value = 0 # No series to sync
# Call the actual function # Call the actual function
@@ -241,9 +241,9 @@ class TestModuleStructure:
assert hasattr(initialization_service, 'settings') assert hasattr(initialization_service, 'settings')
def test_sync_series_function_imported(self): def test_sync_series_function_imported(self):
"""Test sync_series_from_data_files is imported.""" """Test sync_legacy_series_to_db is imported."""
assert hasattr(initialization_service, 'sync_series_from_data_files') assert hasattr(initialization_service, 'sync_legacy_series_to_db')
assert callable(initialization_service.sync_series_from_data_files) assert callable(initialization_service.sync_legacy_series_to_db)
# Simpler integration tests that don't require complex mocking # Simpler integration tests that don't require complex mocking
@@ -413,7 +413,7 @@ class TestInitialSetupWorkflow:
async def test_initial_setup_already_completed(self): async def test_initial_setup_already_completed(self):
"""Test initial setup when already completed.""" """Test initial setup when already completed."""
with patch.object(initialization_service, '_check_initial_scan_status', return_value=True), \ with patch.object(initialization_service, '_check_initial_scan_status', return_value=True), \
patch('src.server.services.anime_service.sync_series_from_data_files'): patch('src.server.services.anime_service.sync_legacy_series_to_db'):
result = await initialization_service.perform_initial_setup() result = await initialization_service.perform_initial_setup()
@@ -425,7 +425,7 @@ class TestInitialSetupWorkflow:
"""Test initial setup with no directory configured.""" """Test initial setup with no directory configured."""
with patch.object(initialization_service, '_check_initial_scan_status', return_value=False), \ with patch.object(initialization_service, '_check_initial_scan_status', return_value=False), \
patch.object(initialization_service, '_validate_anime_directory', return_value=False), \ patch.object(initialization_service, '_validate_anime_directory', return_value=False), \
patch('src.server.services.anime_service.sync_series_from_data_files'): patch('src.server.services.anime_service.sync_legacy_series_to_db'):
result = await initialization_service.perform_initial_setup() result = await initialization_service.perform_initial_setup()
@@ -440,7 +440,7 @@ class TestInitialSetupWorkflow:
patch.object(initialization_service, '_sync_anime_folders', return_value=5), \ patch.object(initialization_service, '_sync_anime_folders', return_value=5), \
patch.object(initialization_service, '_mark_initial_scan_completed'), \ patch.object(initialization_service, '_mark_initial_scan_completed'), \
patch.object(initialization_service, '_load_series_into_memory'), \ patch.object(initialization_service, '_load_series_into_memory'), \
patch('src.server.services.anime_service.sync_series_from_data_files'): patch('src.server.services.anime_service.sync_legacy_series_to_db'):
mock_progress = AsyncMock() mock_progress = AsyncMock()
result = await initialization_service.perform_initial_setup(mock_progress) result = await initialization_service.perform_initial_setup(mock_progress)
@@ -456,7 +456,7 @@ class TestInitialSetupWorkflow:
with patch.object(initialization_service, '_check_initial_scan_status', return_value=False), \ with patch.object(initialization_service, '_check_initial_scan_status', return_value=False), \
patch.object(initialization_service, '_validate_anime_directory', return_value=True), \ patch.object(initialization_service, '_validate_anime_directory', return_value=True), \
patch.object(initialization_service, '_sync_anime_folders', side_effect=OSError("Disk error")), \ patch.object(initialization_service, '_sync_anime_folders', side_effect=OSError("Disk error")), \
patch('src.server.services.anime_service.sync_series_from_data_files'): patch('src.server.services.anime_service.sync_legacy_series_to_db'):
result = await initialization_service.perform_initial_setup() result = await initialization_service.perform_initial_setup()
@@ -469,7 +469,7 @@ class TestInitialSetupWorkflow:
with patch.object(initialization_service, '_check_initial_scan_status', return_value=False), \ with patch.object(initialization_service, '_check_initial_scan_status', return_value=False), \
patch.object(initialization_service, '_validate_anime_directory', return_value=True), \ patch.object(initialization_service, '_validate_anime_directory', return_value=True), \
patch.object(initialization_service, '_sync_anime_folders', side_effect=RuntimeError("DB error")), \ patch.object(initialization_service, '_sync_anime_folders', side_effect=RuntimeError("DB error")), \
patch('src.server.services.anime_service.sync_series_from_data_files'): patch('src.server.services.anime_service.sync_legacy_series_to_db'):
result = await initialization_service.perform_initial_setup() result = await initialization_service.perform_initial_setup()

View File

@@ -16,7 +16,7 @@ import pytest
from src.server.services.anime_service import ( from src.server.services.anime_service import (
AnimeService, AnimeService,
AnimeServiceError, AnimeServiceError,
sync_series_from_data_files, sync_legacy_series_to_db,
) )
from src.server.services.progress_service import ProgressService from src.server.services.progress_service import ProgressService
@@ -1303,7 +1303,7 @@ class TestGetNFOStatisticsSelfManaged:
class TestSyncSeriesFromDataFiles: class TestSyncSeriesFromDataFiles:
"""Test module-level sync_series_from_data_files function.""" """Test module-level sync_legacy_series_to_db function."""
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_sync_adds_new_series(self, tmp_path): async def test_sync_adds_new_series(self, tmp_path):
@@ -1343,7 +1343,7 @@ class TestSyncSeriesFromDataFiles:
] ]
MockApp.return_value = mock_app_instance MockApp.return_value = mock_app_instance
count = await sync_series_from_data_files(str(tmp_path)) count = await sync_legacy_series_to_db(str(tmp_path))
assert count == 1 assert count == 1
mock_create.assert_called_once() mock_create.assert_called_once()
@@ -1382,7 +1382,7 @@ class TestSyncSeriesFromDataFiles:
] ]
MockApp.return_value = mock_app_instance MockApp.return_value = mock_app_instance
count = await sync_series_from_data_files(str(tmp_path)) count = await sync_legacy_series_to_db(str(tmp_path))
assert count == 0 assert count == 0
mock_create.assert_not_called() mock_create.assert_not_called()
@@ -1397,7 +1397,7 @@ class TestSyncSeriesFromDataFiles:
mock_app_instance.get_all_series_from_data_files.return_value = [] mock_app_instance.get_all_series_from_data_files.return_value = []
MockApp.return_value = mock_app_instance MockApp.return_value = mock_app_instance
count = await sync_series_from_data_files(str(tmp_path)) count = await sync_legacy_series_to_db(str(tmp_path))
assert count == 0 assert count == 0
@@ -1436,7 +1436,7 @@ class TestSyncSeriesFromDataFiles:
] ]
MockApp.return_value = mock_app_instance MockApp.return_value = mock_app_instance
count = await sync_series_from_data_files(str(tmp_path)) count = await sync_legacy_series_to_db(str(tmp_path))
assert count == 1 assert count == 1
# The name should have been set to folder # The name should have been set to folder

View File

@@ -160,7 +160,7 @@ class TestSyncAnimeFolders:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_sync_anime_folders_without_progress(self): async def test_sync_anime_folders_without_progress(self):
"""Test syncing anime folders without progress service.""" """Test syncing anime folders without progress service."""
with patch('src.server.services.initialization_service.sync_series_from_data_files', with patch('src.server.services.initialization_service.sync_legacy_series_to_db',
new_callable=AsyncMock, return_value=42) as mock_sync: new_callable=AsyncMock, return_value=42) as mock_sync:
result = await _sync_anime_folders() result = await _sync_anime_folders()
@@ -172,7 +172,7 @@ class TestSyncAnimeFolders:
"""Test syncing anime folders with progress updates.""" """Test syncing anime folders with progress updates."""
mock_progress = AsyncMock() mock_progress = AsyncMock()
with patch('src.server.services.initialization_service.sync_series_from_data_files', with patch('src.server.services.initialization_service.sync_legacy_series_to_db',
new_callable=AsyncMock, return_value=10) as mock_sync: new_callable=AsyncMock, return_value=10) as mock_sync:
result = await _sync_anime_folders(progress_service=mock_progress) result = await _sync_anime_folders(progress_service=mock_progress)