138 lines
8.3 KiB
Python
138 lines
8.3 KiB
Python
import uuid
|
|
from datetime import datetime, timezone
|
|
from sqlalchemy import (
|
|
Boolean, DateTime, ForeignKey, Integer, String, Text
|
|
)
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
from app.core.database import Base
|
|
|
|
|
|
def new_uuid() -> str:
|
|
return str(uuid.uuid4())
|
|
|
|
def now_utc() -> datetime:
|
|
return datetime.now(timezone.utc)
|
|
|
|
|
|
# ── User ──────────────────────────────────────────────────────────────────────
|
|
|
|
class User(Base):
|
|
__tablename__ = "users"
|
|
|
|
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
|
username: Mapped[str] = mapped_column(String(64), unique=True, nullable=False)
|
|
email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
|
|
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=now_utc)
|
|
|
|
projects: Mapped[list["Project"]] = relationship("Project", back_populates="owner")
|
|
memberships: Mapped[list["ProjectMember"]] = relationship("ProjectMember", back_populates="user")
|
|
songs: Mapped[list["Song"]] = relationship("Song", back_populates="owner")
|
|
alternatives: Mapped[list["DanceAlternative"]] = relationship("DanceAlternative", foreign_keys="DanceAlternative.created_by", back_populates="creator")
|
|
alt_ratings: Mapped[list["DanceAlternativeRating"]] = relationship("DanceAlternativeRating", foreign_keys="DanceAlternativeRating.user_id", back_populates="user")
|
|
|
|
|
|
# ── Project ───────────────────────────────────────────────────────────────────
|
|
|
|
class Project(Base):
|
|
__tablename__ = "projects"
|
|
|
|
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
|
owner_id: Mapped[str] = mapped_column(String(36), ForeignKey("users.id"), nullable=False)
|
|
name: Mapped[str] = mapped_column(String(128), nullable=False)
|
|
description: Mapped[str] = mapped_column(Text, default="")
|
|
is_public: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=now_utc, onupdate=now_utc)
|
|
|
|
owner: Mapped["User"] = relationship("User", back_populates="projects")
|
|
members: Mapped[list["ProjectMember"]] = relationship("ProjectMember", back_populates="project", cascade="all, delete-orphan")
|
|
project_songs: Mapped[list["ProjectSong"]] = relationship("ProjectSong", back_populates="project", order_by="ProjectSong.position", cascade="all, delete-orphan")
|
|
|
|
|
|
class ProjectMember(Base):
|
|
__tablename__ = "project_members"
|
|
|
|
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
|
project_id: Mapped[str] = mapped_column(String(36), ForeignKey("projects.id"), nullable=False)
|
|
user_id: Mapped[str] = mapped_column(String(36), ForeignKey("users.id"), nullable=False)
|
|
role: Mapped[str] = mapped_column(String(16), default="viewer") # owner | editor | viewer
|
|
status: Mapped[str] = mapped_column(String(16), default="pending") # pending | accepted
|
|
invited_at: Mapped[datetime] = mapped_column(DateTime, default=now_utc)
|
|
|
|
project: Mapped["Project"] = relationship("Project", back_populates="members")
|
|
user: Mapped["User"] = relationship("User", back_populates="memberships")
|
|
|
|
|
|
# ── Song ──────────────────────────────────────────────────────────────────────
|
|
|
|
class Song(Base):
|
|
__tablename__ = "songs"
|
|
|
|
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
|
owner_id: Mapped[str] = mapped_column(String(36), ForeignKey("users.id"), nullable=False)
|
|
title: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
artist: Mapped[str] = mapped_column(String(255), default="")
|
|
local_path: Mapped[str] = mapped_column(String(512), default="") # kun relevant på PC
|
|
bpm: Mapped[int] = mapped_column(Integer, default=0)
|
|
duration_sec: Mapped[int] = mapped_column(Integer, default=0)
|
|
synced_at: Mapped[datetime] = mapped_column(DateTime, default=now_utc)
|
|
|
|
owner: Mapped["User"] = relationship("User", back_populates="songs")
|
|
dances: Mapped[list["SongDance"]] = relationship("SongDance", back_populates="song", order_by="SongDance.dance_order", cascade="all, delete-orphan")
|
|
project_songs: Mapped[list["ProjectSong"]] = relationship("ProjectSong", back_populates="song")
|
|
|
|
|
|
class ProjectSong(Base):
|
|
__tablename__ = "project_songs"
|
|
|
|
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
|
project_id: Mapped[str] = mapped_column(String(36), ForeignKey("projects.id"), nullable=False)
|
|
song_id: Mapped[str] = mapped_column(String(36), ForeignKey("songs.id"), nullable=False)
|
|
position: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
status: Mapped[str] = mapped_column(String(16), default="pending") # pending | playing | played | skipped
|
|
|
|
project: Mapped["Project"] = relationship("Project", back_populates="project_songs")
|
|
song: Mapped["Song"] = relationship("Song", back_populates="project_songs")
|
|
|
|
|
|
# ── Dance ─────────────────────────────────────────────────────────────────────
|
|
|
|
class SongDance(Base):
|
|
__tablename__ = "song_dances"
|
|
|
|
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
|
song_id: Mapped[str] = mapped_column(String(36), ForeignKey("songs.id"), nullable=False)
|
|
dance_name: Mapped[str] = mapped_column(String(128), nullable=False)
|
|
dance_order: Mapped[int] = mapped_column(Integer, default=1)
|
|
|
|
song: Mapped["Song"] = relationship("Song", back_populates="dances")
|
|
alternatives: Mapped[list["DanceAlternative"]] = relationship("DanceAlternative", foreign_keys="DanceAlternative.song_dance_id", back_populates="song_dance", cascade="all, delete-orphan")
|
|
|
|
|
|
class DanceAlternative(Base):
|
|
__tablename__ = "dance_alternatives"
|
|
|
|
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
|
song_dance_id: Mapped[str] = mapped_column(String(36), ForeignKey("song_dances.id"), nullable=False)
|
|
alt_song_dance_id: Mapped[str] = mapped_column(String(36), ForeignKey("song_dances.id"), nullable=False)
|
|
created_by: Mapped[str] = mapped_column(String(36), ForeignKey("users.id"), nullable=False)
|
|
note: Mapped[str] = mapped_column(Text, default="")
|
|
bayesian_score: Mapped[float] = mapped_column(default=0.0) # genberegnes ved hver rating
|
|
|
|
song_dance: Mapped["SongDance"] = relationship("SongDance", foreign_keys=[song_dance_id], back_populates="alternatives")
|
|
alt_song_dance: Mapped["SongDance"] = relationship("SongDance", foreign_keys=[alt_song_dance_id])
|
|
creator: Mapped["User"] = relationship("User", foreign_keys=[created_by])
|
|
ratings: Mapped[list["DanceAlternativeRating"]] = relationship("DanceAlternativeRating", back_populates="alternative", cascade="all, delete-orphan")
|
|
|
|
|
|
class DanceAlternativeRating(Base):
|
|
__tablename__ = "dance_alternative_ratings"
|
|
|
|
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
|
|
alternative_id: Mapped[str] = mapped_column(String(36), ForeignKey("dance_alternatives.id"), nullable=False)
|
|
user_id: Mapped[str] = mapped_column(String(36), ForeignKey("users.id"), nullable=False)
|
|
score: Mapped[int] = mapped_column(Integer, nullable=False) # 1-5
|
|
|
|
alternative: Mapped["DanceAlternative"] = relationship("DanceAlternative", back_populates="ratings")
|
|
user: Mapped["User"] = relationship("User", foreign_keys=[user_id])
|