temp / instructor /database.py
CheeksTheGeek's picture
Initial commit: LLM Code Deployment System
c5292d8 unverified
Raw
History Blame Contribute Delete
9.49 kB
"""Database models and operations for instructor system."""
import json
from datetime import datetime
from typing import Any
from sqlalchemy import (
Column,
DateTime,
Float,
Integer,
String,
Text,
create_engine,
)
from sqlalchemy.orm import declarative_base, sessionmaker
from shared.config import settings
from shared.logger import setup_logger
logger = setup_logger(__name__)
Base = declarative_base()
class Task(Base):
"""Task records sent to students."""
__tablename__ = "tasks"
id = Column(Integer, primary_key=True)
timestamp = Column(DateTime, default=datetime.utcnow, nullable=False)
email = Column(String(255), nullable=False, index=True)
task = Column(String(255), nullable=False, index=True)
round = Column(Integer, nullable=False)
nonce = Column(String(255), nullable=False, unique=True)
brief = Column(Text, nullable=False)
attachments = Column(Text, nullable=False) # JSON serialized
checks = Column(Text, nullable=False) # JSON serialized
evaluation_url = Column(String(512), nullable=False)
endpoint = Column(String(512), nullable=False)
statuscode = Column(Integer, nullable=True)
secret = Column(String(255), nullable=False)
def to_dict(self) -> dict[str, Any]:
"""Convert to dictionary."""
return {
"id": self.id,
"timestamp": self.timestamp.isoformat() if self.timestamp else None,
"email": self.email,
"task": self.task,
"round": self.round,
"nonce": self.nonce,
"brief": self.brief,
"attachments": json.loads(self.attachments) if self.attachments else [],
"checks": json.loads(self.checks) if self.checks else [],
"evaluation_url": self.evaluation_url,
"endpoint": self.endpoint,
"statuscode": self.statuscode,
}
class Repo(Base):
"""Repository submissions from students."""
__tablename__ = "repos"
id = Column(Integer, primary_key=True)
timestamp = Column(DateTime, default=datetime.utcnow, nullable=False)
email = Column(String(255), nullable=False, index=True)
task = Column(String(255), nullable=False, index=True)
round = Column(Integer, nullable=False)
nonce = Column(String(255), nullable=False, unique=True)
repo_url = Column(String(512), nullable=False)
commit_sha = Column(String(255), nullable=False)
pages_url = Column(String(512), nullable=False)
def to_dict(self) -> dict[str, Any]:
"""Convert to dictionary."""
return {
"id": self.id,
"timestamp": self.timestamp.isoformat() if self.timestamp else None,
"email": self.email,
"task": self.task,
"round": self.round,
"nonce": self.nonce,
"repo_url": self.repo_url,
"commit_sha": self.commit_sha,
"pages_url": self.pages_url,
}
class Result(Base):
"""Evaluation results."""
__tablename__ = "results"
id = Column(Integer, primary_key=True)
timestamp = Column(DateTime, default=datetime.utcnow, nullable=False)
email = Column(String(255), nullable=False, index=True)
task = Column(String(255), nullable=False, index=True)
round = Column(Integer, nullable=False)
repo_url = Column(String(512), nullable=False)
commit_sha = Column(String(255), nullable=False)
pages_url = Column(String(512), nullable=False)
check = Column(String(512), nullable=False)
score = Column(Float, nullable=False)
reason = Column(Text, nullable=False)
logs = Column(Text, nullable=True)
def to_dict(self) -> dict[str, Any]:
"""Convert to dictionary."""
return {
"id": self.id,
"timestamp": self.timestamp.isoformat() if self.timestamp else None,
"email": self.email,
"task": self.task,
"round": self.round,
"repo_url": self.repo_url,
"commit_sha": self.commit_sha,
"pages_url": self.pages_url,
"check": self.check,
"score": self.score,
"reason": self.reason,
"logs": self.logs,
}
class Database:
"""Database manager for instructor system."""
def __init__(self, database_url: str | None = None) -> None:
"""Initialize database connection.
Args:
database_url: Database URL (uses settings if not provided)
"""
self.database_url = database_url or settings.database_url
self.engine = create_engine(self.database_url, echo=False)
self.SessionLocal = sessionmaker(bind=self.engine)
logger.info(f"Initialized database: {self.database_url}")
def create_tables(self) -> None:
"""Create all tables."""
Base.metadata.create_all(self.engine)
logger.info("Created database tables")
def drop_tables(self) -> None:
"""Drop all tables (use with caution)."""
Base.metadata.drop_all(self.engine)
logger.warning("Dropped all database tables")
def get_session(self):
"""Get database session."""
return self.SessionLocal()
# Task operations
def add_task(self, task_data: dict[str, Any]) -> Task:
"""Add a task record.
Args:
task_data: Task data dictionary
Returns:
Created task record
"""
session = self.get_session()
try:
task = Task(
email=task_data["email"],
task=task_data["task"],
round=task_data["round"],
nonce=task_data["nonce"],
brief=task_data["brief"],
attachments=json.dumps(task_data.get("attachments", [])),
checks=json.dumps(task_data.get("checks", [])),
evaluation_url=task_data["evaluation_url"],
endpoint=task_data["endpoint"],
statuscode=task_data.get("statuscode"),
secret=task_data["secret"],
)
session.add(task)
session.commit()
session.refresh(task)
logger.info(f"Added task: {task.task}, round {task.round}")
return task
finally:
session.close()
def get_task_by_nonce(self, nonce: str) -> Task | None:
"""Get task by nonce.
Args:
nonce: Task nonce
Returns:
Task or None
"""
session = self.get_session()
try:
return session.query(Task).filter(Task.nonce == nonce).first()
finally:
session.close()
def task_exists(self, email: str, task: str, round: int) -> bool:
"""Check if task exists.
Args:
email: Student email
task: Task ID
round: Round number
Returns:
True if exists
"""
session = self.get_session()
try:
return (
session.query(Task)
.filter(Task.email == email, Task.task == task, Task.round == round)
.first()
is not None
)
finally:
session.close()
# Repo operations
def add_repo(self, repo_data: dict[str, Any]) -> Repo:
"""Add a repo submission.
Args:
repo_data: Repo data dictionary
Returns:
Created repo record
"""
session = self.get_session()
try:
repo = Repo(**repo_data)
session.add(repo)
session.commit()
session.refresh(repo)
logger.info(f"Added repo: {repo.task}, round {repo.round}")
return repo
finally:
session.close()
def get_repos(self, email: str | None = None) -> list[Repo]:
"""Get repo submissions.
Args:
email: Filter by email (optional)
Returns:
List of repos
"""
session = self.get_session()
try:
query = session.query(Repo)
if email:
query = query.filter(Repo.email == email)
return query.all()
finally:
session.close()
# Result operations
def add_result(self, result_data: dict[str, Any]) -> Result:
"""Add an evaluation result.
Args:
result_data: Result data dictionary
Returns:
Created result record
"""
session = self.get_session()
try:
result = Result(**result_data)
session.add(result)
session.commit()
session.refresh(result)
logger.debug(f"Added result: {result.task}, {result.check}")
return result
finally:
session.close()
def get_results(
self, email: str | None = None, task: str | None = None
) -> list[Result]:
"""Get evaluation results.
Args:
email: Filter by email (optional)
task: Filter by task (optional)
Returns:
List of results
"""
session = self.get_session()
try:
query = session.query(Result)
if email:
query = query.filter(Result.email == email)
if task:
query = query.filter(Result.task == task)
return query.all()
finally:
session.close()