"""Product video sizing rule model.""" from __future__ import annotations from datetime import datetime from typing import Any from sqlalchemy import ( TIMESTAMP, VARCHAR, Boolean, Column, FetchedValue, ForeignKey, Integer, Select, func, select, ) from sqlalchemy.orm import Mapped, Session, mapped_column, relationship from sqlalchemy.sql.expression import false from video.connectors import mysql from video.exceptions import ProductVideoSizingRuleNotFound class ProductVideoSizingRule(mysql.BaseModel): """Product Video Sizing Rule.""" __tablename__ = "video_resizing_rules" pk = Column( Integer, name="id", nullable=False, primary_key=True, autoincrement=True, ) input_inner_width = Column( Integer, nullable=False, ) input_inner_height = Column( Integer, nullable=False, ) output_inner_width = Column( Integer, nullable=False, ) output_inner_height = Column( Integer, nullable=False, ) video_resolution_id = Column( Integer, ForeignKey("supported_video_resolutions.id"), nullable=False, ) video_resolution = relationship("ProductVideoOutputResolution") created_at = Column(TIMESTAMP, nullable=False, server_default=FetchedValue()) created_by = Column(VARCHAR(25), nullable=True) deleted_at: Mapped[datetime | None] = mapped_column(TIMESTAMP, nullable=True) deleted_by: Mapped[str | None] = mapped_column(VARCHAR(25), nullable=True) is_deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) def to_dict(self) -> dict[str, Any]: """Get a dict representation.""" return { "id": self.pk, "input_inner_width": self.input_inner_width, "input_inner_height": self.input_inner_height, "output_inner_width": self.output_inner_width, "output_inner_height": self.output_inner_height, "output_outer_width": self.video_resolution.output_outer_width, "output_outer_height": self.video_resolution.output_outer_height, "video_resolution_id": self.video_resolution_id, } @staticmethod def skip_deleted() -> Select[tuple[ProductVideoSizingRule]]: """Return a select statement that skips soft-deleted rows.""" return select(ProductVideoSizingRule).where( ProductVideoSizingRule.is_deleted == false() ) def _get(sizing_rule_id: int, session: Session) -> ProductVideoSizingRule | None: """Get video sizing rule by id.""" return session.execute( ProductVideoSizingRule.skip_deleted().where( ProductVideoSizingRule.pk == sizing_rule_id ) ).scalar_one_or_none() def get(sizing_rule_id: int, session: Session) -> dict[str, Any]: """Get video sizing rule by id. Raises: ProductVideoSizingRuleNotFound: if no rule with the given id exists. """ sizing_rule = _get(sizing_rule_id, session) if not sizing_rule: raise ProductVideoSizingRuleNotFound() return sizing_rule.to_dict() def get_all(session: Session) -> list[dict[str, Any]]: """Get all video sizing rules.""" sizing_rules = ( session.execute(ProductVideoSizingRule.skip_deleted()).scalars().all() ) return [item.to_dict() for item in sizing_rules] def insert(sizing_rule_data: dict[str, Any], session: Session) -> dict[str, Any]: """Insert sizing rule.""" return create(sizing_rule_data, session) def create(sizing_rule_data: dict[str, Any], session: Session) -> dict[str, Any]: """Create sizing rule.""" sanitized = sanitize_input(sizing_rule_data) sizing_rule = ProductVideoSizingRule(**sanitized) session.add(sizing_rule) session.flush() return sizing_rule.to_dict() def delete( item_id: int, orchard_user_id: str | None = None, *, session: Session, ) -> dict[str, Any]: """Delete sizing rule. Raises: ProductVideoSizingRuleNotFound: if no rule with the given id exists. """ sizing_rule = _get(item_id, session) if not sizing_rule: raise ProductVideoSizingRuleNotFound() sizing_rule.is_deleted = True sizing_rule.deleted_at = func.now() sizing_rule.deleted_by = orchard_user_id session.add(sizing_rule) session.flush() return sizing_rule.to_dict() def find(width: int, height: int, session: Session) -> list[ProductVideoSizingRule]: """Get all output video resolution.""" resolution = ( session.execute( ProductVideoSizingRule.skip_deleted().where( ProductVideoSizingRule.input_inner_width == width, ProductVideoSizingRule.input_inner_height == height, ) ) .scalars() .all() ) return list(resolution) def sanitize_input(sizing_rule_data: dict[str, Any]) -> dict[str, Any]: """Drop keys that shouldn't be monkeyed with.""" valid_columns = ProductVideoSizingRule.__table__.columns.keys() sanitized_dict = { k: sizing_rule_data[k] for k in valid_columns if k in sizing_rule_data } sanitized_dict.pop("id", None) return sanitized_dict