# Prompt: Database & Models Implementation (T06-T11) ## ๐ŸŽฏ OBIETTIVO Implementare la fase **Database & Models** del progetto OpenRouter API Key Monitor seguendo rigorosamente TDD (Test-Driven Development). **Task da completare:** T06, T07, T08, T09, T10, T11 --- ## ๐Ÿ“‹ CONTESTO - **Repository:** `/home/google/Sources/LucaSacchiNet/openrouter-watcher` - **Specifiche:** `/home/google/Sources/LucaSacchiNet/openrouter-watcher/export/architecture.md` - **Kanban:** `/home/google/Sources/LucaSacchiNet/openrouter-watcher/export/kanban.md` - **Stato Attuale:** Setup completato (T01-T05), 59 test passanti --- ## ๐Ÿ—„๏ธ SCHEMA DATABASE (Da architecture.md) ### Tabelle #### 1. users ```sql CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, email VARCHAR(255) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, is_active BOOLEAN DEFAULT 1, CONSTRAINT chk_email_format CHECK (email LIKE '%_@__%.__%') ); ``` #### 2. api_keys ```sql CREATE TABLE api_keys ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, name VARCHAR(100) NOT NULL, key_encrypted TEXT NOT NULL, -- AES-256-GCM encrypted is_active BOOLEAN DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, last_used_at TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); ``` #### 3. usage_stats ```sql CREATE TABLE usage_stats ( id INTEGER PRIMARY KEY AUTOINCREMENT, api_key_id INTEGER NOT NULL, date DATE NOT NULL, model VARCHAR(100) NOT NULL, requests_count INTEGER DEFAULT 0, tokens_input INTEGER DEFAULT 0, tokens_output INTEGER DEFAULT 0, cost DECIMAL(10, 6) DEFAULT 0.0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (api_key_id) REFERENCES api_keys(id) ON DELETE CASCADE, CONSTRAINT uniq_key_date_model UNIQUE (api_key_id, date, model) ); ``` #### 4. api_tokens ```sql CREATE TABLE api_tokens ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, token_hash VARCHAR(255) NOT NULL, name VARCHAR(100) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, last_used_at TIMESTAMP, is_active BOOLEAN DEFAULT 1, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); ``` --- ## ๐Ÿ”ง TASK DETTAGLIATI ### T06: Creare database.py (connection & session) **Requisiti:** - Creare `src/openrouter_monitor/database.py` - Implementare SQLAlchemy engine con SQLite - Configurare session maker con expire_on_commit=False - Implementare funzione `get_db()` per dependency injection FastAPI - Implementare `init_db()` per creazione tabelle - Usare `check_same_thread=False` per SQLite **Test richiesti:** - Test connessione database - Test creazione engine - Test session creation - Test init_db crea tabelle --- ### T07: Creare model User (SQLAlchemy) **Requisiti:** - Creare `src/openrouter_monitor/models/user.py` - Implementare class `User` con tutti i campi - Configurare relationships con ApiKey e ApiToken - Implementare `check_email_format` constraint - Campi: id, email, password_hash, created_at, updated_at, is_active - Index su email **Test richiesti:** - Test creazione utente - Test vincolo email unique - Test validazione email format - Test relationship con api_keys - Test relationship con api_tokens --- ### T08: Creare model ApiKey (SQLAlchemy) **Requisiti:** - Creare `src/openrouter_monitor/models/api_key.py` - Implementare class `ApiKey` - Configurare relationship con User e UsageStats - Foreign key su user_id con ON DELETE CASCADE - Campi: id, user_id, name, key_encrypted, is_active, created_at, last_used_at - Index su user_id e is_active **Test richiesti:** - Test creazione API key - Test relationship con user - Test relationship con usage_stats - Test cascade delete --- ### T09: Creare model UsageStats (SQLAlchemy) **Requisiti:** - Creare `src/openrouter_monitor/models/usage_stats.py` - Implementare class `UsageStats` - Configurare relationship con ApiKey - Unique constraint: (api_key_id, date, model) - Campi: id, api_key_id, date, model, requests_count, tokens_input, tokens_output, cost, created_at - Index su api_key_id, date, model - Usare Numeric(10, 6) per cost **Test richiesti:** - Test creazione usage stats - Test unique constraint - Test relationship con api_key - Test valori default (0) --- ### T10: Creare model ApiToken (SQLAlchemy) **Requisiti:** - Creare `src/openrouter_monitor/models/api_token.py` - Implementare class `ApiToken` - Configurare relationship con User - Foreign key su user_id con ON DELETE CASCADE - Campi: id, user_id, token_hash, name, created_at, last_used_at, is_active - Index su user_id, token_hash, is_active **Test richiesti:** - Test creazione API token - Test relationship con user - Test cascade delete --- ### T11: Setup Alembic e migrazione iniziale **Requisiti:** - Inizializzare Alembic: `alembic init alembic` - Configurare `alembic.ini` con DATABASE_URL - Configurare `alembic/env.py` con Base metadata - Creare migrazione iniziale che crea tutte le tabelle - Migrazione deve includere indici e constraints - Testare upgrade/downgrade **Test richiesti:** - Test alembic init - Test creazione migration file - Test upgrade applica cambiamenti - Test downgrade rimuove cambiamenti - Test tutte le tabelle create correttamente --- ## ๐Ÿ”„ WORKFLOW TDD OBBLIGATORIO Per OGNI task (T06-T11): ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ 1. RED - Scrivi il test che fallisce โ”‚ โ”‚ โ€ข Test prima del codice โ”‚ โ”‚ โ€ข Pattern AAA (Arrange-Act-Assert) โ”‚ โ”‚ โ€ข Nomi descrittivi โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ†“ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ 2. GREEN - Implementa codice minimo โ”‚ โ”‚ โ€ข Solo codice necessario per test โ”‚ โ”‚ โ€ข Nessun refactoring ancora โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ†“ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ 3. REFACTOR - Migliora il codice โ”‚ โ”‚ โ€ข Pulisci duplicazioni โ”‚ โ”‚ โ€ข Migliora nomi variabili โ”‚ โ”‚ โ€ข Test rimangono verdi โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` --- ## ๐Ÿ“ STRUTTURA FILE DA CREARE ``` src/openrouter_monitor/ โ”œโ”€โ”€ database.py # T06 โ””โ”€โ”€ models/ โ”œโ”€โ”€ __init__.py # Esporta tutti i modelli โ”œโ”€โ”€ user.py # T07 โ”œโ”€โ”€ api_key.py # T08 โ”œโ”€โ”€ usage_stats.py # T09 โ””โ”€โ”€ api_token.py # T10 alembic/ โ”œโ”€โ”€ alembic.ini # Configurazione โ”œโ”€โ”€ env.py # Configurato con metadata โ””โ”€โ”€ versions/ โ””โ”€โ”€ 001_initial_schema.py # T11 - Migrazione iniziale tests/unit/models/ โ”œโ”€โ”€ test_database.py # Test T06 โ”œโ”€โ”€ test_user_model.py # Test T07 โ”œโ”€โ”€ test_api_key_model.py # Test T08 โ”œโ”€โ”€ test_usage_stats_model.py # Test T09 โ”œโ”€โ”€ test_api_token_model.py # Test T10 โ””โ”€โ”€ test_migrations.py # Test T11 ``` --- ## ๐Ÿงช REQUISITI TEST ### Pattern AAA (Arrange-Act-Assert) ```python @pytest.mark.unit async def test_create_user_valid_email_succeeds(): # Arrange email = "test@example.com" password_hash = "hashed_password" # Act user = User(email=email, password_hash=password_hash) # Assert assert user.email == email assert user.password_hash == password_hash assert user.is_active is True assert user.created_at is not None ``` ### Marker Pytest ```python @pytest.mark.unit # Logica pura @pytest.mark.integration # Con database @pytest.mark.asyncio # Funzioni async ``` ### Fixtures Condivise (in conftest.py) ```python @pytest.fixture def db_session(): # Sessione database per test @pytest.fixture def sample_user(): # Utente di esempio @pytest.fixture def sample_api_key(): # API key di esempio ``` --- ## ๐Ÿ›ก๏ธ VINCOLI TECNICI ### SQLAlchemy Configuration ```python from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, Session Base = declarative_base() engine = create_engine( DATABASE_URL, connect_args={"check_same_thread": False} # Solo per SQLite ) SessionLocal = sessionmaker( autocommit=False, autoflush=False, bind=engine, expire_on_commit=False ) ``` ### Model Base Requirements - Tutti i modelli ereditano da `Base` - Usare type hints - Configurare `__tablename__` - Definire relationships esplicite - Usare `ondelete="CASCADE"` per FK ### Alembic Requirements - Importare `Base` da models in env.py - Configurare `target_metadata = Base.metadata` - Generare migration: `alembic revision --autogenerate -m "initial schema"` --- ## ๐Ÿ“Š AGGIORNAMENTO PROGRESS Dopo ogni task completato, aggiorna: `/home/google/Sources/LucaSacchiNet/openrouter-watcher/export/progress.md` Esempio: ```markdown ### ๐Ÿ—„๏ธ Database & Models (T06-T11) - [x] T06: Creare database.py - Completato [timestamp] - [x] T07: Creare model User - Completato [timestamp] - [ ] T08: Creare model ApiKey - In progress - [ ] T09: Creare model UsageStats - [ ] T10: Creare model ApiToken - [ ] T11: Setup Alembic e migrazione **Progresso sezione:** 33% (2/6 task) ``` --- ## โœ… CRITERI DI ACCETTAZIONE - [ ] T06: database.py con engine, session, get_db(), init_db() - [ ] T07: Model User completo con relationships e constraints - [ ] T08: Model ApiKey completo con relationships - [ ] T09: Model UsageStats con unique constraint e defaults - [ ] T10: Model ApiToken completo con relationships - [ ] T11: Alembic inizializzato con migrazione funzionante - [ ] Tutti i test passano (`pytest tests/unit/models/`) - [ ] Coverage >= 90% - [ ] 6 commit atomici (uno per task) - [ ] progress.md aggiornato con tutti i task completati --- ## ๐Ÿš€ COMANDO DI VERIFICA Al termine, esegui: ```bash cd /home/google/Sources/LucaSacchiNet/openrouter-watcher pytest tests/unit/models/ -v --cov=src/openrouter_monitor/models alembic upgrade head alembic downgrade -1 alembic upgrade head ``` --- ## ๐Ÿ“ NOTE - Usa SEMPRE path assoluti: `/home/google/Sources/LucaSacchiNet/openrouter-watcher/` - Segui le convenzioni in `.opencode/agents/tdd-developer.md` - Task devono essere verificabili in < 2 ore - Documenta bug complessi in `/docs/bug_ledger.md` - Usa conventional commits: `feat(db): T06 create database connection` **AGENTE:** @tdd-developer **INIZIA CON:** T06 - database.py