import json from datetime import datetime from sqlalchemy import String, Text, Integer, DateTime, ForeignKey, func from sqlalchemy.orm import Mapped, mapped_column, relationship from app.db.base import Base class Class_(Base): __tablename__ = "classes" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) name: Mapped[str] = mapped_column(String(100), nullable=False) cohort_year: Mapped[int] = mapped_column(Integer, nullable=False) description: Mapped[str | None] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now()) updated_at: Mapped[datetime] = mapped_column( DateTime, server_default=func.now(), onupdate=func.now() ) members: Mapped[list["User"]] = relationship("User", back_populates="class_") timelines: Mapped[list["Timeline"]] = relationship( "Timeline", back_populates="class_", cascade="all, delete-orphan" ) schedules: Mapped[list["Schedule"]] = relationship( "Schedule", back_populates="class_", cascade="all, delete-orphan" ) class User(Base): __tablename__ = "users" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False, index=True) password_hash: Mapped[str] = mapped_column(Text, nullable=False) name: Mapped[str] = mapped_column(String(100), nullable=False) student_id: Mapped[str | None] = mapped_column(String(50), nullable=True, unique=True) # role: super_admin | class_admin | student role: Mapped[str] = mapped_column(String(20), default="student", nullable=False) # status: pending | approved | rejected | disabled status: Mapped[str] = mapped_column(String(20), default="pending", nullable=False) class_id: Mapped[int | None] = mapped_column( Integer, ForeignKey("classes.id"), nullable=True ) class_: Mapped["Class_ | None"] = relationship("Class_", back_populates="members") # Profile industry: Mapped[str | None] = mapped_column(String(100), nullable=True) company: Mapped[str | None] = mapped_column(String(100), nullable=True) position: Mapped[str | None] = mapped_column(String(100), nullable=True) skills_tags: Mapped[str | None] = mapped_column(Text, nullable=True) # JSON array wechat_id: Mapped[str | None] = mapped_column(String(100), nullable=True) avatar_url: Mapped[str | None] = mapped_column(Text, nullable=True) bio: Mapped[str | None] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now()) updated_at: Mapped[datetime] = mapped_column( DateTime, server_default=func.now(), onupdate=func.now() ) timeline_posts: Mapped[list["Timeline"]] = relationship( "Timeline", back_populates="author" ) def get_skills_list(self) -> list[str]: if not self.skills_tags: return [] try: return json.loads(self.skills_tags) except (json.JSONDecodeError, TypeError): return [] def set_skills_list(self, tags: list[str]): self.skills_tags = json.dumps(tags, ensure_ascii=False) if tags else None class Timeline(Base): __tablename__ = "timelines" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) class_id: Mapped[int] = mapped_column( Integer, ForeignKey("classes.id"), nullable=False, index=True ) author_id: Mapped[int] = mapped_column( Integer, ForeignKey("users.id"), nullable=False ) title: Mapped[str] = mapped_column(String(200), nullable=False) content: Mapped[str | None] = mapped_column(Text, nullable=True) image_urls: Mapped[str | None] = mapped_column(Text, nullable=True) # JSON array created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now()) updated_at: Mapped[datetime] = mapped_column( DateTime, server_default=func.now(), onupdate=func.now() ) class_: Mapped["Class_"] = relationship("Class_", back_populates="timelines") author: Mapped["User"] = relationship("User", back_populates="timeline_posts") def get_image_urls_list(self) -> list[str]: if not self.image_urls: return [] try: return json.loads(self.image_urls) except (json.JSONDecodeError, TypeError): return [] def set_image_urls_list(self, urls: list[str]): self.image_urls = json.dumps(urls) if urls else None class Schedule(Base): __tablename__ = "schedules" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) class_id: Mapped[int] = mapped_column( Integer, ForeignKey("classes.id"), nullable=False, index=True ) # type: course | deadline | activity type: Mapped[str] = mapped_column(String(20), nullable=False) title: Mapped[str] = mapped_column(String(200), nullable=False) start_time: Mapped[datetime] = mapped_column(DateTime, nullable=False) end_time: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) location: Mapped[str | None] = mapped_column(String(200), nullable=True) description: Mapped[str | None] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now()) updated_at: Mapped[datetime] = mapped_column( DateTime, server_default=func.now(), onupdate=func.now() ) class_: Mapped["Class_"] = relationship("Class_", back_populates="schedules")