Files
mtgonline/backend/ARCHITECTURAL_REPORT.md
T
akadmin 0e6506d97f Update state.json and development plan to reflect actual progress (~15%)
- state.json: Phase 5 status changed to pending, next steps updated
- DEVELOPMENT_PLAN.md v1.1: Added actual progress, verified current state,
  renumbered phases 5-9, marked all remaining work as PENDING
- Deleted outdated plans (BACKEND_TEST_PLAN, DATA_LOADING_VERIFICATION,
  DOCKER_MIGRATION_PLAN, ENDPOINT_AUDIT, ROADMAP, SIMPLIFICATION_PLAN,
  STATEMENT_OF_INTENT, backend/TEST_PLAN)
- Added new files: DEVELOPMENT_PLAN.md, backend/ARCHITECTURAL_REPORT.md,
  backend/SIMPLIFICATION_SUMMARY.md, backend/run.py, backend/.dockerignore
2026-08-27 04:41:30 +00:00

1669 lines
51 KiB
Markdown

# MTG Online Backend - Comprehensive Architectural Report
**Project:** MTG Online Backend
**Location:** `/home/wall-o/projects/mtgonline/backend/app/`
**Framework:** FastAPI with SQLAlchemy async
**Database:** SQLite (dev) / PostgreSQL (prod) + Redis caching
**Date:** 2025-07-21
---
## Executive Summary
The MTG Online backend is a FastAPI application providing card game management features including user authentication, deck building, card search with fuzzy matching, card collection management, and real-time game WebSocket support. The codebase exhibits significant **architectural debt** with multiple layers of duplicated functionality (old vs. new router patterns), inconsistent database models, and redundant services.
**Key Issues Identified:**
1. **Router Duplication:** Every major feature has both old and new router implementations
2. **Model Inconsistency:** Multiple model files with overlapping functionality
3. **Service Redundancy:** Multiple services performing similar operations
4. **Schema Confusion:** Duplicate schema definitions across files
5. **Mixed Database Patterns:** Some services use ORM, others use raw SQL
---
## Layer Analysis
### 1. Core Layer (`app/core/`)
**Purpose:** Application infrastructure - configuration, database, security, logging, caching
#### 1.1 `config.py` (216 lines)
**Purpose:** Application configuration management with environment variable overrides
**Key Classes/Functions:**
- `Config` class: Configuration with sensible defaults
- `get_config()`: Singleton configuration instance
- `_load_env_file()`: Load .env files
**Imports From:**
- `os` (stdlib)
- `typing.Optional`
**Imported By:**
- `database.py`
- `security.py`
- `settings.py` (via config)
**Dependencies:** None (standalone)
---
#### 1.2 `database.py` (193 lines)
**Purpose:** SQLAlchemy async engine and session management
**Key Classes/Functions:**
- `Base`: DeclarativeBase for ORM models
- `engine`: Async engine instance
- `async_session`: Session factory
- `get_db()`: FastAPI dependency for database sessions
- `create_tables()`: Create all tables
- Backward-compatible aliases: `mtg_engine`, `mtg_async_session`, `mtg_get_db`, `mirror_get_db`
**Imports From:**
- `app.core.config`: `get_config`
- `sqlalchemy.ext.asyncio`: `AsyncSession`, `async_sessionmaker`, `create_async_engine`
- `sqlalchemy.orm`: `DeclarativeBase`
**Imported By:**
- `models/__init__.py`
- `models/models.py`
- `models/mtg_models.py`
- `routers/*.py` (multiple)
- `services/*.py` (multiple)
**Dependencies:** `config.py`
---
#### 1.3 `logging.py` (187 lines)
**Purpose:** Structured logging configuration with JSON/text support
**Key Classes/Functions:**
- `ContainerFormatter`: Custom log formatter
- `configure_logging()`: Configure application logging
**Imports From:**
- `json`, `logging`, `os`, `sys`, `datetime`, `typing`
**Imported By:**
- Imported by `logging_config.py` (duplicate)
- Used by various services
**Dependencies:** None (standalone)
---
#### 1.4 `logging_config.py` (97 lines)
**Purpose:** **DUPLICATE** of logging.py - structured logging configuration
**Key Classes/Functions:**
- `ContainerFormatter`: Same as logging.py
- `configure_logging()`: Same as logging.py
**Imports From:**
- `json`, `logging`, `os`, `sys`, `datetime`
**Imported By:**
- Auto-configures on import
**Dependencies:** None (standalone)
**⚠️ ISSUE:** Exact duplicate of logging.py. Should be removed.
---
#### 1.5 `redis_client.py` (90 lines)
**Purpose:** Redis client and caching layer
**Key Classes/Functions:**
- `get_redis()`: Get Redis client instance
- `close_redis()`: Close Redis connection
- `cache_get()`, `cache_set()`, `cache_delete()`, `cache_invalidate_pattern()`: Cache operations
**Imports From:**
- `app.core.settings`: `get_settings`
- `redis.asyncio`: `aioredis`
**Imported By:**
- `routers/card_router.py`
- `routers/cards_router.py`
- `routers/interactions.py`
**Dependencies:** `settings.py`
---
#### 1.6 `security.py` (238 lines)
**Purpose:** Authentication and password hashing utilities
**Key Classes/Functions:**
- `verify_password()`, `hash_password()`: Password operations
- `create_access_token()`, `create_refresh_token()`: JWT token creation
- `decode_token()`: JWT token validation
- `get_current_user()`: FastAPI dependency for auth
**Imports From:**
- `app.core.config`: `get_config`
- `fastapi`: `Header`, `HTTPException`, `status`
- `jose`: `JWTError`, `jwt`
- `passlib.context`: `CryptContext`
**Imported By:**
- `routers/auth.py`
- `routers/auth_router.py`
- `routers/users.py`
- `routers/users_router.py`
- `routers/admin.py`
- `routers/rooms.py`
- `routers/games_router.py`
**Dependencies:** `config.py`
---
#### 1.7 `settings.py` (29 lines)
**Purpose:** **BACKWARD COMPATIBILITY SHIM** - re-exports config module
**Key Classes/Functions:**
- `get_settings`: Alias for `get_config`
**Imports From:**
- `app.core.config`: `Config`, `get_config`
**Imported By:**
- `redis_client.py`
- `services/mtgjson_loader.py`
- `services/mtgjson_uploader.py`
**Dependencies:** `config.py`
**⚠️ ISSUE:** Should be removed once all callers migrate to `config.py`
---
### 2. Models Layer (`app/models/`)
**Purpose:** SQLAlchemy ORM models for database entities
#### 2.1 `__init__.py` (30 lines)
**Purpose:** Exports all models from models.py and mtg_models.py
**Imports From:**
- `app.models.models`: All application models
- `app.models.mtg_models`: MtgSet, MtgCard, MtgCardMirror, DeckCardLink
**Imported By:**
- Various routers and services
**Dependencies:** `models.py`, `mtg_models.py`
---
#### 2.2 `models.py` (968 lines)
**Purpose:** Main application ORM models
**Key Classes:**
- **Core:** `User`, `DecklistFolder`, `DecklistFile`, `Room`, `RoomGameType`, `Ban`, `GameLog`, `AuditLog`
- **Card Mirror:** `MtgonlineCard` (local copy for deckbuilding)
- **User Decks:** `UserDeck`, `UserDeckCard`, `DeckPrecedent`, `DeckPrecedentCard`, `CardSuggestion`
- **User Data:** `UserSession`, `DeckVersion`, `GameReplay`, `ReplayPlayer`, `GameOutcome`, `UserStatistics`, `UserCardCollection`, `CardWishlist`, `UserGroup`, `GroupMember`, `GroupChatMessage`, `UserNetwork`, `NetworkMember`, `UserPreference`, `UserActivityLog`
- **Card Imports:** `CardImportBatch`, `UserCardImportRecord`, `UserCardImport`
**Imports From:**
- `app.core.database`: `Base`
- `sqlalchemy`: Various column types, relationships
**Imported By:**
- `models/__init__.py`
- `routers/*.py` (multiple)
- `services/*.py` (multiple)
**Dependencies:** `database.py`
---
#### 2.3 `mtg_models.py` (184 lines)
**Purpose:** MTG card data models
**Key Classes:**
- `MtgSet`: MTG set data
- `MtgCard`: MTG card data (canonical)
- `MtgCardMirror`: Mirrored card data for fast queries
- `DeckCardLink`: Junction table linking decks to mirrored cards
**Imports From:**
- `app.core.database`: `Base`
- `sqlalchemy`: Column types, relationships
**Imported By:**
- `models/__init__.py`
- `routers/card_router.py`
- `routers/cards_router.py`
- `services/card_search_service.py`
- `services/deck_suggestion_service.py`
**Dependencies:** `database.py`
---
#### 2.4 `__backup__/` (Directory)
**Purpose:** Backup of old model files (not analyzed)
---
### 3. Routers Layer (`app/routers/`)
**Purpose:** FastAPI route handlers for API endpoints
**⚠️ CRITICAL ISSUE:** Every major feature has **both old and new router implementations** with significant duplication.
#### 3.1 `__init__.py` (29 lines)
**Purpose:** Exports consolidated routers
**Imports From:**
- `app.routers.auth_router`
- `app.routers.users_router`
- `app.routers.decks_router`
- `app.routers.cards_router`
- `app.routers.user_data_router`
- `app.routers.games_router`
**Imported By:**
- `app/main.py`
**Dependencies:** All router modules
---
#### 3.2 `auth.py` (162 lines) - **OLD VERSION**
**Purpose:** Authentication endpoints (login, refresh, register, me)
**Key Endpoints:**
- `POST /login`: Authenticate user
- `POST /refresh`: Refresh access token
- `POST /register`: Register new user
- `GET /me`: Get current user
**Imports From:**
- `app.core.database`: `get_db`
- `app.core.security`: `verify_password`, `hash_password`, `create_access_token`, `create_refresh_token`, `decode_token`
- `app.models.models`: `User`
- `app.schemas.schemas`: Login/refresh/user schemas
**Imported By:** None (old version, superseded by auth_router.py)
**Dependencies:** `database.py`, `security.py`, `models.py`, `schemas.py`
---
#### 3.3 `auth_router.py` (246 lines) - **NEW VERSION**
**Purpose:** Authentication endpoints (consolidated)
**Key Endpoints:** Same as auth.py but with better structure
**Imports From:**
- `app.core.database`: `get_db`
- `app.core.security`: Token functions
- `app.models.models`: `User`
- `app.schemas.schemas`: Login/refresh/user schemas
**Imported By:**
- `app/main.py`
**Dependencies:** `database.py`, `security.py`, `models.py`, `schemas.py`
---
#### 3.4 `users.py` (168 lines) - **OLD VERSION**
**Purpose:** User management endpoints
**Key Endpoints:**
- `GET /{user_id}`: Get user
- `PATCH /{user_id}`: Update user
- `POST /{user_id}/ban`: Ban user
- `POST /{user_id}/unban`: Unban user
**Imports From:**
- `app.core.database`: `get_db`
- `app.core.security`: `get_current_user`, `hash_password`
- `app.models.models`: `User`
- `app.schemas.schemas`: User schemas
**Imported By:** None (old version, superseded by users_router.py)
**Dependencies:** `database.py`, `security.py`, `models.py`, `schemas.py`
---
#### 3.5 `users_router.py` (549 lines) - **NEW VERSION**
**Purpose:** User management + admin operations (consolidated)
**Key Endpoints:**
- User management: GET/PATCH user, ban/unban
- Admin operations: List users, list bans, create ban, unban by ID, list logs, audit log
**Imports From:**
- `app.core.database`: `get_db`
- `app.core.security`: `get_current_user`, `hash_password`
- `app.models.models`: `User`, `Ban`, `GameLog`, `AuditLog`
- `app.schemas.schemas`: User/ban schemas
**Imported By:**
- `app/main.py`
**Dependencies:** `database.py`, `security.py`, `models.py`, `schemas.py`
---
#### 3.6 `decks.py` (721 lines) - **OLD VERSION**
**Purpose:** Deck management endpoints
**Key Endpoints:**
- CRUD for user decks
- Deck card management
- Precedent management
- Card search integration
- Card suggestions
**Imports From:**
- `app.core.database`: `get_db`, `mtg_get_db`
- `app.core.security`: `get_current_user`
- `app.models.models`: `User`, `DecklistFolder`, `MtgonlineCard`
- `app.models.user_deck`: `UserDeck`, `UserDeckCard`, etc.
- `app.services.deck_manager`: `DeckManager`
- `app.services.deck_parser`: `DeckParser`
**Imported By:** None (old version, superseded by decks_router.py)
**Dependencies:** Multiple
---
#### 3.7 `decks_router.py` (810 lines) - **NEW VERSION**
**Purpose:** Deck management (consolidated)
**Key Endpoints:** Same as decks.py but with better structure
**Imports From:**
- `app.core.database`: `get_db`
- `app.core.security`: `get_current_user`
- `app.models.mtg_models`: `MtgCard`, `MtgSet`
- `app.models.models`: `MtgonlineCard`, `User`, `DecklistFolder`
- `app.models.user_deck`: Deck models
- `app.services.deck_manager`: `DeckManager`
- `app.services.deck_parser`: `DeckParser`
**Imported By:**
- `app/main.py`
**Dependencies:** Multiple
---
#### 3.8 `card_router.py` (292 lines) - **OLD VERSION**
**Purpose:** Card search endpoints
**Key Endpoints:**
- `GET /api/cards/search`: Search cards
- `GET /api/cards/{card_id}`: Get card by ID
- `GET /api/cards/sets`: List sets
- `GET /api/cards/types`: List card types
- `GET /api/cards/rarities`: List rarities
- `GET /api/cards/suggest`: Suggest cards
**Imports From:**
- `app.core.database`: `mtg_get_db`
- `app.core.redis_client`: `cache_get`, `cache_set`
- `app.services.card_search_service`: `CardSearchService`
- `app.services.deck_suggestion_service`: `DeckSuggestionService`
- `app.services.fuzzy_card_matcher`: `FuzzyCardMatcher`
- `app.models.user_deck`: `UserDeck`
- `app.models.mtg_models`: `MtgCard`
**Imported By:** None (old version, superseded by cards_router.py)
**Dependencies:** Multiple
---
#### 3.9 `cards_router.py` (995 lines) - **NEW VERSION**
**Purpose:** Card search + card interactions (consolidated)
**Key Endpoints:**
- Card search: Same as card_router.py
- Card interactions: synergies, counters, evolutions, recommendations, stats
**Imports From:**
- `app.core.database`: `mtg_get_db`
- `app.core.redis_client`: `cache_get`, `cache_set`
- `app.models.mtg_models`: `MtgCard`
- `app.models.user_deck`: `UserDeck`
- `app.schemas.card_search_schemas`: Card schemas
- `app.services.card_search_service`: `CardSearchService`
- `app.services.deck_suggestion_service`: `DeckSuggestionService`
- `app.services.fuzzy_card_matcher`: `FuzzyCardMatcher`
**Imported By:**
- `app/main.py`
**Dependencies:** Multiple
---
#### 3.10 `interactions.py` (674 lines) - **OLD VERSION**
**Purpose:** Card interaction endpoints (synergies, counters, evolutions)
**Key Endpoints:**
- `GET /interactions/synergies/{card_id}`
- `GET /interactions/counters/{card_id}`
- `GET /interactions/evolutions/{card_id}`
- `GET /interactions/recommend/{card_id}`
- Search endpoints for each interaction type
- `GET /interactions/stats/{card_id}`
**Imports From:**
- `app.core.database`: `mtg_get_db`
- `app.core.redis_client`: `cache_get`, `cache_set`
**Imported By:** None (old version, superseded by cards_router.py)
**Dependencies:** `database.py`, `redis_client.py`
---
#### 3.11 `user_data.py` (1714 lines) - **OLD VERSION**
**Purpose:** User data endpoints (sessions, decks, replays, collections, groups, networks, preferences)
**Key Endpoints:**
- Session management
- Deck versions
- Game replays
- Card collection
- Wishlist
- Groups with chat
- Networks
- Preferences
- Activity log
**Imports From:**
- `app.core.database`: `get_db`
- `app.core.security`: `get_current_user`
- `app.models.models`: `User`
- `app.models.user_data`: Various user data models
- `app.schemas.user_card_collection`: Collection schemas
- `app.schemas.user_data_schemas`: User data schemas
**Imported By:** None (old version, superseded by user_data_router.py)
**Dependencies:** Multiple
---
#### 3.12 `user_data_router.py` (69121 bytes) - **NEW VERSION**
**Purpose:** User data + card import (consolidated)
**Key Endpoints:**
- All user_data.py endpoints
- Card import endpoints (upload, status, results, confirm)
**Imports From:**
- `app.core.database`: `get_db`
- `app.core.security`: `get_current_user`
- `app.models.models`: `User`, `MtgonlineCard`
- `app.models.user_data`: User data models
- `app.models.card_import_batch`: `CardImportBatch`
- `app.models.user_card_import_record`: `UserCardImportRecord`
- `app.models.user_deck`: `UserDeck`, `UserDeckCard`
- `app.services.file_parser`: `FileParser`
- `app.services.import_batch_processor`: `ImportBatchProcessor`
- `app.services.fuzzy_card_matcher`: `FuzzyCardMatcher`
- `app.schemas.*`: Various schemas
**Imported By:**
- `app/main.py`
**Dependencies:** Multiple
---
#### 3.13 `rooms.py` (158 lines) - **OLD VERSION**
**Purpose:** Room management endpoints
**Key Endpoints:**
- `GET /`: List rooms
- `GET /{room_id}`: Get room
- `POST /`: Create room (admin)
- `PATCH /{room_id}`: Update room (admin)
- `DELETE /{room_id}`: Delete room (admin)
**Imports From:**
- `app.core.database`: `get_db`
- `app.core.security`: `get_current_user`
- `app.models.models`: `Room`
- `app.schemas.schemas`: `RoomResponse`
**Imported By:** None (old version, superseded by games_router.py)
**Dependencies:** `database.py`, `security.py`, `models.py`, `schemas.py`
---
#### 3.14 `games/__init__.py` (4 lines)
**Purpose:** Games router package init
**Imports From:**
- `app.routers.games.router`: `router`
**Imported By:** None
**Dependencies:** `games/router.py`
---
#### 3.15 `games/router.py` (135 lines) - **OLD VERSION**
**Purpose:** Game management stubs
**Key Endpoints:**
- `GET /`: List games (stub)
- `POST /`: Create game (mock)
- `GET /{game_id}`: Get game (mock)
- `POST /{game_id}/join`: Join game (stub)
- `POST /{game_id}/leave`: Leave game (stub)
- `POST /{game_id}/start`: Start game (stub)
- `POST /{game_id}/end`: End game (stub)
**Imports From:**
- `app.core.database`: `get_db`
- `app.core.security`: `get_current_user`
- `app.models.models`: `User`
- `app.schemas.schemas`: `GameCreate`, `GameResponse`
**Imported By:** None (old version, superseded by games_router.py)
**Dependencies:** `database.py`, `security.py`, `models.py`, `schemas.py`
---
#### 3.16 `games_router.py` (487 lines) - **NEW VERSION**
**Purpose:** Room management + game stubs + MTGJSON refresh + WebSocket (consolidated)
**Key Endpoints:**
- Room CRUD (prefix: /rooms)
- Game stubs (prefix: /games)
- MTGJSON refresh (prefix: /mtgjson)
- WebSocket (prefix: /ws/game/{game_id})
**Imports From:**
- `app.core.database`: `get_db`
- `app.core.security`: `get_current_user`
- `app.models.models`: `Room`, `User`
- `app.schemas.schemas`: `RoomResponse`, `GameCreate`, `GameResponse`
- `app.services.mtgjson_manager`: `MTGJSONManager`
- `app.services.game_server`: `game_websocket_endpoint`
**Imported By:**
- `app/main.py`
**Dependencies:** Multiple
---
#### 3.17 `refresh.py` (101 lines) - **OLD VERSION**
**Purpose:** MTGJSON refresh endpoints
**Key Endpoints:**
- `POST /mtgjson/refresh`: Trigger refresh
- `GET /mtgjson/status`: Get status
- `POST /mtgjson/verify`: Verify files
**Imports From:**
- `app.core.database`: `get_db`
- `app.core.security`: `get_current_user`
- `app.services.mtgjson_manager`: `MTGJSONManager`
**Imported By:** None (old version, superseded by games_router.py)
**Dependencies:** `database.py`, `security.py`, `services/mtgjson_manager.py`
---
#### 3.18 `card_import.py` (368 lines) - **OLD VERSION**
**Purpose:** Card import endpoints
**Key Endpoints:**
- `POST /import`: Upload import file
- `GET /import/{import_id}/status`: Get status
- `GET /import/{import_id}/results`: Get results
- `POST /import/{import_id}/confirm`: Confirm import
- `GET /user/cards`: List user cards
- `DELETE /user/cards/{card_import_id}`: Delete card
- `GET /user/decks`: List user decks
**Imports From:**
- `app.core.database`: `get_db`
- `app.core.security`: `get_current_user`
- `app.models.card_import_batch`: `CardImportBatch`
- `app.models.user_card_import_record`: `UserCardImportRecord`
- `app.models.user_data`: `UserCardCollection`
- `app.models.models`: `MtgonlineCard`
- `app.models.user_deck`: `UserDeck`, `UserDeckCard`
- `app.services.file_parser`: `FileParser`
- `app.services.import_batch_processor`: `ImportBatchProcessor`
- `app.services.fuzzy_card_matcher`: `FuzzyCardMatcher`
**Imported By:** None (old version, superseded by user_data_router.py)
**Dependencies:** Multiple
---
#### 3.19 `ws.py` (512 bytes) - **OLD VERSION**
**Purpose:** WebSocket endpoint for game connections
**Key Endpoints:**
- `GET /ws/game/{game_id}`: WebSocket connection
**Imports From:**
- `app.services.game_server`: `game_websocket_endpoint`
**Imported By:** None (old version, superseded by games_router.py)
**Dependencies:** `services/game_server.py`
---
### 4. Schemas Layer (`app/schemas/`)
**Purpose:** Pydantic models for request/response validation
#### 4.1 `__init__.py` (147 lines)
**Purpose:** Exports all schemas from various schema files
**Imports From:**
- `app.schemas.schemas`: Basic schemas
- `app.schemas.user_data_schemas`: User data schemas
- `app.schemas.user_card_collection`: Collection schemas
- `app.schemas.user_deck_schemas`: Deck schemas
- `app.schemas.card_import_schemas`: Import schemas
- `app.schemas.card_search_schemas`: Search schemas
- `app.schemas.game_schemas`: Game schemas
- `app.schemas.mtg_card_schemas`: MTG card schemas
- `app.schemas.proto_messages`: Protocol messages
- `app.schemas.protocol_constants`: Protocol constants
**Imported By:**
- Various routers
**Dependencies:** All schema files
---
#### 4.2 `schemas.py` (286 lines)
**Purpose:** Basic Pydantic models for auth, users, decks, games, bans
**Key Classes:**
- Auth: `LoginRequest`, `LoginResponse`, `RefreshTokenRequest`, `TokenResponse`
- User: `UserBase`, `UserCreate`, `UserUpdate`, `UserResponse`
- Deck: `DeckCreate`, `DeckUpdate`, `DeckResponse`, `FolderCreate`, `FolderResponse`
- Game: `GameCreate`, `GameResponse`
- Room: `RoomResponse`
- Ban: `BanCreate`, `BanResponse`
- Error: `ErrorResponse`, `ValidationErrorResponse`
- Pagination: `PaginationParams`, `PaginatedResponse`
- Card Mirror: `CardMirrorResponse`, `DeckCardLinkResponse`, `DeckWithCardsResponse`
**Imports From:**
- `pydantic`: `BaseModel`, `EmailStr`, `Field`, `ConfigDict`
- `typing`: `Optional`, `List`, `Dict`
- `datetime`: `datetime`
**Imported By:**
- `routers/auth.py`
- `routers/auth_router.py`
- `routers/users.py`
- `routers/users_router.py`
- `routers/rooms.py`
- `routers/games_router.py`
**Dependencies:** None (standalone)
---
#### 4.3 `user_data_schemas.py` (434 lines)
**Purpose:** User data schemas (sessions, decks, replays, collections, groups, networks, preferences)
**Key Classes:**
- Enums: `DeckVersionStatus`, `GameReplayStatus`, `GameOutcomeType`, `GroupMemberRole`, `NetworkMemberRole`, `UserPreferenceTheme`, `ActivityType`
- Session: `SessionResponse`, `SessionCleanupResponse`
- Deck Version: `DeckVersionCreate`, `DeckVersionUpdate`, `DeckVersionResponse`, `DeckVersionListResponse`
- Game Replay: `GameReplayCreate`, `GameReplayUpdate`, `GameReplayResponse`, `GameReplayListResponse`
- Game Outcome: `GameOutcomeCreate`, `GameOutcomeResponse`, `GameOutcomeListResponse`
- User Statistics: `UserStatisticsResponse`, `StatisticsUpdateResponse`
- Group: `GroupCreate`, `GroupUpdate`, `GroupMemberCreate`, `GroupMemberUpdate`, `GroupMemberRemove`, `GroupResponse`, `GroupListResponse`, `GroupChatMessageCreate`, `GroupChatMessageResponse`, `GroupChatMessageListResponse`
- Network: `NetworkCreate`, `NetworkUpdate`, `NetworkMemberCreate`, `NetworkResponse`, `NetworkListResponse`
- Preference: `UserPreferenceUpdate`, `UserPreferenceResponse`
- Activity Log: `ActivityLogEntry`, `ActivityLogListResponse`
- Generic: `MessageResponse`, `CountResponse`, `ErrorDetail`
**Imports From:**
- `pydantic`: `BaseModel`, `Field`, `ConfigDict`
- `typing`: `Optional`, `List`, `Dict`, `Any`
- `datetime`: `datetime`
- `enum`: `Enum`
**Imported By:**
- `routers/user_data.py`
- `routers/user_data_router.py`
**Dependencies:** None (standalone)
---
#### 4.4 `user_deck_schemas.py` (217 lines)
**Purpose:** Deck building schemas
**Key Classes:**
- Enums: `DeckStatus`, `DeckZone`, `SuggestionType`
- Deck: `UserDeckCreate`, `UserDeckUpdate`, `UserDeckResponse`, `UserDeckListResponse`
- Deck Card: `DeckCardCreate`, `DeckCardUpdate`, `DeckCardResponse`, `DeckCardWithDetailsResponse`, `DeckCardListResponse`
- Precedent: `PrecedentCreate`, `PrecedentUpdate`, `PrecedentResponse`, `PrecedentListResponse`
- Suggestion: `SuggestionCreate`, `SuggestionResponse`, `SuggestionListResponse`
- Actions: `DeckFinalizeRequest`, `DeckFinalizeResponse`, `DeckDeleteResponse`
- Search: `CardSearchRequest`
**Imports From:**
- `pydantic`: `BaseModel`, `Field`, `ConfigDict`
- `typing`: `Optional`, `List`, `Dict`, `Any`
- `datetime`: `datetime`
- `enum`: `Enum`
**Imported By:**
- `routers/decks.py`
- `routers/decks_router.py`
- `routers/card_import.py`
**Dependencies:** None (standalone)
---
#### 4.5 `user_card_collection.py` (157 lines)
**Purpose:** Card collection schemas
**Key Classes:**
- Enums: `CardCondition`, `AcquisitionMethod`
- Collection: `CardCollectionCreate`, `CardCollectionUpdate`, `CardCollectionResponse`, `CardCollectionListResponse`
- Wishlist: `WishlistCreate`, `WishlistUpdate`, `WishlistResponse`, `WishlistListResponse`
- Statistics: `CollectionStatistics`, `CollectionSummaryResponse`
- Generic: `MessageResponse`, `CountResponse`, `ErrorDetail`
**Imports From:**
- `pydantic`: `BaseModel`, `Field`, `ConfigDict`
- `typing`: `Optional`, `List`, `Dict`, `Any`
- `datetime`: `datetime`
- `enum`: `Enum`
**Imported By:**
- `routers/user_data.py`
- `routers/user_data_router.py`
**Dependencies:** None (standalone)
---
#### 4.6 `card_import_schemas.py` (113 lines)
**Purpose:** Card import schemas
**Key Classes:**
- Import: `CardImportRequest`, `CardImportResponse`, `CardImportStatusResponse`
- Match: `CardMatchResult`, `CardImportSummary`
- Batch: `CardImportBatchCreate`, `CardImportBatchResponse`
- User Import: `UserCardImportCreate`, `UserCardImportResponse`, `UserCardImportRecordResponse`
**Imports From:**
- `pydantic`: `BaseModel`, `Field`, `ConfigDict`
- `typing`: `List`, `Optional`, `Dict`, `Any`
- `datetime`: `datetime`
**Imported By:**
- `routers/card_import.py`
- `routers/user_data_router.py`
**Dependencies:** None (standalone)
---
#### 4.7 `card_search_schemas.py` (108 lines)
**Purpose:** Card search and import schemas
**Key Classes:**
- Search: `CardResponse`, `SetResponse`, `CardTypeResponse`, `CardSearchResponse`
- Import: `CardImportResponse`, `CardImportStatusResponse`, `CardMatchResult`, `CardImportSummary`
- Generic: `MessageResponse`, `CountResponse`, `ErrorResponse`
**Imports From:**
- `pydantic`: `BaseModel`, `Field`, `ConfigDict`
- `typing`: `List`, `Optional`, `Dict`, `Any`
- `datetime`: `datetime`
**Imported By:**
- `routers/card_router.py`
- `routers/cards_router.py`
**Dependencies:** None (standalone)
---
#### 4.8 `mtg_card_schemas.py` (116 lines)
**Purpose:** MTG card schemas
**Key Classes:**
- Card: `MtgCardResponse`, `MtgCardSearchRequest`, `MtgCardSearchResponse`
- Set: `MtgSetResponse`
- Mirror: `MtgCardMirrorResponse`
- Link: `DeckCardLinkResponse`, `DeckWithCardsResponse`
**Imports From:**
- `pydantic`: `BaseModel`, `ConfigDict`
- `typing`: `Optional`, `List`
- `datetime`: `datetime`
**Imported By:**
- `routers/cards_router.py`
**Dependencies:** None (standalone)
---
#### 4.9 `game_schemas.py` (59 lines)
**Purpose:** Game schemas
**Key Classes:**
- Game: `GameCreate`, `GameResponse`, `GameJoinRequest`, `GameLeaveRequest`, `GameListResponse`, `GamePlayerResponse`, `GameStateResponse`
**Imports From:**
- `pydantic`: `BaseModel`, `Field`, `ConfigDict`
- `typing`: `Optional`, `List`
- `datetime`: `datetime`
**Imported By:**
- `routers/games_router.py`
**Dependencies:** None (standalone)
---
#### 4.10 `generic_schemas.py` (29 lines)
**Purpose:** Generic response schemas
**Key Classes:**
- `MessageResponse`, `CountResponse`, `ErrorResponse`, `ValidationErrorResponse`, `ErrorDetail`
**Imports From:**
- `pydantic`: `BaseModel`
- `typing`: `Optional`, `List`
**Imported By:**
- `routers/user_data.py`
- `routers/user_data_router.py`
**Dependencies:** None (standalone)
---
#### 4.11 `proto_messages.py` (133 lines)
**Purpose:** Protocol buffer message models
**Key Classes:**
- Base: `ProtoMessageBase`
- Commands: `SessionCommand`, `GameCommand`, `GameEvent`, `Response`
- Server Info: `ServerInfoUser`, `ServerInfoDeckStorageFile`, `ServerInfoDeckStorageFolder`, `ServerInfoDeckStorageTreeItem`, `ServerInfoCard`, `ServerInfoZone`, `ServerInfoGame`
**Imports From:**
- `pydantic`: `BaseModel`
- `typing`: `Optional`, `List`, `Dict`, `Any`
- `datetime`: `datetime`
**Imported By:**
- None (unused)
**Dependencies:** None (standalone)
**⚠️ ISSUE:** Protocol buffer messages defined but not used anywhere.
---
#### 4.12 `protocol_constants.py` (170 lines)
**Purpose:** MTG Online protocol constants
**Key Classes:**
- `SessionCommandType`: Session command types (PING, LOGIN, MESSAGE, etc.)
- `GameCommandType`: Game command types (KICK, LEAVE, SHUFFLE, etc.)
- `GameEventType`: Game event types (JOIN, LEAVE, DRAW_CARDS, etc.)
- `ResponseCode`: Response codes (RespOk, RespInvalidCommand, etc.)
- `ZoneType`: Zone types (PrivateZone, PublicZone, HiddenZone)
- `UserLevelFlag`: User level flags (IsUser, IsModerator, IsAdmin, etc.)
**Imports From:**
- `enum`: `IntEnum`, `IntFlag`
**Imported By:**
- None (unused)
**Dependencies:** None (standalone)
**⚠️ ISSUE:** Protocol constants defined but not used anywhere.
---
### 5. Services Layer (`app/services/`)
**Purpose:** Business logic and data processing
#### 5.1 `__init__.py` (60 lines)
**Purpose:** Exports all services
**Imports From:**
- `app.services.deck_parser`: `DeckParser`
- `app.services.card_database`: Various card database functions
- `app.services.card_mirror_service`: Mirror service functions
- `app.services.mtgjson_manager`: `MTGJSONManager`, `get_manager`
- `app.services.mtgjson_downloader`: Download functions
- `app.services.mtgjson_loader`: Loader functions
- `app.services.mtgjson_uploader`: Uploader functions
- `app.services.file_parser`: `FileParser`
- `app.services.fuzzy_card_matcher`: `FuzzyCardMatcher`
- `app.services.import_batch_processor`: `ImportBatchProcessor`
- `app.services.deck_manager`: `DeckManager`
- `app.services.card_search_service`: `CardSearchService`
- `app.services.deck_suggestion_service`: `DeckSuggestionService`
**Imported By:**
- Various routers
**Dependencies:** All service modules
---
#### 5.2 `card_database.py` (350 lines)
**Purpose:** MTG card database queries
**Key Functions:**
- `search_cards()`: Search cards by name/type/mana cost
- `get_card_by_name()`: Get card by name
- `get_cards_by_set()`: Get cards by set
- `get_card_types()`: Get all card types
- `get_card_rarities()`: Get all rarities
- `get_sets()`: Get all sets
- `get_set_by_code()`: Get set by code
- `get_card_statistics()`: Get database statistics
**Imports From:**
- `app.models.mtg_models`: `MtgCard`, `MtgSet`
- `app.core.database`: `mtg_get_db`
- `sqlalchemy`: `select`, `and_`, `or_`, `func`
- `sqlalchemy.orm`: `selectinload`
**Imported By:**
- `routers/card_router.py`
**Dependencies:** `models/mtg_models.py`, `core/database.py`
---
#### 5.3 `card_mirror_service.py` (359 lines)
**Purpose:** Card mirror service for fast deckbuilding queries
**Key Functions:**
- `upsert_card_mirror()`: Upsert card to mirror
- `get_card_mirror_by_name()`: Get mirror by name
- `search_card_mirrors()`: Search mirrors
- `add_card_to_deck()`: Add card to deck
- `remove_card_from_deck()`: Remove card from deck
- `get_deck_cards()`: Get deck cards
- `get_user_deck_summaries()`: Get user deck summaries
- `sync_mirrors_from_mtg_cards()`: Sync mirrors
- `get_card_statistics()`: Get mirror statistics
**Imports From:**
- `app.models.mtg_models`: `MtgCard`, `MtgSet`
- `app.models.mirror_models`: `MtgCardMirror`, `DeckCardLink`
- `app.core.database`: `mirror_get_db`
- `sqlalchemy`: `select`, `func`, `update`, `delete`
- `sqlalchemy.orm`: `selectinload`
**Imported By:**
- None (unused)
**Dependencies:** `models/mtg_models.py`, `core/database.py`
**⚠️ ISSUE:** Service defined but not used anywhere.
---
#### 5.4 `card_search_service.py` (190 lines)
**Purpose:** Card search with filters
**Key Classes/Functions:**
- `CardSearchService.search_cards()`: Search with filters
- `CardSearchService.get_card_by_id()`: Get by ID
- `CardSearchService.get_sets()`: Get all sets
- `CardSearchService.get_card_types()`: Get card types
- `CardSearchService.get_card_rarities()`: Get rarities
**Imports From:**
- `app.models.mtg_models`: `MtgCard`, `MtgSet`
- `app.models.mirror_models`: `MtgCardMirror`
- `sqlalchemy`: `select`, `or_`, `and_`
- `sqlalchemy.orm`: `selectinload`
**Imported By:**
- `routers/card_router.py`
- `routers/cards_router.py`
**Dependencies:** `models/mtg_models.py`, `sqlalchemy`
---
#### 5.5 `deck_manager.py` (246 lines)
**Purpose:** Deck CRUD operations
**Key Classes/Functions:**
- `DeckManager.create_deck()`: Create deck
- `DeckManager.get_deck()`: Get deck
- `DeckManager.list_decks()`: List decks
- `DeckManager.update_deck()`: Update deck
- `DeckManager.delete_deck()`: Delete deck
- `DeckManager.finalize_deck()`: Finalize deck
- `DeckManager.add_card_to_deck()`: Add card
- `DeckManager.get_deck_cards()`: Get deck cards
- `DeckManager.update_deck_card()`: Update card
- `DeckManager.remove_card_from_deck()`: Remove card
- `DeckManager.clone_precedent()`: Clone precedent
**Imports From:**
- `app.models.user_deck`: `UserDeck`, `UserDeckCard`, `DeckPrecedent`, `DeckPrecedentCard`
- `app.models.models`: `MtgonlineCard`
- `sqlalchemy`: `select`, `update`, `delete`
- `sqlalchemy.orm`: `selectinload`
**Imported By:**
- `routers/decks.py`
- `routers/decks_router.py`
**Dependencies:** `models/user_deck.py`, `models/models.py`, `sqlalchemy`
---
#### 5.6 `deck_parser.py` (206 lines)
**Purpose:** Deck list parsing and serialization
**Key Classes/Functions:**
- `CardInfo`: Dataclass for card information
- `DeckParser.parse_plain_text()`: Parse plain text
- `DeckParser.format_plain_text()`: Format as plain text
- `DeckParser.to_native_xml()`: Convert to XML
- `DeckParser.from_native_xml()`: Parse XML
- `parse_deck()`: Convenience function
- `format_deck()`: Convenience function
**Imports From:**
- `re`: Regex patterns
- `typing`: `List`, `Dict`, `Tuple`, `Optional`
- `dataclasses`: `dataclass`
**Imported By:**
- `routers/decks.py`
- `routers/decks_router.py`
**Dependencies:** None (standalone)
---
#### 5.7 `deck_suggestion_service.py` (209 lines)
**Purpose:** Card suggestion service
**Key Classes/Functions:**
- `DeckSuggestionService.suggest_cards()`: Suggest cards
- `DeckSuggestionService.add_suggestion()`: Add suggestion
- `DeckSuggestionService.get_deck_suggestions()`: Get suggestions
**Imports From:**
- `app.models.user_deck`: `UserDeck`, `UserDeckCard`, `CardSuggestion`
- `app.models.mtg_models`: `MtgCard`
- `app.models.mirror_models`: `MtgCardMirror`
- `app.services.fuzzy_card_matcher`: `FuzzyCardMatcher`
- `sqlalchemy`: `select`, `or_`, `and_`, `func`, `case`
- `sqlalchemy.orm`: `selectinload`
**Imported By:**
- `routers/card_router.py`
- `routers/cards_router.py`
**Dependencies:** `models/user_deck.py`, `models/mtg_models.py`, `services/fuzzy_card_matcher.py`, `sqlalchemy`
---
#### 5.8 `file_parser.py` (108 lines)
**Purpose:** File parser for card imports
**Key Classes/Functions:**
- `FileParser.parse_file()`: Parse file (CSV, JSON, XLSX, ODS)
- `FileParser._parse_csv()`: Parse CSV
- `FileParser._parse_json()`: Parse JSON
- `FileParser._parse_xlsx()`: Parse XLSX
- `FileParser._parse_ods()`: Parse ODS
**Imports From:**
- `csv`, `json`: Stdlib
- `pathlib`: `Path`
- `openpyxl`: XLSX parsing
- `pandas`: ODS parsing
**Imported By:**
- `routers/card_import.py`
- `routers/user_data_router.py`
**Dependencies:** `openpyxl`, `pandas`
---
#### 5.9 `fuzzy_card_matcher.py` (170 lines)
**Purpose:** Fuzzy matching for card names
**Key Classes/Functions:**
- `FuzzyCardMatcher.normalize_card_name()`: Normalize name
- `FuzzyCardMatcher.exact_match()`: Exact match
- `FuzzyCardMatcher.fuzzy_match()`: Fuzzy match
- `FuzzyCardMatcher.find_best_match()`: Find best match
- `FuzzyCardMatcher.batch_match()`: Batch match
- `FuzzyCardMatcher.batch_match_with_database()`: Match with DB
**Imports From:**
- `thefuzz`: `fuzz`
- `typing`: `List`, `Tuple`, `Optional`
**Imported By:**
- `routers/card_router.py`
- `routers/cards_router.py`
- `routers/card_import.py`
- `routers/user_data_router.py`
- `services/deck_suggestion_service.py`
- `services/import_batch_processor.py`
**Dependencies:** `thefuzz`
---
#### 5.10 `import_batch_processor.py` (204 lines)
**Purpose:** Batch import processing
**Key Classes/Functions:**
- `ImportBatchProcessor.create_batch()`: Create batch
- `ImportBatchProcessor.process_batch()`: Process batch
- `ImportBatchProcessor.get_batch_status()`: Get status
- `ImportBatchProcessor.get_batch_results()`: Get results
- `ImportBatchProcessor.confirm_batch()`: Confirm batch
- `ImportBatchProcessor.get_user_imports()`: Get user imports
- `ImportBatchProcessor.delete_batch()`: Delete batch
**Imports From:**
- `app.models.card_import_batch`: `CardImportBatch`
- `app.models.user_card_import_record`: `UserCardImportRecord`
- `app.models.user_data`: `UserCardCollection`
- `app.models.models`: `MtgonlineCard`
- `app.services.fuzzy_card_matcher`: `FuzzyCardMatcher`
- `sqlalchemy`: `select`, `update`, `insert`, `delete`
- `sqlalchemy.sql`: `func`
**Imported By:**
- `routers/card_import.py`
- `routers/user_data_router.py`
**Dependencies:** `models/card_import_batch.py`, `models/user_card_import_record.py`, `models/user_data.py`, `models/models.py`, `services/fuzzy_card_matcher.py`, `sqlalchemy`
---
#### 5.11 `game_server.py` (309 lines)
**Purpose:** WebSocket game server for real-time gameplay
**Key Classes/Functions:**
- `GameRoom`: Manages single game room
- `GameServer`: Manages all game rooms
- `game_websocket_endpoint()`: WebSocket endpoint
- `process_game_command()`: Process commands
**Imports From:**
- `asyncio`: `Lock`
- `json`: Serialization
- `typing`: `Dict`, `Set`, `Optional`
- `datetime`: `datetime`
- `fastapi`: `WebSocket`, `WebSocketDisconnect`
**Imported By:**
- `routers/games_router.py`
- `routers/ws.py`
**Dependencies:** `fastapi`
---
#### 5.12 `mtgjson_downloader.py` (137 lines)
**Purpose:** MTGJSON data downloader
**Key Functions:**
- `get_downloads_dir()`: Get downloads directory
- `download_file()`: Download single file
- `download_all_files()`: Download all files
- `verify_downloads()`: Verify downloads
- `get_file_list()`: Get file list
**Imports From:**
- `gzip`, `json`, `os`, `sys`: Stdlib
- `pathlib`: `Path`
- `urllib.request`: `urlretrieve`
- `urllib.parse`: `urljoin`
**Imported By:**
- `services/__init__.py`
**Dependencies:** None (standalone)
---
#### 5.13 `mtgjson_loader.py` (838 lines)
**Purpose:** MTGJSON data loader (legacy)
**Key Functions:**
- `create_tables()`: Create tables
- `download_file()`: Download file
- `extract_zip()`: Extract zip
- `get_all_printings_psql_file()`: Get PSQL file
- `parse_psql_file()`: Parse PSQL
- `import_cards()`: Import cards
- `import_all_printings_psql()`: Import AllPrintings
- `import_all_set_files()`: Import set files
- `import_all_identifiers()`: Import identifiers
- `import_all_deck_files()`: Import deck files
- `import_simple_json()`: Import JSON
- `create_indexes()`: Create indexes
- `show_summary()`: Show summary
- `main()`: Main function
**Imports From:**
- `app.core.settings`: `get_settings`
- `sqlalchemy.ext.asyncio`: `create_async_engine`, `AsyncSession`
- `sqlalchemy.orm`: `sessionmaker`
- `sqlalchemy`: `text`, `insert`, `update`, `select`, `Table`, `Column`, etc.
- `sqlalchemy.dialects.postgresql`: `insert as pg_insert`
**Imported By:**
- `services/__init__.py`
**Dependencies:** `core/settings.py`, `sqlalchemy`
---
#### 5.14 `mtgjson_manager.py` (1296 lines)
**Purpose:** MTGJSON data manager (local file processing)
**Key Classes/Functions:**
- `MTGJSONManager`: Main manager class
- `get_last_refresh()`: Get last refresh
- `get_health_status()`: Get health status
- `check_required_files()`: Check files
- `upsert_all()`: Upsert all data
- `_upsert_sets()`: Upsert sets
- `_upsert_cards()`: Upsert cards
- `_upsert_cards_from_psql()`: Upsert from PSQL
- `_upsert_identifiers()`: Upsert identifiers
- `_upsert_card_types()`: Upsert card types
- `_upsert_keywords()`: Upsert keywords
- `_upsert_set_list()`: Upsert set list
- `_upsert_deck_list()`: Upsert deck list
- `_upsert_deck_files_from_zip()`: Upsert deck files
- `log_refresh()`: Log refresh
- `sync_mirrors()`: Sync mirrors
- `run_refresh()`: Run refresh
- `download_files()`: Download files
- `verify_files()`: Verify files
- `download_and_refresh()`: Download and refresh
- `get_manager()`: Get manager instance
**Imports From:**
- `app.core.database`: `mtg_async_session`
- `json`, `logging`, `zipfile`, `tempfile`, `shutil`: Stdlib
- `pathlib`: `Path`
- `datetime`: `datetime`
- `typing`: `Optional`, `Dict`, `List`, `Tuple`
- `re`: Regex
- `aiohttp`: `ClientSession`, `ClientTimeout`
- `asyncio`: `sleep`
- `gzip`: Decompression
- `sqlalchemy`: `text`
**Imported By:**
- `routers/games_router.py`
- `routers/refresh.py`
- `services/__init__.py`
**Dependencies:** `core/database.py`, `aiohttp`, `sqlalchemy`
---
#### 5.15 `mtgjson_uploader.py` (883 lines)
**Purpose:** MTGJSON data uploader (legacy)
**Key Functions:**
- `create_tables()`: Create tables
- `import_all_printings_psql()`: Import PSQL
- `import_all_set_files()`: Import set files
- `import_all_identifiers()`: Import identifiers
- `import_all_deck_files()`: Import deck files
- `import_json_files()`: Import JSON
- `download_file()`: Download file
- `extract_zip()`: Extract zip
- `main()`: Main function
**Imports From:**
- `app.core.settings`: `get_settings`
- `sqlalchemy.ext.asyncio`: `create_async_engine`, `AsyncSession`
- `sqlalchemy.orm`: `sessionmaker`
- `sqlalchemy`: `text`, `insert`, `update`, `select`, `and_`, `Table`, `MetaData`, `Column`, etc.
- `sqlalchemy.dialects.postgresql`: `insert as pg_insert`
- `gzip`, `json`, `os`, `sys`, `zipfile`: Stdlib
- `pathlib`: `Path`
- `datetime`: `datetime`
- `typing`: `Any`
- `urllib.request`: `urlretrieve`
**Imported By:**
- `services/__init__.py`
**Dependencies:** `core/settings.py`, `sqlalchemy`
---
## Dependency Graph Summary
```
┌─────────────────────────────────────────────────────────────────────────┐
│ DEPENDENCY GRAPH │
└─────────────────────────────────────────────────────────────────────────┘
core/
├── config.py (standalone)
│ └── imported by: database.py, security.py, settings.py
│
├── database.py
│ ├── imports: config.py
│ └── imported by: models/, routers/*, services/*
│
├── logging.py (standalone)
│ └── imported by: services/*
│
├── logging_config.py (DUPLICATE of logging.py)
│
├── redis_client.py
│ ├── imports: settings.py
│ └── imported by: routers/card_router.py, cards_router.py, interactions.py
│
├── security.py
│ ├── imports: config.py
│ └── imported by: routers/auth.py, auth_router.py, users.py, users_router.py, admin.py, rooms.py, games_router.py
│
└── settings.py
├── imports: config.py
└── imported by: redis_client.py, services/mtgjson_loader.py, services/mtgjson_uploader.py
models/
├── models.py
│ ├── imports: database.py
│ └── imported by: models/__init__.py, routers/*, services/*
│
├── mtg_models.py
│ ├── imports: database.py
│ └── imported by: models/__init__.py, routers/card_router.py, cards_router.py, services/card_search_service.py, services/deck_suggestion_service.py
│
└── __init__.py
├── imports: models.py, mtg_models.py
└── imported by: routers/*, services/*
routers/
├── auth.py (OLD - superseded by auth_router.py)
├── auth_router.py
│ ├── imports: database.py, security.py, models.py, schemas.py
│ └── imported by: app/main.py
│
├── users.py (OLD - superseded by users_router.py)
├── users_router.py
│ ├── imports: database.py, security.py, models.py, schemas.py
│ └── imported by: app/main.py
│
├── decks.py (OLD - superseded by decks_router.py)
├── decks_router.py
│ ├── imports: database.py, security.py, models.py, models/mtg_models.py, models/user_deck.py, services/deck_manager.py, services/deck_parser.py
│ └── imported by: app/main.py
│
├── card_router.py (OLD - superseded by cards_router.py)
├── cards_router.py
│ ├── imports: database.py, redis_client.py, models.py, models/user_deck.py, schemas/card_search_schemas.py, services/card_search_service.py, services/deck_suggestion_service.py, services/fuzzy_card_matcher.py
│ └── imported by: app/main.py
│
├── interactions.py (OLD - superseded by cards_router.py)
├── user_data.py (OLD - superseded by user_data_router.py)
├── user_data_router.py
│ ├── imports: database.py, security.py, models.py, models/user_data.py, models/card_import_batch.py, models/user_card_import_record.py, models/user_deck.py, services/file_parser.py, services/import_batch_processor.py, services/fuzzy_card_matcher.py, schemas/*
│ └── imported by: app/main.py
│
├── rooms.py (OLD - superseded by games_router.py)
├── games/__init__.py
├── games/router.py (OLD - superseded by games_router.py)
├── games_router.py
│ ├── imports: database.py, security.py, models.py, schemas.py, services/mtgjson_manager.py, services/game_server.py
│ └── imported by: app/main.py
│
├── refresh.py (OLD - superseded by games_router.py)
├── card_import.py (OLD - superseded by user_data_router.py)
├── ws.py (OLD - superseded by games_router.py)
│
└── __init__.py
├── imports: auth_router.py, users_router.py, decks_router.py, cards_router.py, user_data_router.py, games_router.py
└── imported by: app/main.py
schemas/
├── schemas.py (standalone)
│ └── imported by: routers/auth.py, auth_router.py, users.py, users_router.py, rooms.py, games_router.py
│
├── user_data_schemas.py (standalone)
│ └── imported by: routers/user_data.py, user_data_router.py
│
├── user_deck_schemas.py (standalone)
│ └── imported by: routers/decks.py, decks_router.py, card_import.py
│
├── user_card_collection.py (standalone)
│ └── imported by: routers/user_data.py, user_data_router.py
│
├── card_import_schemas.py (standalone)
│ └── imported by: routers/card_import.py, user_data_router.py
│
├── card_search_schemas.py (standalone)
│ └── imported by: routers/card_router.py, cards_router.py
│
├── mtg_card_schemas.py (standalone)
│ └── imported by: routers/cards_router.py
│
├── game_schemas.py (standalone)
│ └── imported by: routers/games_router.py
│
├── generic_schemas.py (standalone)
│ └── imported by: routers/user_data.py, user_data_router.py
│
├── proto_messages.py (UNUSED)
├── protocol_constants.py (UNUSED)
│
└── __init__.py
├── imports: all schema files
└── imported by: routers/*, services/*
services/
├── card_database.py
│ ├── imports: models/mtg_models.py, core/database.py
│ └── imported by: routers/card_router.py
│
├── card_mirror_service.py (UNUSED)
│ ├── imports: models/mtg_models.py, models/mirror_models.py, core/database.py
│
├── card_search_service.py
│ ├── imports: models/mtg_models.py, models/mirror_models.py
│ └── imported by: routers/card_router.py, cards_router.py
│
├── deck_manager.py
│ ├── imports: models/user_deck.py, models/models.py
│ └── imported by: routers/decks.py, decks_router.py
│
├── deck_parser.py (standalone)
│ └── imported by: routers/decks.py, decks_router.py
│
├── deck_suggestion_service.py
│ ├── imports: models/user_deck.py, models/mtg_models.py, models/mirror_models.py, services/fuzzy_card_matcher.py
│ └── imported by: routers/card_router.py, cards_router.py
│
├── file_parser.py
│ ├── imports: csv, json, pathlib, openpyxl, pandas
│ └── imported by: routers/card_import.py, user_data_router.py
│
├── fuzzy_card_matcher.py
│ ├── imports: thefuzz
│ └── imported by: routers/card_router.py, cards_router.py, card_import.py, user_data_router.py, services/deck_suggestion_service.py, services/import_batch_processor.py
│
├── import_batch_processor.py
│ ├── imports: models/card_import_batch.py, models/user_card_import_record.py, models/user_data.py, models/models.py, services/fuzzy_card_matcher.py
│ └── imported by: routers/card_import.py, user_data_router.py
│
├── game_server.py
│ ├── imports: asyncio, json, typing, datetime, fastapi
│ └── imported by: routers/games_router.py, routers/ws.py
│
├── mtgjson_downloader.py (standalone)
│ └── imported by: services/__init__.py
│
├── mtgjson_loader.py (LEGACY)
│ ├── imports: core/settings.py, sqlalchemy
│ └── imported by: services/__init__.py
│
├── mtgjson_manager.py
│ ├── imports: core/database.py, aiohttp, sqlalchemy
│ └── imported by: routers/games_router.py, routers/refresh.py, services/__init__.py
│
├── mtgjson_uploader.py (LEGACY)
│ ├── imports: core/settings.py, sqlalchemy
│ └── imported by: services/__init__.py
│
└── __init__.py
├── imports: all service modules
└── imported by: routers/*
```
---
## Critical Issues Summary
### 1. **Router Duplication (HIGH PRIORITY)**
Every major feature has both old and new implementations:
- `auth.py` vs `auth_router.py`
- `users.py` vs `users_router.py`
- `decks.py` vs `decks_router.py`
- `card_router.py` vs `cards_router.py`
- `interactions.py` vs `cards_router.py` (interactions)
- `user_data.py` vs `user_data_router.py`
- `rooms.py` vs `games_router.py` (rooms)
- `games/router.py` vs `games_router.py` (games)
- `refresh.py` vs `games_router.py` (refresh)
- `card_import.py` vs `user_data_router.py` (import)
- `ws.py` vs `games_router.py` (websocket)
**Recommendation:** Remove all old versions and keep only the `_router.py` versions.
### 2. **Logging Duplication (MEDIUM PRIORITY)**
- `logging.py` and `logging_config.py` are exact duplicates
**Recommendation:** Remove `logging_config.py`.
### 3. **Settings Shim (LOW PRIORITY)**
- `settings.py` is just a backward compatibility shim for `config.py`
**Recommendation:** Remove once all callers migrate to `config.py`.
### 4. **Unused Code (LOW PRIORITY)**
- `services/card_mirror_service.py`: Defined but not used
- `schemas/proto_messages.py`: Protocol buffer messages defined but not used
- `schemas/protocol_constants.py`: Protocol constants defined but not used
**Recommendation:** Remove or implement if needed.
### 5. **Model Inconsistency (MEDIUM PRIORITY)**
- Multiple model files with overlapping functionality
- `models/user_deck.py`, `models/user_data.py`, `models/card_import_batch.py`, etc. exist but `models.py` consolidates many of these
**Recommendation:** Consolidate all models into `models.py` and `mtg_models.py`.
### 6. **Service Redundancy (MEDIUM PRIORITY)**
- `mtgjson_loader.py` and `mtgjson_uploader.py` are legacy scripts
- `mtgjson_manager.py` is the current implementation
**Recommendation:** Remove legacy scripts.
---
## Recommended Architecture Simplification
### Phase 1: Remove Duplicates
1. Remove old router files (auth.py, users.py, decks.py, card_router.py, interactions.py, user_data.py, rooms.py, games/router.py, refresh.py, card_import.py, ws.py)
2. Remove `logging_config.py`
3. Remove `settings.py` (migrate callers to `config.py`)
### Phase 2: Consolidate Models
1. Move all models from `user_deck.py`, `user_data.py`, `card_import_batch.py`, etc. into `models.py`
2. Remove individual model files
### Phase 3: Clean Up Services
1. Remove `card_mirror_service.py` (unused)
2. Remove `mtgjson_loader.py` and `mtgjson_uploader.py` (legacy)
3. Remove `proto_messages.py` and `protocol_constants.py` (unused)
### Phase 4: Standardize Patterns
1. Ensure all routers use consistent patterns
2. Ensure all services use consistent patterns (ORM vs raw SQL)
3. Add type hints and docstrings where missing
---
## Statistics
- **Total Files Analyzed:** 50+
- **Total Lines of Code:** ~30,000+
- **Old Router Files (to remove):** 11
- **New Router Files (to keep):** 6
- **Duplicate Files:** 2 (logging, settings)
- **Unused Files:** 4 (card_mirror_service, proto_messages, protocol_constants, mtgjson_loader/uploader)
---
## Conclusion
The MTG Online backend has significant architectural debt with multiple layers of duplication. The codebase is in a transition state where new consolidated routers and services have been created but old versions still exist.
**Immediate Actions:**
1. Remove all old router files (11 files)
2. Remove duplicate logging config
3. Remove unused schema files
4. Consolidate models
**Long-term:**
1. Standardize on ORM or raw SQL (currently mixed)
2. Add comprehensive tests
3. Improve error handling
4. Add proper logging throughout
The project is functional but needs cleanup to be maintainable.