Spaces:
Running
Running
File size: 7,483 Bytes
6ec2d12 306fa3b 6ec2d12 306fa3b 6ec2d12 306fa3b 6ec2d12 306fa3b 6ec2d12 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
"""
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()
|