# plugins/media/models.py from datetime import datetime from flask import url_for from app import db class Media(db.Model): __tablename__ = "media" __table_args__ = {"extend_existing": True} id = db.Column(db.Integer, primary_key=True) plugin = db.Column(db.String(50), nullable=False) related_id = db.Column(db.Integer, nullable=False) filename = db.Column(db.String(256), nullable=False) uploaded_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) uploader_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) caption = db.Column(db.String(255), nullable=True) plant_id = db.Column(db.Integer, db.ForeignKey("plant.id"), nullable=True) growlog_id = db.Column(db.Integer, db.ForeignKey("grow_logs.id"), nullable=True) created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) file_url = db.Column(db.String(512), nullable=False) hearts = db.relationship( "ImageHeart", backref="media", lazy="dynamic", cascade="all, delete-orphan", ) featured_entries = db.relationship( "FeaturedImage", backref="media", lazy="dynamic", cascade="all, delete-orphan", ) # ↔ Media items attached to a Plant plant = db.relationship( "Plant", back_populates="media_items", foreign_keys=[plant_id], lazy="joined", ) # ↔ Media items attached to a GrowLog growlog = db.relationship( "GrowLog", back_populates="media_items", foreign_keys=[growlog_id], lazy="joined", ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self.plant_id: self.plugin = "plant" self.related_id = self.plant_id elif self.growlog_id: self.plugin = "growlog" self.related_id = self.growlog_id else: self.plugin = kwargs.get("plugin", "") self.related_id = kwargs.get("related_id", 0) self.file_url = f"{self.plugin}/{self.related_id}/{self.filename}" @property def url(self): return url_for("media.media_file", filename=self.file_url) @property def featured(self): return any( fe.context == "plant" and fe.is_featured for fe in self.featured_entries ) class ZipJob(db.Model): __tablename__ = 'zip_jobs' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, nullable=False) filename = db.Column(db.String(255), nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow) status = db.Column(db.String(20), default='queued') # queued|processing|done|failed error = db.Column(db.Text, nullable=True) class ImageHeart(db.Model): __tablename__ = "image_hearts" __table_args__ = {"extend_existing": True} id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) media_id = db.Column(db.Integer, db.ForeignKey("media.id"), nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) class FeaturedImage(db.Model): __tablename__ = "featured_images" __table_args__ = {"extend_existing": True} id = db.Column(db.Integer, primary_key=True) media_id = db.Column(db.Integer, db.ForeignKey("media.id"), nullable=False) context = db.Column(db.String(50), nullable=False) context_id = db.Column(db.Integer, nullable=False) override_text = db.Column(db.String(255), nullable=True) is_featured = db.Column(db.Boolean, default=True, nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)