from flask_sqlalchemy import SQLAlchemy from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, MappedAsDataclass from sqlalchemy import types, text, String, DateTime, func, Boolean, ForeignKey, SmallInteger from datetime import datetime import uuid from typing import List from ..config import * from ..constants import UserRole, PublishedStatus class Base(MappedAsDataclass, DeclarativeBase): pass db = SQLAlchemy(model_class=Base) class User(db.Model): __tablename__ = 'user' id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) email: Mapped[str] = mapped_column(String(64), nullable=False, unique=True) firstName: Mapped[str] = mapped_column(String(32), nullable=False) lastName: Mapped[str] = mapped_column(String(32), nullable=False) username: Mapped[str] = mapped_column(String(32), nullable=False) hash_password: Mapped[str] = mapped_column(String(256), nullable=False) activationKey: Mapped[str] = mapped_column(String(128), nullable=False) sessions: Mapped[List["Session"]] = relationship(back_populates="user", cascade="all, delete-orphan") enrollments: Mapped[List["Enrollment"]] = relationship(back_populates="user", cascade="all, delete-orphan") quizzes: Mapped[List["Quiz"]] = relationship(back_populates="creatorUser", cascade="all, delete-orphan") quiz_attempts: Mapped[List["QuizAttempt"]] = relationship(back_populates="user", cascade="all, delete-orphan") chats: Mapped[List["Chat"]] = relationship(back_populates="user", cascade="all, delete-orphan") notifications: Mapped[List["Notification"]] = relationship(back_populates="user", cascade="all, delete-orphan") dob: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.fromisocalendar(2002, 1, 1)) user_badges: Mapped[List["UserBadge"]] = relationship(back_populates="user", cascade="all, delete-orphan") pfpFilename: Mapped[str] = mapped_column(String(256), nullable=False, default=DEFAULT_PROFILE_FILE) joinedDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) lastOnline: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now(), onupdate=func.now()) bio: Mapped[str] = mapped_column(String(256), nullable=False, default='') role: Mapped[int] = mapped_column(SmallInteger, nullable=False, default=UserRole.USER) isActivated: Mapped[bool] = mapped_column(Boolean, nullable=False, default=ACTIVATE_ACCOUNTS_ON_SIGNUP) class Session(db.Model): __tablename__ = 'session' id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) userID: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id")) user: Mapped["User"] = relationship(back_populates="sessions") key: Mapped[str] = mapped_column(String(256), nullable=False, unique=True) ua: Mapped[str] = mapped_column(String(1024), nullable=False) creationDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) lastUsed: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now(), onupdate=func.now()) isValid: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True) class Category(db.Model): __tablename__ = 'category' id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) name: Mapped[str] = mapped_column(String(32), nullable=False, unique=True) courses: Mapped[List["Course"]] = relationship(back_populates="category", cascade="all, delete-orphan") description: Mapped[str] = mapped_column(String(128), nullable=False, default='') isActive: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True) creationDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) class Course(db.Model): __tablename__ = 'course' id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) name: Mapped[str] = mapped_column(String(64), nullable=False, unique=True) categoryID: Mapped[uuid.UUID] = mapped_column(ForeignKey("category.id")) category: Mapped["Category"] = relationship(back_populates="courses") enrollments: Mapped[List["Enrollment"]] = relationship(back_populates="course", cascade="all, delete-orphan") quizzes: Mapped[List['Quiz']] = relationship(back_populates="course", cascade="all, delete-orphan") chats: Mapped[List["Chat"]] = relationship(back_populates="course", cascade="all, delete-orphan") description: Mapped[str] = mapped_column(String(1024), nullable=False, default='') isActive: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True) publishedStatus: Mapped[int] = mapped_column(SmallInteger, nullable=False, default=PublishedStatus.DRAFT) creationDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) coverImage: Mapped[str] = mapped_column(String(256), nullable=False, default=DEFAULT_COURSE_COVER) serverFilename: Mapped[str] = mapped_column(String(256), nullable=False, default='') class Enrollment(db.Model): __tablename__ = 'enrollment' id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) userID: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id")) user: Mapped["User"] = relationship(back_populates="enrollments") courseID: Mapped[uuid.UUID] = mapped_column(ForeignKey("course.id")) course: Mapped["Course"] = relationship(back_populates="enrollments") joinedDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) lastActivity: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now(), onupdate=func.now()) currentPage: Mapped[int] = mapped_column(nullable=False, default=1) maxPage: Mapped[int] = mapped_column(nullable=False, default=1) class Quiz(db.Model): __tablename__ = 'quiz' id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) creatorUserID: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id")) creatorUser: Mapped["User"] = relationship(back_populates="quizzes") quiz_attempts: Mapped[List["QuizAttempt"]] = relationship(back_populates="quiz", cascade="all, delete-orphan") courseID: Mapped[uuid.UUID] = mapped_column(ForeignKey("course.id")) course: Mapped["Course"] = relationship(back_populates="quizzes") quizJson: Mapped[str] = mapped_column(String, nullable=False) creationDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) isActive: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True) class QuizAttempt(db.Model): __tablename__ = 'quiz_attempts' id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) userID: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id")) user: Mapped["User"] = relationship(back_populates="quiz_attempts") quizID: Mapped[uuid.UUID] = mapped_column(ForeignKey("quiz.id")) quiz: Mapped["Quiz"] = relationship(back_populates="quiz_attempts") answerKey: Mapped[str] = mapped_column(String, nullable=False) score: Mapped[int] = mapped_column(default=0, nullable=False) attemptDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) class Chat(db.Model): __tablename__ = 'chat' id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) textContent: Mapped[str] = mapped_column(nullable=False) userID: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id")) courseID: Mapped[uuid.UUID] = mapped_column(ForeignKey("course.id")) course: Mapped["Course"] = relationship(back_populates="chats") user: Mapped["User"] = relationship(back_populates="chats") chatDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) class Notification(db.Model): __tablename__ = 'notification' id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) userID: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id")) user: Mapped["User"] = relationship(back_populates="notifications") notificationType: Mapped[int] = mapped_column(SmallInteger, nullable=False) notificationData: Mapped[str] = mapped_column(String, nullable=False) isSeen: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) notifDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) class Badge(db.Model): __tablename__ = 'badge' id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) name: Mapped[str] = mapped_column(String(16), nullable=False) user_badges: Mapped[List["UserBadge"]] = relationship(back_populates="badge", cascade="all, delete-orphan") description: Mapped[str] = mapped_column(String(256), nullable=False, default='') createDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) icon: Mapped[str] = mapped_column(String(256), nullable=False, default='') canClaim: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) class UserBadge(db.Model): __tablename__ = 'userbadge' id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) userID: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id")) badgeID: Mapped[uuid.UUID] = mapped_column(ForeignKey("badge.id")) user: Mapped["User"] = relationship(back_populates="user_badges") badge: Mapped["Badge"] = relationship(back_populates="user_badges") claimedDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now())