- 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
1669 lines
51 KiB
Markdown
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.
|