import uuid from datetime import datetime from flask_login import UserMixin from werkzeug.security import check_password_hash, generate_password_hash from .extensions import db class Company(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(120), unique=True, nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) users = db.relationship("User", back_populates="company", cascade="all, delete-orphan") displays = db.relationship("Display", back_populates="company", cascade="all, delete-orphan") playlists = db.relationship("Playlist", back_populates="company", cascade="all, delete-orphan") class User(UserMixin, db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) email = db.Column(db.String(255), unique=True, nullable=True) password_hash = db.Column(db.String(255), nullable=True) is_admin = db.Column(db.Boolean, default=False, nullable=False) company_id = db.Column(db.Integer, db.ForeignKey("company.id"), nullable=True) company = db.relationship("Company", back_populates="users") created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) def set_password(self, password: str): self.password_hash = generate_password_hash(password) def check_password(self, password: str) -> bool: if not self.password_hash: return False return check_password_hash(self.password_hash, password) class Playlist(db.Model): id = db.Column(db.Integer, primary_key=True) company_id = db.Column(db.Integer, db.ForeignKey("company.id"), nullable=False) name = db.Column(db.String(120), nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) company = db.relationship("Company", back_populates="playlists") items = db.relationship( "PlaylistItem", back_populates="playlist", cascade="all, delete-orphan", order_by="PlaylistItem.position.asc()", ) class PlaylistItem(db.Model): id = db.Column(db.Integer, primary_key=True) playlist_id = db.Column(db.Integer, db.ForeignKey("playlist.id"), nullable=False) # image|video|webpage item_type = db.Column(db.String(20), nullable=False) title = db.Column(db.String(200), nullable=True) # For image/video: stored under /static/uploads file_path = db.Column(db.String(400), nullable=True) # For webpage url = db.Column(db.String(1000), nullable=True) # For image/webpage duration (seconds). For video we play until ended. duration_seconds = db.Column(db.Integer, default=10, nullable=False) position = db.Column(db.Integer, default=0, nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) playlist = db.relationship("Playlist", back_populates="items") class Display(db.Model): id = db.Column(db.Integer, primary_key=True) company_id = db.Column(db.Integer, db.ForeignKey("company.id"), nullable=False) name = db.Column(db.String(120), nullable=False) token = db.Column(db.String(64), unique=True, nullable=False, default=lambda: uuid.uuid4().hex) assigned_playlist_id = db.Column(db.Integer, db.ForeignKey("playlist.id"), nullable=True) assigned_playlist = db.relationship("Playlist") created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) company = db.relationship("Company", back_populates="displays") class DisplaySession(db.Model): """Tracks active viewers of a display token so we can limit concurrent usage.""" id = db.Column(db.Integer, primary_key=True) display_id = db.Column(db.Integer, db.ForeignKey("display.id"), nullable=False, index=True) sid = db.Column(db.String(64), nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) last_seen_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False, index=True) # Optional diagnostics ip = db.Column(db.String(64), nullable=True) user_agent = db.Column(db.String(300), nullable=True) display = db.relationship("Display") __table_args__ = (db.UniqueConstraint("display_id", "sid", name="uq_display_session_display_sid"),)