Phase 8: Documentation and deprecation warnings for identifier standardization
- Enhanced infrastructure.md with identifier convention table, format requirements, migration notes - Updated docs/README.md with series identifier convention section - Updated docs/api_reference.md with key-based API examples and notes - Added deprecation warnings to SerieList.get_by_folder() - Added deprecation warnings to anime.py folder fallback lookup - Added deprecation warnings to validate_series_key_or_folder() - All warnings include v3.0.0 removal timeline - All 1006 tests pass
This commit is contained in:
parent
ddff43595f
commit
85a6b053eb
@ -97,6 +97,42 @@ Production deployment instructions covering:
|
|||||||
- Status notifications
|
- Status notifications
|
||||||
- Error alerts
|
- Error alerts
|
||||||
|
|
||||||
|
## Series Identifier Convention
|
||||||
|
|
||||||
|
### Understanding Series Identifiers
|
||||||
|
|
||||||
|
The application uses two identifiers for anime series:
|
||||||
|
|
||||||
|
| Identifier | Purpose | Example | Used For |
|
||||||
|
| ---------- | ------------------------ | -------------------------- | ----------------- |
|
||||||
|
| `key` | **Primary identifier** | `"attack-on-titan"` | All API lookups |
|
||||||
|
| `folder` | Filesystem metadata only | `"Attack on Titan (2013)"` | Display purposes |
|
||||||
|
|
||||||
|
### Key Format
|
||||||
|
|
||||||
|
- Lowercase letters and numbers only
|
||||||
|
- Words separated by hyphens (`-`)
|
||||||
|
- No spaces, underscores, or uppercase letters
|
||||||
|
- Examples: `"one-piece"`, `"86-eighty-six"`, `"re-zero"`
|
||||||
|
|
||||||
|
### Usage Guidelines
|
||||||
|
|
||||||
|
```python
|
||||||
|
# ✅ Correct: Use 'key' for API operations
|
||||||
|
GET /api/anime/attack-on-titan
|
||||||
|
POST /api/queue/add {"serie_id": "attack-on-titan", ...}
|
||||||
|
|
||||||
|
# ❌ Deprecated: Folder-based lookups (backward compatibility only)
|
||||||
|
GET /api/anime/Attack%20on%20Titan%20(2013) # Will work but deprecated
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backward Compatibility
|
||||||
|
|
||||||
|
For existing integrations, folder-based lookups are still supported but deprecated:
|
||||||
|
- API endpoints check `key` first, then fall back to `folder`
|
||||||
|
- New code should always use `key` as the identifier
|
||||||
|
- Deprecation warnings will be added in future versions
|
||||||
|
|
||||||
## Documentation Examples
|
## Documentation Examples
|
||||||
|
|
||||||
### API Usage Example
|
### API Usage Example
|
||||||
@ -285,8 +321,8 @@ docs/
|
|||||||
|
|
||||||
## Document Info
|
## Document Info
|
||||||
|
|
||||||
- **Last Updated**: October 22, 2025
|
- **Last Updated**: November 28, 2025
|
||||||
- **Version**: 1.0.0
|
- **Version**: 2.0.0
|
||||||
- **Status**: Production Ready
|
- **Status**: Production Ready
|
||||||
- **Maintainers**: Development Team
|
- **Maintainers**: Development Team
|
||||||
|
|
||||||
|
|||||||
@ -421,12 +421,16 @@ Authorization: Bearer <token>
|
|||||||
|
|
||||||
### Anime Endpoints
|
### Anime Endpoints
|
||||||
|
|
||||||
|
> **Note on Identifiers**: All anime endpoints use `key` as the primary series identifier (e.g., `"attack-on-titan"`).
|
||||||
|
> The `folder` field is metadata only and should not be used for lookups.
|
||||||
|
> For backward compatibility, folder-based lookups are supported but deprecated.
|
||||||
|
|
||||||
#### List Anime with Missing Episodes
|
#### List Anime with Missing Episodes
|
||||||
|
|
||||||
Lists all anime series with missing episodes.
|
Lists all anime series with missing episodes.
|
||||||
|
|
||||||
```http
|
```http
|
||||||
GET /api/v1/anime
|
GET /api/anime
|
||||||
Authorization: Bearer <token>
|
Authorization: Bearer <token>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -442,14 +446,18 @@ Authorization: Bearer <token>
|
|||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id": "aniworld_123",
|
"key": "attack-on-titan",
|
||||||
"title": "Attack on Titan",
|
"name": "Attack on Titan",
|
||||||
"missing_episodes": 5
|
"folder": "Attack on Titan (2013)",
|
||||||
|
"missing_episodes": 5,
|
||||||
|
"link": "https://aniworld.to/anime/stream/attack-on-titan"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "aniworld_456",
|
"key": "demon-slayer",
|
||||||
"title": "Demon Slayer",
|
"name": "Demon Slayer",
|
||||||
"missing_episodes": 2
|
"folder": "Demon Slayer (2019)",
|
||||||
|
"missing_episodes": 2,
|
||||||
|
"link": "https://aniworld.to/anime/stream/demon-slayer"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
@ -461,20 +469,23 @@ Authorization: Bearer <token>
|
|||||||
Retrieves detailed information for a specific anime series.
|
Retrieves detailed information for a specific anime series.
|
||||||
|
|
||||||
```http
|
```http
|
||||||
GET /api/v1/anime/{anime_id}
|
GET /api/anime/{anime_id}
|
||||||
Authorization: Bearer <token>
|
Authorization: Bearer <token>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Path Parameters**:
|
||||||
|
|
||||||
|
- `anime_id` (string): Series `key` (preferred) or `folder` (deprecated)
|
||||||
|
|
||||||
**Response (200 OK)**:
|
**Response (200 OK)**:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"id": "aniworld_123",
|
"key": "attack-on-titan",
|
||||||
"title": "Attack on Titan",
|
"title": "Attack on Titan",
|
||||||
"episodes": ["Season 1 Episode 1", "Season 1 Episode 2"],
|
"folder": "Attack on Titan (2013)",
|
||||||
"description": "Anime description...",
|
"episodes": ["S01-E01", "S01-E02", "S02-E01"],
|
||||||
"total_episodes": 100,
|
"description": "Anime description..."
|
||||||
"downloaded_episodes": 95
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -489,7 +500,7 @@ Authorization: Bearer <token>
|
|||||||
Rescans the local anime directory for new series and episodes.
|
Rescans the local anime directory for new series and episodes.
|
||||||
|
|
||||||
```http
|
```http
|
||||||
POST /api/v1/anime/rescan
|
POST /api/anime/rescan
|
||||||
Authorization: Bearer <token>
|
Authorization: Bearer <token>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -539,6 +550,9 @@ Authorization: Bearer <token>
|
|||||||
|
|
||||||
### Download Queue Endpoints
|
### Download Queue Endpoints
|
||||||
|
|
||||||
|
> **Note on Identifiers**: Download queue operations use `serie_id` which should be the series `key` (e.g., `"attack-on-titan"`).
|
||||||
|
> The `serie_folder` field is filesystem metadata and should not be used for identification.
|
||||||
|
|
||||||
#### Get Queue Status
|
#### Get Queue Status
|
||||||
|
|
||||||
Retrieves download queue status and statistics.
|
Retrieves download queue status and statistics.
|
||||||
@ -577,13 +591,21 @@ Authorization: Bearer <token>
|
|||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"anime_id": "aniworld_123",
|
"serie_id": "attack-on-titan",
|
||||||
|
"serie_folder": "Attack on Titan (2013)",
|
||||||
|
"serie_name": "Attack on Titan",
|
||||||
"episodes": ["S01E01", "S01E02"],
|
"episodes": ["S01E01", "S01E02"],
|
||||||
"priority": "normal"
|
"priority": 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Priority Values**: `low`, `normal`, `high`
|
**Request Fields**:
|
||||||
|
|
||||||
|
- `serie_id` (string, required): Series `key` - primary identifier
|
||||||
|
- `serie_folder` (string, optional): Filesystem folder name (metadata)
|
||||||
|
- `serie_name` (string, required): Display name
|
||||||
|
- `episodes` (array, required): List of episodes to download
|
||||||
|
- `priority` (integer, optional): Priority level (default: 0)
|
||||||
|
|
||||||
**Response (201 Created)**:
|
**Response (201 Created)**:
|
||||||
|
|
||||||
@ -592,7 +614,7 @@ Content-Type: application/json
|
|||||||
"success": true,
|
"success": true,
|
||||||
"data": {
|
"data": {
|
||||||
"queue_item_id": "queue_456",
|
"queue_item_id": "queue_456",
|
||||||
"anime_id": "aniworld_123",
|
"serie_id": "attack-on-titan",
|
||||||
"status": "pending"
|
"status": "pending"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -682,18 +704,21 @@ Authorization: Bearer <token>
|
|||||||
|
|
||||||
### WebSocket Endpoints
|
### WebSocket Endpoints
|
||||||
|
|
||||||
|
> **Note on Identifiers**: All WebSocket events include `key` as the primary series identifier.
|
||||||
|
> The `folder` field is included as metadata but should not be used for identification.
|
||||||
|
|
||||||
#### Real-Time Progress Updates
|
#### Real-Time Progress Updates
|
||||||
|
|
||||||
Establishes WebSocket connection for real-time download progress updates.
|
Establishes WebSocket connection for real-time download progress updates.
|
||||||
|
|
||||||
```
|
```
|
||||||
WS /ws/downloads
|
WS /ws/connect
|
||||||
```
|
```
|
||||||
|
|
||||||
**Connection**:
|
**Connection**:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const ws = new WebSocket("ws://localhost:8000/ws/downloads");
|
const ws = new WebSocket("ws://localhost:8000/ws/connect");
|
||||||
|
|
||||||
ws.onmessage = (event) => {
|
ws.onmessage = (event) => {
|
||||||
const message = JSON.parse(event.data);
|
const message = JSON.parse(event.data);
|
||||||
@ -701,6 +726,8 @@ ws.onmessage = (event) => {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Rooms**: `downloads`, `download_progress`, `scan_progress`
|
||||||
|
|
||||||
**Message Types**:
|
**Message Types**:
|
||||||
|
|
||||||
**Download Started**:
|
**Download Started**:
|
||||||
@ -710,8 +737,9 @@ ws.onmessage = (event) => {
|
|||||||
"type": "download_started",
|
"type": "download_started",
|
||||||
"timestamp": "2025-10-22T12:00:00Z",
|
"timestamp": "2025-10-22T12:00:00Z",
|
||||||
"data": {
|
"data": {
|
||||||
"queue_item_id": "queue_456",
|
"download_id": "dl_456",
|
||||||
"anime_title": "Attack on Titan",
|
"key": "attack-on-titan",
|
||||||
|
"folder": "Attack on Titan (2013)",
|
||||||
"episode": "S01E01"
|
"episode": "S01E01"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -724,11 +752,12 @@ ws.onmessage = (event) => {
|
|||||||
"type": "download_progress",
|
"type": "download_progress",
|
||||||
"timestamp": "2025-10-22T12:00:05Z",
|
"timestamp": "2025-10-22T12:00:05Z",
|
||||||
"data": {
|
"data": {
|
||||||
"queue_item_id": "queue_456",
|
"download_id": "dl_456",
|
||||||
"progress_percent": 45,
|
"key": "attack-on-titan",
|
||||||
"downloaded_bytes": 500000000,
|
"folder": "Attack on Titan (2013)",
|
||||||
"total_bytes": 1100000000,
|
"percent": 45.2,
|
||||||
"speed_mbps": 5.5
|
"speed_mbps": 5.5,
|
||||||
|
"eta_seconds": 180
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -737,11 +766,12 @@ ws.onmessage = (event) => {
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "download_completed",
|
"type": "download_complete",
|
||||||
"timestamp": "2025-10-22T12:05:00Z",
|
"timestamp": "2025-10-22T12:05:00Z",
|
||||||
"data": {
|
"data": {
|
||||||
"queue_item_id": "queue_456",
|
"download_id": "dl_456",
|
||||||
"total_time_seconds": 300,
|
"key": "attack-on-titan",
|
||||||
|
"folder": "Attack on Titan (2013)",
|
||||||
"file_path": "/path/to/anime/file.mkv"
|
"file_path": "/path/to/anime/file.mkv"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -751,15 +781,17 @@ ws.onmessage = (event) => {
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "download_error",
|
"type": "download_failed",
|
||||||
"timestamp": "2025-10-22T12:05:00Z",
|
"timestamp": "2025-10-22T12:05:00Z",
|
||||||
"data": {
|
"data": {
|
||||||
"queue_item_id": "queue_456",
|
"download_id": "dl_456",
|
||||||
"error_message": "Connection timeout",
|
"key": "attack-on-titan",
|
||||||
"error_code": "PROVIDER_ERROR"
|
"folder": "Attack on Titan (2013)",
|
||||||
|
"error": "Connection timeout"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -41,12 +41,30 @@ tests/ # Test suites
|
|||||||
|
|
||||||
## Series Identifier Convention
|
## Series Identifier Convention
|
||||||
|
|
||||||
Throughout the codebase, two identifiers are used for anime series:
|
Throughout the codebase, three identifiers are used for anime series:
|
||||||
|
|
||||||
- **`key`**: Primary identifier (provider-assigned, URL-safe, e.g., `"attack-on-titan"`)
|
| Identifier | Type | Purpose | Example |
|
||||||
- **`folder`**: Display/filesystem metadata only (e.g., `"Attack on Titan (2013)"`)
|
| ---------- | --------------- | --------------------------------------------------------- | ----------------------------- |
|
||||||
|
| `key` | Unique, Indexed | **PRIMARY** - All lookups, API operations, WebSocket events | `"attack-on-titan"` |
|
||||||
|
| `folder` | String | Display/filesystem metadata only (never for lookups) | `"Attack on Titan (2013)"` |
|
||||||
|
| `id` | Primary Key | Internal database key for relationships | `1`, `42` |
|
||||||
|
|
||||||
All lookups, events, and API operations use `key`. The `folder` is metadata for display purposes.
|
### Key Format Requirements
|
||||||
|
|
||||||
|
- **Lowercase only**: No uppercase letters allowed
|
||||||
|
- **URL-safe**: Only alphanumeric characters and hyphens
|
||||||
|
- **Hyphen-separated**: Words separated by single hyphens
|
||||||
|
- **No leading/trailing hyphens**: Must start and end with alphanumeric
|
||||||
|
- **No consecutive hyphens**: `attack--titan` is invalid
|
||||||
|
|
||||||
|
**Valid examples**: `"attack-on-titan"`, `"one-piece"`, `"86-eighty-six"`, `"re-zero"`
|
||||||
|
**Invalid examples**: `"Attack On Titan"`, `"attack_on_titan"`, `"attack on titan"`
|
||||||
|
|
||||||
|
### Migration Notes
|
||||||
|
|
||||||
|
- **Backward Compatibility**: API endpoints accepting `anime_id` will check `key` first, then fall back to `folder` lookup
|
||||||
|
- **Deprecation**: Folder-based lookups are deprecated and will be removed in a future version
|
||||||
|
- **New Code**: Always use `key` for identification; `folder` is metadata only
|
||||||
|
|
||||||
## API Endpoints
|
## API Endpoints
|
||||||
|
|
||||||
@ -282,3 +300,38 @@ conda run -n AniWorld python -m pytest tests/api/ -v
|
|||||||
- Move WebSocket registry to Redis
|
- Move WebSocket registry to Redis
|
||||||
- Use distributed locking for queue operations
|
- Use distributed locking for queue operations
|
||||||
- Consider Redis for session/cache storage
|
- Consider Redis for session/cache storage
|
||||||
|
|
||||||
|
## Code Examples
|
||||||
|
|
||||||
|
### API Usage with Key Identifier
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Fetching anime list - response includes 'key' as identifier
|
||||||
|
response = requests.get("/api/anime", headers={"Authorization": f"Bearer {token}"})
|
||||||
|
anime_list = response.json()
|
||||||
|
# Each item has: key="attack-on-titan", folder="Attack on Titan (2013)", ...
|
||||||
|
|
||||||
|
# Fetching specific anime by key (preferred)
|
||||||
|
response = requests.get("/api/anime/attack-on-titan", headers={"Authorization": f"Bearer {token}"})
|
||||||
|
|
||||||
|
# Adding to download queue using key
|
||||||
|
download_request = {
|
||||||
|
"serie_id": "attack-on-titan", # Use key, not folder
|
||||||
|
"serie_folder": "Attack on Titan (2013)", # Metadata for filesystem
|
||||||
|
"serie_name": "Attack on Titan",
|
||||||
|
"episodes": ["S01E01", "S01E02"],
|
||||||
|
"priority": 1
|
||||||
|
}
|
||||||
|
response = requests.post("/api/queue/add", json=download_request, headers=headers)
|
||||||
|
```
|
||||||
|
|
||||||
|
### WebSocket Event Handling
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// WebSocket events always include 'key' as identifier
|
||||||
|
socket.on("download_progress", (data) => {
|
||||||
|
const key = data.key; // Primary identifier: "attack-on-titan"
|
||||||
|
const folder = data.folder; // Metadata: "Attack on Titan (2013)"
|
||||||
|
updateProgressBar(key, data.percent);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|||||||
@ -248,75 +248,18 @@ conda run -n AniWorld python -m pytest tests/integration/test_identifier_consist
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Phase 8: Documentation and Cleanup
|
### Phase 8: Documentation and Cleanup ✅ **Completed November 28, 2025**
|
||||||
|
|
||||||
#### Task 8.1: Update Infrastructure Documentation
|
All tasks completed:
|
||||||
|
|
||||||
**File:** [`infrastructure.md`](infrastructure.md)
|
- **Task 8.1**: Updated `infrastructure.md` with enhanced identifier convention section including table format, key format requirements, migration notes, and code examples
|
||||||
|
- **Task 8.2**: Updated `docs/README.md` with series identifier convention section, updated `docs/api_reference.md` with key-based API examples
|
||||||
|
- **Task 8.3**: Added deprecation warnings with Python's `warnings` module to:
|
||||||
|
- `SerieList.get_by_folder()` in `src/core/entities/SerieList.py`
|
||||||
|
- Folder fallback lookup in `src/server/api/anime.py`
|
||||||
|
- `validate_series_key_or_folder()` in `src/server/utils/validators.py`
|
||||||
|
|
||||||
**Objective:** Document the identifier standardization in infrastructure documentation.
|
All deprecation warnings include removal timeline (v3.0.0) and guidance to use `key` instead.
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
|
|
||||||
1. Open [`infrastructure.md`](infrastructure.md)
|
|
||||||
2. Add section explaining identifier usage:
|
|
||||||
- `key`: Unique series identifier (provider-assigned)
|
|
||||||
- `folder`: Filesystem folder name (metadata only)
|
|
||||||
- `id`: Database primary key (internal use)
|
|
||||||
3. Update all API documentation to show `key` as identifier
|
|
||||||
4. Update data model documentation
|
|
||||||
5. Add migration notes if needed
|
|
||||||
|
|
||||||
**Success Criteria:**
|
|
||||||
|
|
||||||
- [ ] Documentation clearly explains identifier usage
|
|
||||||
- [ ] All API examples use `key`
|
|
||||||
- [ ] Data model section updated
|
|
||||||
- [ ] Migration notes added if applicable
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### Task 8.2: Update README and Developer Documentation
|
|
||||||
|
|
||||||
**Files:** [`README.md`](README.md), [`docs/`](docs/)
|
|
||||||
|
|
||||||
**Objective:** Update all developer-facing documentation.
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
|
|
||||||
1. Update main README to explain identifier usage
|
|
||||||
2. Update any developer guides to use `key`
|
|
||||||
3. Add note about backward compatibility with `folder`
|
|
||||||
4. Update code examples to use `key`
|
|
||||||
|
|
||||||
**Success Criteria:**
|
|
||||||
|
|
||||||
- [ ] README updated
|
|
||||||
- [ ] Developer guides updated
|
|
||||||
- [ ] Code examples use `key`
|
|
||||||
- [ ] Backward compatibility documented
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### Task 8.3: Add Deprecation Warnings for Folder-Based Lookups
|
|
||||||
|
|
||||||
**Files:** Various service and API files
|
|
||||||
|
|
||||||
**Objective:** Add deprecation warnings where `folder` is still accepted for backward compatibility.
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
|
|
||||||
1. Identify all methods that accept `folder` for lookups
|
|
||||||
2. Add deprecation warnings using Python's `warnings` module
|
|
||||||
3. Update docstrings to indicate deprecation
|
|
||||||
4. Plan removal timeline (e.g., next major version)
|
|
||||||
|
|
||||||
**Success Criteria:**
|
|
||||||
|
|
||||||
- [ ] Deprecation warnings added
|
|
||||||
- [ ] Docstrings indicate deprecation
|
|
||||||
- [ ] Removal timeline documented
|
|
||||||
- [ ] Tests updated to suppress warnings
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -482,18 +425,11 @@ conda run -n AniWorld python -m pytest tests/integration/test_identifier_consist
|
|||||||
- [x] Phase 3: Service Layer ✅
|
- [x] Phase 3: Service Layer ✅
|
||||||
- [x] Phase 4: API Layer ✅ **Completed November 28, 2025**
|
- [x] Phase 4: API Layer ✅ **Completed November 28, 2025**
|
||||||
- [x] Phase 5: Frontend ✅ **Completed November 28, 2025**
|
- [x] Phase 5: Frontend ✅ **Completed November 28, 2025**
|
||||||
- [x] Task 5.1: Update Frontend JavaScript
|
|
||||||
- [x] Task 5.2: Update WebSocket Events
|
|
||||||
- [x] Task 5.3: Update Additional Frontend JavaScript Files
|
|
||||||
- [x] Task 5.4: Update HTML Templates
|
|
||||||
- [x] Phase 6: Database Layer ✅ **Completed November 28, 2025**
|
- [x] Phase 6: Database Layer ✅ **Completed November 28, 2025**
|
||||||
- [x] Task 6.1: Verify Database Models
|
|
||||||
- [x] Task 6.2: Update Database Services
|
|
||||||
- [x] Phase 7: Testing and Validation ✅ **Completed November 28, 2025**
|
- [x] Phase 7: Testing and Validation ✅ **Completed November 28, 2025**
|
||||||
- [x] Task 7.1: Update Test Fixtures - Updated all test fixtures and mocks to use `key` consistently with realistic key values (URL-safe, lowercase, hyphenated)
|
- [x] Phase 8: Documentation and Cleanup ✅ **Completed November 28, 2025**
|
||||||
- [x] Task 7.2: Add Integration Tests - Created `tests/integration/test_identifier_consistency.py` with 10 tests verifying `key` usage across all layers
|
- [x] Task 8.1: Update Infrastructure Documentation - Enhanced identifier convention section with table, format requirements, migration notes, and code examples
|
||||||
- [ ] Phase 8: Documentation and Cleanup
|
- [x] Task 8.2: Update README and Developer Docs - Updated docs/README.md with identifier section, updated api_reference.md with key-based examples
|
||||||
- [ ] Task 8.1: Update Infrastructure Documentation
|
- [x] Task 8.3: Add Deprecation Warnings - Added warnings to SerieList.get_by_folder(), anime.py folder fallback, and validators.py
|
||||||
- [ ] Task 8.2: Update README
|
|
||||||
- [ ] Task 8.3: Add Deprecation Warnings
|
|
||||||
- [ ] Phase 9: Final Validation
|
- [ ] Phase 9: Final Validation
|
||||||
|
- [ ] Phase 10: Deployment
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import warnings
|
||||||
from json import JSONDecodeError
|
from json import JSONDecodeError
|
||||||
from typing import Dict, Iterable, List, Optional
|
from typing import Dict, Iterable, List, Optional
|
||||||
|
|
||||||
@ -144,6 +145,11 @@ class SerieList:
|
|||||||
"""
|
"""
|
||||||
Get a series by its folder name.
|
Get a series by its folder name.
|
||||||
|
|
||||||
|
.. deprecated:: 2.0.0
|
||||||
|
Use :meth:`get_by_key` instead. Folder-based lookups will be
|
||||||
|
removed in version 3.0.0. The `folder` field is metadata only
|
||||||
|
and should not be used for identification.
|
||||||
|
|
||||||
This method is provided for backward compatibility only.
|
This method is provided for backward compatibility only.
|
||||||
Prefer using get_by_key() for new code.
|
Prefer using get_by_key() for new code.
|
||||||
|
|
||||||
@ -153,6 +159,12 @@ class SerieList:
|
|||||||
Returns:
|
Returns:
|
||||||
The Serie instance if found, None otherwise
|
The Serie instance if found, None otherwise
|
||||||
"""
|
"""
|
||||||
|
warnings.warn(
|
||||||
|
"get_by_folder() is deprecated and will be removed in v3.0.0. "
|
||||||
|
"Use get_by_key() instead. The 'folder' field is metadata only.",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2
|
||||||
|
)
|
||||||
for serie in self.keyDict.values():
|
for serie in self.keyDict.values():
|
||||||
if serie.folder == folder:
|
if serie.folder == folder:
|
||||||
return serie
|
return serie
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import logging
|
||||||
|
import warnings
|
||||||
from typing import Any, List, Optional
|
from typing import Any, List, Optional
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
@ -11,6 +13,8 @@ from src.server.utils.dependencies import (
|
|||||||
require_auth,
|
require_auth,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/anime", tags=["anime"])
|
router = APIRouter(prefix="/api/anime", tags=["anime"])
|
||||||
|
|
||||||
|
|
||||||
@ -715,6 +719,21 @@ async def get_anime(
|
|||||||
for serie in series:
|
for serie in series:
|
||||||
if getattr(serie, "folder", None) == anime_id:
|
if getattr(serie, "folder", None) == anime_id:
|
||||||
found = serie
|
found = serie
|
||||||
|
# Log deprecation warning for folder-based lookup
|
||||||
|
key = getattr(serie, "key", "unknown")
|
||||||
|
logger.warning(
|
||||||
|
"Folder-based lookup for '%s' is deprecated. "
|
||||||
|
"Use series key '%s' instead. Folder-based lookups "
|
||||||
|
"will be removed in v3.0.0.",
|
||||||
|
anime_id,
|
||||||
|
key
|
||||||
|
)
|
||||||
|
warnings.warn(
|
||||||
|
f"Folder-based lookup for '{anime_id}' is deprecated. "
|
||||||
|
f"Use series key '{key}' instead.",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2
|
||||||
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
if not found:
|
if not found:
|
||||||
|
|||||||
@ -6,6 +6,7 @@ utilities for ensuring data integrity across the application.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import warnings
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
@ -496,6 +497,10 @@ def validate_series_key_or_folder(
|
|||||||
"""
|
"""
|
||||||
Validate an identifier that could be either a series key or folder.
|
Validate an identifier that could be either a series key or folder.
|
||||||
|
|
||||||
|
.. deprecated:: 2.0.0
|
||||||
|
Folder-based identification is deprecated. Use series `key` only.
|
||||||
|
This function will require key format only in v3.0.0.
|
||||||
|
|
||||||
This function provides backward compatibility during the transition
|
This function provides backward compatibility during the transition
|
||||||
from folder-based to key-based identification.
|
from folder-based to key-based identification.
|
||||||
|
|
||||||
@ -532,6 +537,15 @@ def validate_series_key_or_folder(
|
|||||||
"Keys must be lowercase with hyphens (e.g., 'attack-on-titan')."
|
"Keys must be lowercase with hyphens (e.g., 'attack-on-titan')."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Emit deprecation warning for folder-based identification
|
||||||
|
warnings.warn(
|
||||||
|
f"Folder-based identification for '{identifier}' is deprecated. "
|
||||||
|
"Use series key (lowercase with hyphens) instead. "
|
||||||
|
"Folder-based identification will be removed in v3.0.0.",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2
|
||||||
|
)
|
||||||
|
|
||||||
# Validate as folder (more permissive)
|
# Validate as folder (more permissive)
|
||||||
if len(identifier) > 1000:
|
if len(identifier) > 1000:
|
||||||
raise ValueError("Identifier too long (max 1000 characters)")
|
raise ValueError("Identifier too long (max 1000 characters)")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user