cam / app /models.py
cacode's picture
Upload 67 files
4fb3744 verified
from __future__ import annotations
from datetime import datetime
from typing import Optional
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
class TimestampMixin:
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow, onupdate=datetime.utcnow
)
class Admin(TimestampMixin, Base):
__tablename__ = "admins"
id: Mapped[int] = mapped_column(primary_key=True)
username: Mapped[str] = mapped_column(String(80), unique=True, index=True)
display_name: Mapped[str] = mapped_column(String(120))
password_hash: Mapped[str] = mapped_column(String(255))
role: Mapped[str] = mapped_column(String(32), default="admin")
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
last_seen_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
created_activities: Mapped[list["Activity"]] = relationship(back_populates="created_by")
reviewed_submissions: Mapped[list["Submission"]] = relationship(
back_populates="reviewed_by",
foreign_keys="Submission.reviewed_by_id",
)
assigned_submissions: Mapped[list["Submission"]] = relationship(
back_populates="assigned_admin",
foreign_keys="Submission.assigned_admin_id",
)
class Group(TimestampMixin, Base):
__tablename__ = "groups"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(120), unique=True)
max_members: Mapped[int] = mapped_column(Integer, default=6)
members: Mapped[list["User"]] = relationship(back_populates="group")
submissions: Mapped[list["Submission"]] = relationship(back_populates="group")
class User(TimestampMixin, Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
student_id: Mapped[str] = mapped_column(String(64), unique=True, index=True)
full_name: Mapped[str] = mapped_column(String(120))
password_hash: Mapped[str] = mapped_column(String(255))
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
group_id: Mapped[Optional[int]] = mapped_column(ForeignKey("groups.id"), nullable=True)
last_seen_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
group: Mapped[Optional["Group"]] = relationship(back_populates="members")
submissions: Mapped[list["Submission"]] = relationship(back_populates="user")
class Activity(TimestampMixin, Base):
__tablename__ = "activities"
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str] = mapped_column(String(160))
description: Mapped[str] = mapped_column(Text, default="")
start_at: Mapped[datetime] = mapped_column(DateTime)
deadline_at: Mapped[datetime] = mapped_column(DateTime)
is_visible: Mapped[bool] = mapped_column(Boolean, default=True)
leaderboard_visible: Mapped[bool] = mapped_column(Boolean, default=True)
clue_interval_minutes: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
created_by_id: Mapped[int] = mapped_column(ForeignKey("admins.id"))
created_by: Mapped["Admin"] = relationship(back_populates="created_activities")
tasks: Mapped[list["Task"]] = relationship(
back_populates="activity",
order_by="Task.display_order",
cascade="all, delete-orphan",
)
class Task(TimestampMixin, Base):
__tablename__ = "tasks"
id: Mapped[int] = mapped_column(primary_key=True)
activity_id: Mapped[int] = mapped_column(ForeignKey("activities.id"))
title: Mapped[str] = mapped_column(String(160))
description: Mapped[str] = mapped_column(Text, default="")
display_order: Mapped[int] = mapped_column(Integer, default=1)
image_url: Mapped[Optional[str]] = mapped_column(String(1000), nullable=True)
image_path: Mapped[Optional[str]] = mapped_column(String(600), nullable=True)
image_mime: Mapped[str] = mapped_column(String(120), default="image/jpeg")
image_filename: Mapped[str] = mapped_column(String(255), default="task.jpg")
clue_image_url: Mapped[Optional[str]] = mapped_column(String(1000), nullable=True)
clue_image_path: Mapped[Optional[str]] = mapped_column(String(600), nullable=True)
clue_image_mime: Mapped[Optional[str]] = mapped_column(String(120), nullable=True)
clue_image_filename: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
clue_release_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
activity: Mapped["Activity"] = relationship(back_populates="tasks")
submissions: Mapped[list["Submission"]] = relationship(
back_populates="task", cascade="all, delete-orphan"
)
class Submission(TimestampMixin, Base):
__tablename__ = "submissions"
__table_args__ = (
UniqueConstraint("group_id", "task_id", name="uq_group_task_submission"),
)
id: Mapped[int] = mapped_column(primary_key=True)
task_id: Mapped[int] = mapped_column(ForeignKey("tasks.id"))
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
group_id: Mapped[Optional[int]] = mapped_column(ForeignKey("groups.id"), nullable=True)
stored_filename: Mapped[str] = mapped_column(String(255))
original_filename: Mapped[str] = mapped_column(String(255))
file_path: Mapped[str] = mapped_column(String(600))
mime_type: Mapped[str] = mapped_column(String(120), default="image/jpeg")
file_size: Mapped[int] = mapped_column(Integer)
status: Mapped[str] = mapped_column(String(32), default="pending")
feedback: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
reviewed_by_id: Mapped[Optional[int]] = mapped_column(
ForeignKey("admins.id"), nullable=True
)
assigned_admin_id: Mapped[Optional[int]] = mapped_column(
ForeignKey("admins.id"), nullable=True
)
assigned_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
reviewed_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
approved_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
task: Mapped["Task"] = relationship(back_populates="submissions")
user: Mapped["User"] = relationship(back_populates="submissions")
group: Mapped[Optional["Group"]] = relationship(back_populates="submissions")
reviewed_by: Mapped[Optional["Admin"]] = relationship(
back_populates="reviewed_submissions",
foreign_keys=[reviewed_by_id],
)
assigned_admin: Mapped[Optional["Admin"]] = relationship(
back_populates="assigned_submissions",
foreign_keys=[assigned_admin_id],
)