Phase 6: Update database layer identifier documentation

- Updated AnimeSeries model docstring to clarify key is primary identifier
- Updated folder field to indicate metadata-only usage
- Updated AnimeSeriesService docstring and get_by_key method
- Updated infrastructure.md with database identifier documentation
- All 996 tests passing
This commit is contained in:
Lukas 2025-11-28 17:19:30 +01:00
parent a833077f97
commit 0c8b296aa6
4 changed files with 53 additions and 92 deletions

View File

@ -132,6 +132,20 @@ All series-related WebSocket events include `key` as the primary identifier in t
**Mixins**: `TimestampMixin` (created_at, updated_at), `SoftDeleteMixin`
### AnimeSeries Identifier Fields
| Field | Type | Purpose |
| ------ | ----------- | ------------------------------------------------- |
| `id` | Primary Key | Internal database key for relationships |
| `key` | Unique, Indexed | **PRIMARY IDENTIFIER** for all lookups |
| `folder` | String | Filesystem metadata only (not for identification) |
**Database Service Methods:**
- `AnimeSeriesService.get_by_key(key)` - **Primary lookup method**
- `AnimeSeriesService.get_by_id(id)` - Internal lookup by database ID
- No `get_by_folder()` method exists - folder is never used for lookups
## Core Services
### SeriesApp (`src/core/SeriesApp.py`)

View File

@ -156,88 +156,12 @@ All API layer tasks completed.
### Phase 5: Frontend ✅ (Completed November 28, 2025)
All frontend tasks completed:
- **Task 5.1: Update Frontend JavaScript**
- Updated `app.js` to use `key` as primary series identifier
- `selectedSeries` Set now uses `key` instead of `folder`
- `createSerieCard()` uses `data-key` attribute for identification
- `toggleSerieSelection()` uses `key` for lookups
- All selection and download operations use `key`
### Phase 6: Database Layer ✅ (Completed November 28, 2025)
- **Task 5.2: Update WebSocket Events**
- WebSocket service already has proper documentation for `key` usage
- Updated tests to include `key` and `folder` in broadcast data
- Tests verify both fields are included in messages
- **Task 5.3: Update Additional Frontend JavaScript Files**
- Reviewed `queue.js`, `websocket_client.js`, and utility files
- No changes needed - these files use download item IDs correctly
- Series identification is handled in `app.js`
- **Task 5.4: Update HTML Templates**
- Reviewed all templates (`index.html`, `queue.html`, etc.)
- No changes needed - series cards are rendered dynamically in JavaScript
- Static templates don't contain series data attributes
---
### Phase 6: Database Layer
#### Task 6.1: Verify Database Models Use Key Correctly
**File:** [`src/server/database/models.py`](src/server/database/models.py)
**Objective:** Verify and document that database models correctly use `key` as unique identifier.
**Steps:**
1. Open [`src/server/database/models.py`](src/server/database/models.py)
2. Verify `AnimeSeries.key` is unique and indexed
3. Verify `AnimeSeries.folder` is not used for lookups
4. Update docstrings to clarify identifier usage
5. Ensure all relationships use `id` (primary key) not `key` or `folder`
**Success Criteria:**
- [ ] `key` is unique and indexed
- [ ] `folder` is metadata only
- [ ] Docstrings are clear
- [ ] All database model tests pass
**Test Command:**
```bash
conda run -n AniWorld python -m pytest tests/unit/test_database_models.py -v
```
---
#### Task 6.2: Update Database Services to Use Key
**File:** [`src/server/database/service.py`](src/server/database/service.py)
**Objective:** Ensure all database service methods use `key` for series lookup.
**Steps:**
1. Open [`src/server/database/service.py`](src/server/database/service.py)
2. Verify `AnimeSeriesService.get_by_key()` is used for lookups
3. Verify no methods use `folder` for identification
4. Update any methods that incorrectly use `folder` for lookups
5. Add migration helper if needed to update existing data
**Success Criteria:**
- [ ] All service methods use `key` for lookups
- [ ] `folder` never used as identifier
- [ ] All database service tests pass
**Test Command:**
```bash
conda run -n AniWorld python -m pytest tests/unit/test_database_service.py -v
```
All database layer tasks completed:
- Task 6.1: Verified `AnimeSeries.key` is unique and indexed, `folder` is metadata only, updated docstrings
- Task 6.2: Verified all service methods use `key` for lookups, no folder-based identification
---
@ -543,9 +467,9 @@ conda run -n AniWorld python -m pytest tests/integration/test_identifier_consist
- [x] Task 5.2: Update WebSocket Events
- [x] Task 5.3: Update Additional Frontend JavaScript Files
- [x] Task 5.4: Update HTML Templates
- [ ] Phase 6: Database Layer
- [ ] Task 6.1: Verify Database Models
- [ ] Task 6.2: Update Database Services
- [x] Phase 6: Database Layer ✅ **Completed November 28, 2025**
- [x] Task 6.1: Verify Database Models
- [x] Task 6.2: Update Database Services
- [ ] Phase 7: Testing and Validation
- [ ] Task 7.1: Update Test Fixtures
- [ ] Task 7.2: Add Integration Tests

