import enum from datetime import datetime from sqlalchemy import String, Text, Integer, Numeric, Boolean, DateTime, Enum, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database import Base class ModelType(str, enum.Enum): CURRENT_OPLUS = "current_oplus" AI_OPLUS = "ai_oplus" CURRENT_LOCAL = "current_local" AI_LOCAL = "ai_local" ASSET_FACTORY = "asset_factory" # Map spreadsheet header text -> enum MODEL_TYPE_MAP = { "Current Model - O+ Market": ModelType.CURRENT_OPLUS, "AI Model - O+ Market": ModelType.AI_OPLUS, "Current Model - Local Market": ModelType.CURRENT_LOCAL, "AI Model - Local Market": ModelType.AI_LOCAL, "Asset Factory Model": ModelType.ASSET_FACTORY, } class GmalAsset(Base): __tablename__ = "gmal_assets" id: Mapped[int] = mapped_column(primary_key=True) gmal_id: Mapped[str] = mapped_column(String(20), unique=True, nullable=False, index=True) swop_asset_id: Mapped[str | None] = mapped_column(String(50)) region: Mapped[str | None] = mapped_column(String(20)) category: Mapped[str | None] = mapped_column(String(100)) sub_category: Mapped[str | None] = mapped_column(String(100), index=True) sub_category_description: Mapped[str | None] = mapped_column(Text) asset_name: Mapped[str | None] = mapped_column(String(255)) complexity_level: Mapped[int | None] = mapped_column(Integer) complexity_name: Mapped[str | None] = mapped_column(String(20)) unique_name: Mapped[str | None] = mapped_column(String(255)) asset_description: Mapped[str | None] = mapped_column(Text) complexity_description: Mapped[str | None] = mapped_column(Text) caveats: Mapped[str | None] = mapped_column(Text) ai_enhanced_description: Mapped[str | None] = mapped_column(Text) master_adapt: Mapped[str | None] = mapped_column(String(20)) ai_efficiency_pct: Mapped[float | None] = mapped_column(Numeric(5, 2)) has_hour_routes: Mapped[bool] = mapped_column(Boolean, default=False) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) hours: Mapped[list["GmalHours"]] = relationship(back_populates="asset", cascade="all, delete-orphan") class Role(Base): __tablename__ = "roles" id: Mapped[int] = mapped_column(primary_key=True) discipline: Mapped[str] = mapped_column(String(100), nullable=False, index=True) role_title: Mapped[str] = mapped_column(String(200), nullable=False) entity: Mapped[str | None] = mapped_column(String(100)) resource_location: Mapped[str | None] = mapped_column(String(50)) unique_name: Mapped[str | None] = mapped_column(String(255)) sort_order: Mapped[int | None] = mapped_column(Integer) is_programme_role: Mapped[bool] = mapped_column(Boolean, default=False) __table_args__ = ( UniqueConstraint("role_title", "entity", name="uq_role_title_entity"), ) hours: Mapped[list["GmalHours"]] = relationship(back_populates="role") class GmalHours(Base): __tablename__ = "gmal_hours" id: Mapped[int] = mapped_column(primary_key=True) gmal_asset_id: Mapped[int] = mapped_column(ForeignKey("gmal_assets.id"), nullable=False) role_id: Mapped[int] = mapped_column(ForeignKey("roles.id"), nullable=False) model_type: Mapped[ModelType] = mapped_column(Enum(ModelType), nullable=False) hours: Mapped[float] = mapped_column(Numeric(10, 2), nullable=False) asset: Mapped["GmalAsset"] = relationship(back_populates="hours") role: Mapped["Role"] = relationship(back_populates="hours") __table_args__ = ( UniqueConstraint("gmal_asset_id", "role_id", "model_type", name="uq_gmal_role_model"), Index("idx_gmal_hours_asset", "gmal_asset_id"), Index("idx_gmal_hours_model", "model_type"), ) class GmalServiceLine(Base): __tablename__ = "gmal_service_lines" id: Mapped[int] = mapped_column(primary_key=True) number: Mapped[str | None] = mapped_column(String(20)) name: Mapped[str | None] = mapped_column(String(255)) type: Mapped[str | None] = mapped_column(String(50)) gmal_id: Mapped[str | None] = mapped_column(String(50), index=True) class RoleLevelMapping(Base): __tablename__ = "role_level_mappings" id: Mapped[int] = mapped_column(primary_key=True) role_name: Mapped[str | None] = mapped_column(String(200)) number: Mapped[str | None] = mapped_column(String(20)) level_name: Mapped[str | None] = mapped_column(String(200)) type: Mapped[str | None] = mapped_column(String(50))