chore: Add deprecation warnings and update documentation (Task 9)
Task 9: Clean up legacy code - Added deprecation warnings to Serie.save_to_file() and load_from_file() - Updated infrastructure.md with Data Storage section documenting: - SQLite database as primary storage - Legacy file storage as deprecated - Data migration process - Added deprecation warning tests for Serie class - Updated existing tests to handle new warnings - All 1012 tests pass (872 unit + 55 API + 85 integration)
This commit is contained in:
parent
73283dea64
commit
396b243d59
@ -17,7 +17,7 @@
|
|||||||
"keep_days": 30
|
"keep_days": 30
|
||||||
},
|
},
|
||||||
"other": {
|
"other": {
|
||||||
"master_password_hash": "$pbkdf2-sha256$29000$JGRMCcHY.9.7d24tJQQAAA$pzRSDd4nRdLzzB/ZhSmFoENCwyn9K43tqvRwq6SNeGA",
|
"master_password_hash": "$pbkdf2-sha256$29000$FmIsBUBIaU2p1Vrr/b83Jg$UgbOlqKmQi4LydrIrcS1fP5jnuEyts/3vb/HUwCQjqg",
|
||||||
"anime_directory": "/mnt/server/serien/Serien/"
|
"anime_directory": "/mnt/server/serien/Serien/"
|
||||||
},
|
},
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
|
|||||||
@ -1,6 +1,488 @@
|
|||||||
{
|
{
|
||||||
"pending": [],
|
"pending": [
|
||||||
|
{
|
||||||
|
"id": "04732603-bad5-459a-a933-284c8fd6f82e",
|
||||||
|
"serie_id": "test-series-2",
|
||||||
|
"serie_folder": "Another Series (2024)",
|
||||||
|
"serie_name": "Another Series",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "HIGH",
|
||||||
|
"added_at": "2025-12-01T18:54:55.016640Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "0c8f9952-3ce7-4933-ab0c-d460f215118b",
|
||||||
|
"serie_id": "series-high",
|
||||||
|
"serie_folder": "Series High (2024)",
|
||||||
|
"serie_name": "Series High",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "HIGH",
|
||||||
|
"added_at": "2025-12-01T18:54:55.048838Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c327873a-7a79-432b-a6c6-ebd23f147989",
|
||||||
|
"serie_id": "series-normal",
|
||||||
|
"serie_folder": "Series Normal (2024)",
|
||||||
|
"serie_name": "Series Normal",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:54:55.051772Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "71dbcc0c-9713-4f15-865d-cf9d87bc45e2",
|
||||||
|
"serie_id": "series-low",
|
||||||
|
"serie_folder": "Series Low (2024)",
|
||||||
|
"serie_name": "Series Low",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "LOW",
|
||||||
|
"added_at": "2025-12-01T18:54:55.053938Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "faa33e0b-0b7a-4e40-89e0-498695d2bbda",
|
||||||
|
"serie_id": "test-series",
|
||||||
|
"serie_folder": "Test Series (2024)",
|
||||||
|
"serie_name": "Test Series",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:54:55.224152Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2677795e-e9e0-4465-a781-d30ffc5c7e9b",
|
||||||
|
"serie_id": "test-series",
|
||||||
|
"serie_folder": "Test Series (2024)",
|
||||||
|
"serie_name": "Test Series",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:54:55.284539Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fc834fab-591b-41ba-a525-a738d79c4595",
|
||||||
|
"serie_id": "invalid-series",
|
||||||
|
"serie_folder": "Invalid Series (2024)",
|
||||||
|
"serie_name": "Invalid Series",
|
||||||
|
"episode": {
|
||||||
|
"season": 99,
|
||||||
|
"episode": 99,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:54:55.347386Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c9fee1c5-0f48-4fa2-896e-82495f62c55a",
|
||||||
|
"serie_id": "test-series",
|
||||||
|
"serie_folder": "Test Series (2024)",
|
||||||
|
"serie_name": "Test Series",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:54:55.378258Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "642ef6f4-457d-4c4c-9dd9-2a008bac0f6d",
|
||||||
|
"serie_id": "series-3",
|
||||||
|
"serie_folder": "Series 3 (2024)",
|
||||||
|
"serie_name": "Series 3",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:54:55.455273Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "79ff4b4c-9887-4c9e-b56a-65b3f909c5cd",
|
||||||
|
"serie_id": "series-1",
|
||||||
|
"serie_folder": "Series 1 (2024)",
|
||||||
|
"serie_name": "Series 1",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:54:55.457074Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "48229ca6-4ed5-4a4f-a8bc-8af3abb341dd",
|
||||||
|
"serie_id": "series-0",
|
||||||
|
"serie_folder": "Series 0 (2024)",
|
||||||
|
"serie_name": "Series 0",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:54:55.457770Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "eccce67c-9522-4ada-9d20-a60c5ac6dae0",
|
||||||
|
"serie_id": "series-4",
|
||||||
|
"serie_folder": "Series 4 (2024)",
|
||||||
|
"serie_name": "Series 4",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:54:55.458468Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "48c60671-9bad-4eca-bc71-30f20df79946",
|
||||||
|
"serie_id": "series-2",
|
||||||
|
"serie_folder": "Series 2 (2024)",
|
||||||
|
"serie_name": "Series 2",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:54:55.459109Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6449c9aa-9548-4196-af0e-36e348bf7613",
|
||||||
|
"serie_id": "persistent-series",
|
||||||
|
"serie_folder": "Persistent Series (2024)",
|
||||||
|
"serie_name": "Persistent Series",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:54:55.533666Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "90944690-3005-4ae4-949a-fb45e8f4220e",
|
||||||
|
"serie_id": "ws-series",
|
||||||
|
"serie_folder": "WebSocket Series (2024)",
|
||||||
|
"serie_name": "WebSocket Series",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:54:55.594463Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "d241f6aa-c419-47fe-817c-33f340f67c9b",
|
||||||
|
"serie_id": "workflow-series",
|
||||||
|
"serie_folder": "Workflow Test Series (2024)",
|
||||||
|
"serie_name": "Workflow Test Series",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "HIGH",
|
||||||
|
"added_at": "2025-12-01T18:54:55.625259Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5a75dfe8-fcd4-4a4d-896f-a9216f332436",
|
||||||
|
"serie_id": "attack-on-titan",
|
||||||
|
"serie_folder": "Attack on Titan (2013)",
|
||||||
|
"serie_name": "Attack on Titan",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:55:01.172047Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "23f51f6a-35fc-49ca-9e51-01d7d831244b",
|
||||||
|
"serie_id": "one-piece-0efa2f3c",
|
||||||
|
"serie_folder": "One Piece (0efa2f3c)",
|
||||||
|
"serie_name": "One Piece",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:55:01.203694Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6c68a9ae-0808-490f-86d6-f7c0160c1695",
|
||||||
|
"serie_id": "naruto-original-4b0feeea",
|
||||||
|
"serie_folder": "Naruto Series (4b0feeea)",
|
||||||
|
"serie_name": "Naruto",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:55:01.236658Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6bb0feff-bba9-487c-9214-fe9e5332c59b",
|
||||||
|
"serie_id": "naruto-shippuden-4b0feeea",
|
||||||
|
"serie_folder": "Naruto Series (4b0feeea)",
|
||||||
|
"serie_name": "Naruto Shippuden",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:55:01.239999Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1415c34e-6b2d-4cba-92ab-ee5695e421ab",
|
||||||
|
"serie_id": "valid-key-format-d5b283fe",
|
||||||
|
"serie_folder": "Valid Key (d5b283fe)",
|
||||||
|
"serie_name": "Valid Key",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:55:01.313502Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9a104e48-37d4-48c7-b396-7b6262545dd2",
|
||||||
|
"serie_id": "bleach-tybw-2c2d6cf4",
|
||||||
|
"serie_folder": "Bleach: TYBW (2c2d6cf4)",
|
||||||
|
"serie_name": "Bleach: TYBW",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": null
|
||||||
|
},
|
||||||
|
"status": "pending",
|
||||||
|
"priority": "HIGH",
|
||||||
|
"added_at": "2025-12-01T18:55:01.359222Z",
|
||||||
|
"started_at": null,
|
||||||
|
"completed_at": null,
|
||||||
|
"progress": null,
|
||||||
|
"error": null,
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
}
|
||||||
|
],
|
||||||
"active": [],
|
"active": [],
|
||||||
"failed": [],
|
"failed": [
|
||||||
"timestamp": "2025-12-01T18:47:07.269087+00:00"
|
{
|
||||||
|
"id": "77e2ce7f-7d22-4aca-aa40-43a7e397817a",
|
||||||
|
"serie_id": "test-series-1",
|
||||||
|
"serie_folder": "Test Anime Series (2024)",
|
||||||
|
"serie_name": "Test Anime Series",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 1,
|
||||||
|
"title": "Episode 1"
|
||||||
|
},
|
||||||
|
"status": "failed",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:54:54.984148Z",
|
||||||
|
"started_at": "2025-12-01T18:54:55.144007Z",
|
||||||
|
"completed_at": "2025-12-01T18:54:55.163314Z",
|
||||||
|
"progress": null,
|
||||||
|
"error": "Download failed",
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e7b0f2fb-718b-40f2-b3d0-6ec8607d7bff",
|
||||||
|
"serie_id": "test-series-1",
|
||||||
|
"serie_folder": "Test Anime Series (2024)",
|
||||||
|
"serie_name": "Test Anime Series",
|
||||||
|
"episode": {
|
||||||
|
"season": 1,
|
||||||
|
"episode": 2,
|
||||||
|
"title": "Episode 2"
|
||||||
|
},
|
||||||
|
"status": "failed",
|
||||||
|
"priority": "NORMAL",
|
||||||
|
"added_at": "2025-12-01T18:54:54.984200Z",
|
||||||
|
"started_at": "2025-12-01T18:54:55.629626Z",
|
||||||
|
"completed_at": "2025-12-01T18:54:55.646498Z",
|
||||||
|
"progress": null,
|
||||||
|
"error": "Download failed",
|
||||||
|
"retry_count": 0,
|
||||||
|
"source_url": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": "2025-12-01T18:55:01.359448+00:00"
|
||||||
}
|
}
|
||||||
@ -164,6 +164,53 @@ All series-related WebSocket events include `key` as the primary identifier in t
|
|||||||
- `AnimeSeriesService.get_by_id(id)` - Internal lookup by database ID
|
- `AnimeSeriesService.get_by_id(id)` - Internal lookup by database ID
|
||||||
- No `get_by_folder()` method exists - folder is never used for lookups
|
- No `get_by_folder()` method exists - folder is never used for lookups
|
||||||
|
|
||||||
|
## Data Storage
|
||||||
|
|
||||||
|
### Storage Architecture
|
||||||
|
|
||||||
|
The application uses **SQLite database** as the primary storage for anime series metadata. This replaces the legacy file-based storage system.
|
||||||
|
|
||||||
|
| Storage Method | Status | Location | Purpose |
|
||||||
|
| -------------- | --------------------- | ------------------------- | ------------------------------ |
|
||||||
|
| SQLite DB | **Primary (Current)** | `data/aniworld.db` | All series metadata and state |
|
||||||
|
| Data Files | **Deprecated** | `{anime_dir}/*/data` | Legacy per-series JSON files |
|
||||||
|
|
||||||
|
### Database Storage (Recommended)
|
||||||
|
|
||||||
|
All new series are stored in the SQLite database via `AnimeSeriesService`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Add series to database
|
||||||
|
await AnimeSeriesService.create(db_session, series_data)
|
||||||
|
|
||||||
|
# Query series by key
|
||||||
|
series = await AnimeSeriesService.get_by_key(db_session, "attack-on-titan")
|
||||||
|
|
||||||
|
# Update series
|
||||||
|
await AnimeSeriesService.update(db_session, series_id, update_data)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Legacy File Storage (Deprecated)
|
||||||
|
|
||||||
|
The legacy file-based storage is **deprecated** and will be removed in v3.0.0:
|
||||||
|
|
||||||
|
- `Serie.save_to_file()` - Deprecated, use `AnimeSeriesService.create()`
|
||||||
|
- `Serie.load_from_file()` - Deprecated, use `AnimeSeriesService.get_by_key()`
|
||||||
|
- `SerieList.add()` - Deprecated, use `SerieList.add_to_db()`
|
||||||
|
|
||||||
|
Deprecation warnings are raised when using these methods.
|
||||||
|
|
||||||
|
### Data Migration
|
||||||
|
|
||||||
|
On application startup, the system automatically migrates legacy data files to the database:
|
||||||
|
|
||||||
|
1. **Scan**: `DataMigrationService.scan_for_data_files()` finds legacy `data` files
|
||||||
|
2. **Migrate**: `DataMigrationService.migrate_data_file()` imports each file to DB
|
||||||
|
3. **Skip**: Existing series (by key) are skipped; changed episode data is updated
|
||||||
|
4. **Log**: Migration results are logged at startup
|
||||||
|
|
||||||
|
Migration is idempotent and safe to run multiple times.
|
||||||
|
|
||||||
## Core Services
|
## Core Services
|
||||||
|
|
||||||
### SeriesApp (`src/core/SeriesApp.py`)
|
### SeriesApp (`src/core/SeriesApp.py`)
|
||||||
|
|||||||
@ -328,7 +328,7 @@ async def lifespan(app: FastAPI):
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Task 8: Write Integration Tests ⬜
|
### Task 8: Write Integration Tests ✅
|
||||||
|
|
||||||
**File:** `tests/integration/test_data_file_migration.py`
|
**File:** `tests/integration/test_data_file_migration.py`
|
||||||
|
|
||||||
@ -336,13 +336,13 @@ async def lifespan(app: FastAPI):
|
|||||||
|
|
||||||
**Test Cases:**
|
**Test Cases:**
|
||||||
|
|
||||||
1. `test_migration_on_fresh_start` - No data files, no database entries
|
1. `test_migration_on_fresh_start` ✅ - No data files, no database entries
|
||||||
2. `test_migration_with_existing_data_files` - Data files exist, migrate to DB
|
2. `test_migration_with_existing_data_files` ✅ - Data files exist, migrate to DB
|
||||||
3. `test_migration_skips_existing_db_entries` - Series already in DB, skip migration
|
3. `test_migration_skips_existing_db_entries` ✅ - Series already in DB, skip migration
|
||||||
4. `test_add_series_saves_to_database` - New series via API saves to DB
|
4. `test_add_series_saves_to_database` ✅ - New series via API saves to DB
|
||||||
5. `test_scan_saves_to_database` - Scan results save to DB
|
5. `test_scan_saves_to_database` ✅ - Scan results save to DB
|
||||||
6. `test_list_reads_from_database` - Series list reads from DB
|
6. `test_list_reads_from_database` ✅ - Series list reads from DB
|
||||||
7. `test_search_and_add_workflow` - Search -> Add -> Verify in DB
|
7. `test_search_and_add_workflow` ✅ - Search -> Add -> Verify in DB
|
||||||
|
|
||||||
**Setup:**
|
**Setup:**
|
||||||
|
|
||||||
@ -350,6 +350,11 @@ async def lifespan(app: FastAPI):
|
|||||||
- Use test database (in-memory SQLite)
|
- Use test database (in-memory SQLite)
|
||||||
- Create sample data files for migration tests
|
- Create sample data files for migration tests
|
||||||
|
|
||||||
|
**Implementation Notes:**
|
||||||
|
- Added 5 new integration tests to cover all required test cases
|
||||||
|
- All 11 migration integration tests pass
|
||||||
|
- All 870 tests pass (815 unit + 55 API)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Task 9: Clean Up Legacy Code ⬜
|
### Task 9: Clean Up Legacy Code ⬜
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import json
|
import json
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
|
||||||
class Serie:
|
class Serie:
|
||||||
@ -154,13 +155,46 @@ class Serie:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def save_to_file(self, filename: str):
|
def save_to_file(self, filename: str):
|
||||||
"""Save Serie object to JSON file."""
|
"""Save Serie object to JSON file.
|
||||||
|
|
||||||
|
.. deprecated::
|
||||||
|
File-based storage is deprecated. Use database storage via
|
||||||
|
`AnimeSeriesService.create()` instead. This method will be
|
||||||
|
removed in v3.0.0.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filename: Path to save the JSON file
|
||||||
|
"""
|
||||||
|
warnings.warn(
|
||||||
|
"save_to_file() is deprecated and will be removed in v3.0.0. "
|
||||||
|
"Use database storage via AnimeSeriesService.create() instead.",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2
|
||||||
|
)
|
||||||
with open(filename, "w", encoding="utf-8") as file:
|
with open(filename, "w", encoding="utf-8") as file:
|
||||||
json.dump(self.to_dict(), file, indent=4)
|
json.dump(self.to_dict(), file, indent=4)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_from_file(cls, filename: str) -> "Serie":
|
def load_from_file(cls, filename: str) -> "Serie":
|
||||||
"""Load Serie object from JSON file."""
|
"""Load Serie object from JSON file.
|
||||||
|
|
||||||
|
.. deprecated::
|
||||||
|
File-based storage is deprecated. Use database storage via
|
||||||
|
`AnimeSeriesService.get_by_key()` instead. This method will be
|
||||||
|
removed in v3.0.0.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filename: Path to load the JSON file from
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Serie: The loaded Serie object
|
||||||
|
"""
|
||||||
|
warnings.warn(
|
||||||
|
"load_from_file() is deprecated and will be removed in v3.0.0. "
|
||||||
|
"Use database storage via AnimeSeriesService instead.",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2
|
||||||
|
)
|
||||||
with open(filename, "r", encoding="utf-8") as file:
|
with open(filename, "r", encoding="utf-8") as file:
|
||||||
data = json.load(file)
|
data = json.load(file)
|
||||||
return cls.from_dict(data)
|
return cls.from_dict(data)
|
||||||
|
|||||||
@ -173,6 +173,8 @@ class TestSerieProperties:
|
|||||||
|
|
||||||
def test_serie_save_and_load_from_file(self):
|
def test_serie_save_and_load_from_file(self):
|
||||||
"""Test saving and loading Serie from file."""
|
"""Test saving and loading Serie from file."""
|
||||||
|
import warnings
|
||||||
|
|
||||||
serie = Serie(
|
serie = Serie(
|
||||||
key="test-key",
|
key="test-key",
|
||||||
name="Test Series",
|
name="Test Series",
|
||||||
@ -190,6 +192,10 @@ class TestSerieProperties:
|
|||||||
temp_filename = f.name
|
temp_filename = f.name
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Suppress deprecation warnings for this test
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore", DeprecationWarning)
|
||||||
|
|
||||||
# Save to file
|
# Save to file
|
||||||
serie.save_to_file(temp_filename)
|
serie.save_to_file(temp_filename)
|
||||||
|
|
||||||
@ -242,3 +248,75 @@ class TestSerieDocumentation:
|
|||||||
assert Serie.folder.fget.__doc__ is not None
|
assert Serie.folder.fget.__doc__ is not None
|
||||||
assert "metadata" in Serie.folder.fget.__doc__.lower()
|
assert "metadata" in Serie.folder.fget.__doc__.lower()
|
||||||
assert "not used for lookups" in Serie.folder.fget.__doc__.lower()
|
assert "not used for lookups" in Serie.folder.fget.__doc__.lower()
|
||||||
|
|
||||||
|
|
||||||
|
class TestSerieDeprecationWarnings:
|
||||||
|
"""Test deprecation warnings for file-based methods."""
|
||||||
|
|
||||||
|
def test_save_to_file_raises_deprecation_warning(self):
|
||||||
|
"""Test save_to_file() raises deprecation warning."""
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
serie = Serie(
|
||||||
|
key="test-key",
|
||||||
|
name="Test Series",
|
||||||
|
site="https://example.com",
|
||||||
|
folder="Test Folder",
|
||||||
|
episodeDict={1: [1, 2, 3]}
|
||||||
|
)
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
mode='w', suffix='.json', delete=False
|
||||||
|
) as temp_file:
|
||||||
|
temp_filename = temp_file.name
|
||||||
|
|
||||||
|
try:
|
||||||
|
with warnings.catch_warnings(record=True) as w:
|
||||||
|
warnings.simplefilter("always")
|
||||||
|
serie.save_to_file(temp_filename)
|
||||||
|
|
||||||
|
# Check deprecation warning was raised
|
||||||
|
assert len(w) == 1
|
||||||
|
assert issubclass(w[0].category, DeprecationWarning)
|
||||||
|
assert "deprecated" in str(w[0].message).lower()
|
||||||
|
assert "save_to_file" in str(w[0].message)
|
||||||
|
finally:
|
||||||
|
if os.path.exists(temp_filename):
|
||||||
|
os.remove(temp_filename)
|
||||||
|
|
||||||
|
def test_load_from_file_raises_deprecation_warning(self):
|
||||||
|
"""Test load_from_file() raises deprecation warning."""
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
serie = Serie(
|
||||||
|
key="test-key",
|
||||||
|
name="Test Series",
|
||||||
|
site="https://example.com",
|
||||||
|
folder="Test Folder",
|
||||||
|
episodeDict={1: [1, 2, 3]}
|
||||||
|
)
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
mode='w', suffix='.json', delete=False
|
||||||
|
) as temp_file:
|
||||||
|
temp_filename = temp_file.name
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Save first (suppress warning for this)
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
serie.save_to_file(temp_filename)
|
||||||
|
|
||||||
|
# Now test loading
|
||||||
|
with warnings.catch_warnings(record=True) as w:
|
||||||
|
warnings.simplefilter("always")
|
||||||
|
Serie.load_from_file(temp_filename)
|
||||||
|
|
||||||
|
# Check deprecation warning was raised
|
||||||
|
assert len(w) == 1
|
||||||
|
assert issubclass(w[0].category, DeprecationWarning)
|
||||||
|
assert "deprecated" in str(w[0].message).lower()
|
||||||
|
assert "load_from_file" in str(w[0].message)
|
||||||
|
finally:
|
||||||
|
if os.path.exists(temp_filename):
|
||||||
|
os.remove(temp_filename)
|
||||||
|
|||||||
@ -473,10 +473,19 @@ class TestSerieListDeprecationWarnings:
|
|||||||
warnings.simplefilter("always")
|
warnings.simplefilter("always")
|
||||||
serie_list.add(sample_serie)
|
serie_list.add(sample_serie)
|
||||||
|
|
||||||
# Check deprecation warning was raised
|
# Check at least one deprecation warning was raised for add()
|
||||||
assert len(w) == 1
|
# (Note: save_to_file also raises a warning, so we may get 2)
|
||||||
assert issubclass(w[0].category, DeprecationWarning)
|
deprecation_warnings = [
|
||||||
assert "add_to_db()" in str(w[0].message)
|
warning for warning in w
|
||||||
|
if issubclass(warning.category, DeprecationWarning)
|
||||||
|
]
|
||||||
|
assert len(deprecation_warnings) >= 1
|
||||||
|
# Check that one of them is from add()
|
||||||
|
add_warnings = [
|
||||||
|
warning for warning in deprecation_warnings
|
||||||
|
if "add_to_db()" in str(warning.message)
|
||||||
|
]
|
||||||
|
assert len(add_warnings) == 1
|
||||||
|
|
||||||
def test_get_by_folder_raises_deprecation_warning(
|
def test_get_by_folder_raises_deprecation_warning(
|
||||||
self, temp_directory, sample_serie
|
self, temp_directory, sample_serie
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user