Spaces:
Sleeping
Sleeping
| """ | |
| SQLite database layer for Projects feature. | |
| Manages projects and validation history. | |
| """ | |
| import sqlite3 | |
| import json | |
| import logging | |
| import os | |
| from datetime import datetime | |
| from pathlib import Path | |
| from typing import List, Dict, Optional, Any | |
| logger = logging.getLogger(__name__) | |
| def get_database_path(): | |
| """Get writable database path based on environment.""" | |
| # Check if running on Hugging Face Spaces | |
| if os.environ.get("SPACE_ID"): | |
| # Use /tmp for HF Spaces (writable directory) | |
| db_dir = "/tmp" | |
| else: | |
| # Local development - use current directory | |
| db_dir = "." | |
| return os.path.join(db_dir, "projects.db") | |
| class ProjectDatabase: | |
| """Database manager for projects and validations.""" | |
| def __init__(self, db_path: str = None): | |
| """Initialize database connection and create tables if needed.""" | |
| self.db_path = db_path or get_database_path() | |
| self._init_database() | |
| def _init_database(self): | |
| """Create tables if they don't exist.""" | |
| with sqlite3.connect(self.db_path) as conn: | |
| cursor = conn.cursor() | |
| # Projects table | |
| cursor.execute(""" | |
| CREATE TABLE IF NOT EXISTS projects ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| name TEXT NOT NULL UNIQUE, | |
| description TEXT, | |
| created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP | |
| ) | |
| """) | |
| # Validations table | |
| cursor.execute(""" | |
| CREATE TABLE IF NOT EXISTS validations ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| project_id INTEGER, | |
| validation_type TEXT NOT NULL, | |
| template_key TEXT, | |
| filename TEXT, | |
| status TEXT, | |
| created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | |
| results_json TEXT, | |
| FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE | |
| ) | |
| """) | |
| conn.commit() | |
| logger.info(f"Database initialized at {self.db_path}") | |
| # Project CRUD operations | |
| def create_project(self, name: str, description: str = "") -> int: | |
| """Create a new project.""" | |
| with sqlite3.connect(self.db_path) as conn: | |
| cursor = conn.cursor() | |
| try: | |
| cursor.execute( | |
| "INSERT INTO projects (name, description) VALUES (?, ?)", | |
| (name, description) | |
| ) | |
| conn.commit() | |
| project_id = cursor.lastrowid | |
| logger.info(f"Created project: {name} (ID: {project_id})") | |
| return project_id | |
| except sqlite3.IntegrityError: | |
| raise ValueError(f"Project '{name}' already exists") | |
| def list_projects(self) -> List[Dict[str, Any]]: | |
| """List all projects.""" | |
| with sqlite3.connect(self.db_path) as conn: | |
| conn.row_factory = sqlite3.Row | |
| cursor = conn.cursor() | |
| cursor.execute(""" | |
| SELECT p.*, COUNT(v.id) as validation_count | |
| FROM projects p | |
| LEFT JOIN validations v ON p.id = v.project_id | |
| GROUP BY p.id | |
| ORDER BY p.created_at DESC | |
| """) | |
| projects = [dict(row) for row in cursor.fetchall()] | |
| return projects | |
| def get_project(self, project_id: int) -> Optional[Dict[str, Any]]: | |
| """Get a specific project by ID.""" | |
| with sqlite3.connect(self.db_path) as conn: | |
| conn.row_factory = sqlite3.Row | |
| cursor = conn.cursor() | |
| cursor.execute(""" | |
| SELECT p.*, COUNT(v.id) as validation_count | |
| FROM projects p | |
| LEFT JOIN validations v ON p.id = v.project_id | |
| WHERE p.id = ? | |
| GROUP BY p.id | |
| """, (project_id,)) | |
| row = cursor.fetchone() | |
| return dict(row) if row else None | |
| def delete_project(self, project_id: int) -> bool: | |
| """Delete a project and all associated validations.""" | |
| with sqlite3.connect(self.db_path) as conn: | |
| cursor = conn.cursor() | |
| cursor.execute("DELETE FROM projects WHERE id = ?", (project_id,)) | |
| conn.commit() | |
| deleted = cursor.rowcount > 0 | |
| if deleted: | |
| logger.info(f"Deleted project ID: {project_id}") | |
| return deleted | |
| # Validation operations | |
| def save_validation( | |
| self, | |
| project_id: Optional[int], | |
| validation_type: str, | |
| filename: str, | |
| status: str, | |
| results: Dict[str, Any], | |
| template_key: Optional[str] = None | |
| ) -> int: | |
| """Save a validation result to a project.""" | |
| with sqlite3.connect(self.db_path) as conn: | |
| cursor = conn.cursor() | |
| cursor.execute(""" | |
| INSERT INTO validations | |
| (project_id, validation_type, template_key, filename, status, results_json) | |
| VALUES (?, ?, ?, ?, ?, ?) | |
| """, ( | |
| project_id, | |
| validation_type, | |
| template_key, | |
| filename, | |
| status, | |
| json.dumps(results) | |
| )) | |
| conn.commit() | |
| validation_id = cursor.lastrowid | |
| logger.info(f"Saved {validation_type} validation (ID: {validation_id}) to project {project_id}") | |
| return validation_id | |
| def get_project_validations(self, project_id: int) -> List[Dict[str, Any]]: | |
| """Get all validations for a specific project.""" | |
| with sqlite3.connect(self.db_path) as conn: | |
| conn.row_factory = sqlite3.Row | |
| cursor = conn.cursor() | |
| cursor.execute(""" | |
| SELECT * FROM validations | |
| WHERE project_id = ? | |
| ORDER BY created_at DESC | |
| """, (project_id,)) | |
| validations = [] | |
| for row in cursor.fetchall(): | |
| validation = dict(row) | |
| # Parse JSON results | |
| if validation['results_json']: | |
| validation['results'] = json.loads(validation['results_json']) | |
| del validation['results_json'] | |
| validations.append(validation) | |
| return validations | |
| def get_recent_validations(self, limit: int = 50) -> List[Dict[str, Any]]: | |
| """Get recent validations across all projects.""" | |
| with sqlite3.connect(self.db_path) as conn: | |
| conn.row_factory = sqlite3.Row | |
| cursor = conn.cursor() | |
| cursor.execute(""" | |
| SELECT v.*, p.name as project_name | |
| FROM validations v | |
| LEFT JOIN projects p ON v.project_id = p.id | |
| ORDER BY v.created_at DESC | |
| LIMIT ? | |
| """, (limit,)) | |
| validations = [] | |
| for row in cursor.fetchall(): | |
| validation = dict(row) | |
| if validation['results_json']: | |
| validation['results'] = json.loads(validation['results_json']) | |
| del validation['results_json'] | |
| validations.append(validation) | |
| return validations | |
| # Global database instance | |
| db = ProjectDatabase() | |