View File

@ -38,20 +38,31 @@ class AnimeSeries(Base, TimestampMixin):
Represents an anime series with metadata, provider information,
and links to episodes. Corresponds to the core Serie class.
Series Identifier Convention:
- `key`: PRIMARY IDENTIFIER - Unique, provider-assigned, URL-safe
(e.g., "attack-on-titan"). Used for all lookups and operations.
- `folder`: METADATA ONLY - Filesystem folder name for display
(e.g., "Attack on Titan (2013)"). Never used for identification.
- `id`: Internal database primary key for relationships.
Attributes:
id: Primary key
key: Unique identifier used by provider
name: Series name
id: Database primary key (internal use for relationships)
key: Unique provider key - PRIMARY IDENTIFIER for all lookups
name: Display name of the series
site: Provider site URL
folder: Local filesystem path
folder: Filesystem folder name (metadata only, not for lookups)
description: Optional series description
status: Current status (ongoing, completed, etc.)
total_episodes: Total number of episodes
cover_url: URL to series cover image
episodes: Relationship to Episode models
download_items: Relationship to DownloadQueueItem models
episodes: Relationship to Episode models (via id foreign key)
download_items: Relationship to DownloadQueueItem models (via id foreign key)
created_at: Creation timestamp (from TimestampMixin)
updated_at: Last update timestamp (from TimestampMixin)
Note:
All database relationships use `id` (primary key), not `key` or `folder`.
Use `get_by_key()` in AnimeSeriesService for lookups.
"""
__tablename__ = "anime_series"
@ -63,7 +74,7 @@ class AnimeSeries(Base, TimestampMixin):
# Core identification
key: Mapped[str] = mapped_column(
String(255), unique=True, nullable=False, index=True,
doc="Unique provider key"
doc="Unique provider key - PRIMARY IDENTIFIER for all lookups"
)
name: Mapped[str] = mapped_column(
String(500), nullable=False, index=True,
@ -75,7 +86,7 @@ class AnimeSeries(Base, TimestampMixin):
)
folder: Mapped[str] = mapped_column(
String(1000), nullable=False,
doc="Local filesystem path"
doc="Filesystem folder name - METADATA ONLY, not for lookups"
)
# Metadata

View File

@ -43,6 +43,11 @@ class AnimeSeriesService:
Provides methods for creating, reading, updating, and deleting anime series
with support for both async and sync database sessions.
Series Identifier Convention:
- Use `get_by_key()` for lookups by provider key (primary identifier)
- Use `get_by_id()` for lookups by database primary key (internal)
- Never use `folder` for identification - it's metadata only
"""
@staticmethod
@ -115,12 +120,19 @@ class AnimeSeriesService:
async def get_by_key(db: AsyncSession, key: str) -> Optional[AnimeSeries]:
"""Get anime series by provider key.
This is the PRIMARY lookup method for series identification.
Use this method instead of get_by_id() when looking up by
the provider-assigned unique key.
Args:
db: Database session
key: Unique provider key
key: Unique provider key (e.g., "attack-on-titan")
Returns:
AnimeSeries instance or None if not found
Note:
Do NOT use folder for lookups - it's metadata only.
"""
result = await db.execute(
select(AnimeSeries).where(AnimeSeries.key == key)