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` **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 ## Core Services
### SeriesApp (`src/core/SeriesApp.py`) ### SeriesApp (`src/core/SeriesApp.py`)

View File

@ -156,88 +156,12 @@ All API layer tasks completed.
### Phase 5: Frontend ✅ (Completed November 28, 2025) ### Phase 5: Frontend ✅ (Completed November 28, 2025)
All frontend tasks completed:
- **Task 5.1: Update Frontend JavaScript** ### Phase 6: Database Layer ✅ (Completed November 28, 2025)
- 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`
- **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
--- All database layer tasks completed:
- Task 6.1: Verified `AnimeSeries.key` is unique and indexed, `folder` is metadata only, updated docstrings
### Phase 6: Database Layer - Task 6.2: Verified all service methods use `key` for lookups, no folder-based identification
#### 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
```
--- ---
@ -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.2: Update WebSocket Events
- [x] Task 5.3: Update Additional Frontend JavaScript Files - [x] Task 5.3: Update Additional Frontend JavaScript Files
- [x] Task 5.4: Update HTML Templates - [x] Task 5.4: Update HTML Templates
- [ ] Phase 6: Database Layer - [x] Phase 6: Database Layer ✅ **Completed November 28, 2025**
- [ ] Task 6.1: Verify Database Models - [x] Task 6.1: Verify Database Models
- [ ] Task 6.2: Update Database Services - [x] Task 6.2: Update Database Services
- [ ] Phase 7: Testing and Validation - [ ] Phase 7: Testing and Validation
- [ ] Task 7.1: Update Test Fixtures - [ ] Task 7.1: Update Test Fixtures
- [ ] Task 7.2: Add Integration Tests - [ ] Task 7.2: Add Integration Tests

View File

@ -38,20 +38,31 @@ class AnimeSeries(Base, TimestampMixin):
Represents an anime series with metadata, provider information, Represents an anime series with metadata, provider information,
and links to episodes. Corresponds to the core Serie class. 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: Attributes:
id: Primary key id: Database primary key (internal use for relationships)
key: Unique identifier used by provider key: Unique provider key - PRIMARY IDENTIFIER for all lookups
name: Series name name: Display name of the series
site: Provider site URL site: Provider site URL
folder: Local filesystem path folder: Filesystem folder name (metadata only, not for lookups)
description: Optional series description description: Optional series description
status: Current status (ongoing, completed, etc.) status: Current status (ongoing, completed, etc.)
total_episodes: Total number of episodes total_episodes: Total number of episodes
cover_url: URL to series cover image cover_url: URL to series cover image
episodes: Relationship to Episode models episodes: Relationship to Episode models (via id foreign key)
download_items: Relationship to DownloadQueueItem models download_items: Relationship to DownloadQueueItem models (via id foreign key)
created_at: Creation timestamp (from TimestampMixin) created_at: Creation timestamp (from TimestampMixin)
updated_at: Last update 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" __tablename__ = "anime_series"
@ -63,7 +74,7 @@ class AnimeSeries(Base, TimestampMixin):
# Core identification # Core identification
key: Mapped[str] = mapped_column( key: Mapped[str] = mapped_column(
String(255), unique=True, nullable=False, index=True, 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( name: Mapped[str] = mapped_column(
String(500), nullable=False, index=True, String(500), nullable=False, index=True,
@ -75,7 +86,7 @@ class AnimeSeries(Base, TimestampMixin):
) )
folder: Mapped[str] = mapped_column( folder: Mapped[str] = mapped_column(
String(1000), nullable=False, String(1000), nullable=False,
doc="Local filesystem path" doc="Filesystem folder name - METADATA ONLY, not for lookups"
) )
# Metadata # Metadata

View File

@ -43,6 +43,11 @@ class AnimeSeriesService:
Provides methods for creating, reading, updating, and deleting anime series Provides methods for creating, reading, updating, and deleting anime series
with support for both async and sync database sessions. 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 @staticmethod
@ -115,12 +120,19 @@ class AnimeSeriesService:
async def get_by_key(db: AsyncSession, key: str) -> Optional[AnimeSeries]: async def get_by_key(db: AsyncSession, key: str) -> Optional[AnimeSeries]:
"""Get anime series by provider key. """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: Args:
db: Database session db: Database session
key: Unique provider key key: Unique provider key (e.g., "attack-on-titan")
Returns: Returns:
AnimeSeries instance or None if not found AnimeSeries instance or None if not found
Note:
Do NOT use folder for lookups - it's metadata only.
""" """
result = await db.execute( result = await db.execute(
select(AnimeSeries).where(AnimeSeries.key == key) select(AnimeSeries).where(AnimeSeries.key == key)