Files

84 lines
3.0 KiB
Python

from datetime import datetime, timezone
import enum
from sqlalchemy import (
Column,
Enum,
Integer,
String,
Text,
ForeignKey,
DateTime,
UniqueConstraint,
)
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import relationship
from .database import Base
class UserRole(str, enum.Enum):
admin = "admin"
teacher = "teacher"
student = "student"
class UserStatus(str, enum.Enum):
pending = "pending"
approved = "approved"
rejected = "rejected"
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String(64), unique=True, nullable=False, index=True)
email = Column(String(255), unique=True, nullable=False, index=True)
password_hash = Column(String(255), nullable=False)
role = Column(Enum(UserRole, name="user_role"), nullable=False, default=UserRole.student)
status = Column(Enum(UserStatus, name="user_status"), nullable=False, default=UserStatus.pending)
created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False)
# Relationships
pdfs = relationship("PDF", back_populates="owner", cascade="all, delete-orphan")
annotations = relationship("Annotation", back_populates="owner", cascade="all, delete-orphan")
class PDF(Base):
__tablename__ = "pdfs"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True)
title = Column(String(255), nullable=False)
file_path = Column(Text, nullable=False) # path inside Docker volume
total_pages = Column(Integer, nullable=True) # populated after upload processing
created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False)
# Relationships
owner = relationship("User", back_populates="pdfs")
annotations = relationship("Annotation", back_populates="pdf", cascade="all, delete-orphan")
class Annotation(Base):
__tablename__ = "annotations"
__table_args__ = (
# One annotation record per (pdf, user, page) — upsert target
UniqueConstraint("pdf_id", "user_id", "page_number", name="uq_annotation_pdf_user_page"),
)
id = Column(Integer, primary_key=True, index=True)
pdf_id = Column(Integer, ForeignKey("pdfs.id", ondelete="CASCADE"), nullable=False, index=True)
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True)
page_number = Column(Integer, nullable=False) # 1-based
canvas_data = Column(JSONB, nullable=False, default=dict) # Fabric.js JSON state
updated_at = Column(
DateTime(timezone=True),
default=lambda: datetime.now(timezone.utc),
onupdate=lambda: datetime.now(timezone.utc),
nullable=False,
)
# Relationships
pdf = relationship("PDF", back_populates="annotations")
owner = relationship("User", back_populates="annotations")