""" Assessment Model — ORM + Pydantic Schemas ORM: Assessment (SQLAlchemy table: assessments) Schemas: AssessmentCreate, AssessmentResponse Stores individual Ghost Shopper evaluation items for each visit. Each visit has ~21 assessment items grouped into 5 categories (A–E). Categories: A = Pelayanan Awal (Frontliner/Kasir) — 5 items B = Kualitas Produk (Minuman & Makanan) — 5 items C = Kebersihan dan Suasana Outlet — 4 items D = Kepatuhan SOP & Keselamatan — 2 items E = Keseluruhan Pengalaman Pelanggan — 5 items All ratings are normalized to a 1.0–5.0 float scale regardless of the original input format (Ya/Tidak, Qualitative, or Numeric). """ import uuid from datetime import datetime from typing import Optional from sqlalchemy import String, Text, Float, Integer, DateTime, ForeignKey, func from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import Mapped, mapped_column, relationship from pydantic import BaseModel, Field, ConfigDict from models.base import Base # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # ORM Model # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ class Assessment(Base): """SQLAlchemy ORM model for the 'assessments' table.""" __tablename__ = "assessments" id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), primary_key=True, default=uuid.uuid4 ) visit_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("visits.id", ondelete="CASCADE"), nullable=False, ) category: Mapped[str] = mapped_column( String(1), nullable=False, comment="Assessment category: A, B, C, D, or E" ) item_no: Mapped[int] = mapped_column( Integer, nullable=False, comment="Item sequence number within the category (1-based)" ) criteria: Mapped[str] = mapped_column( String(500), nullable=False, comment="Assessment question/criteria text" ) score: Mapped[float] = mapped_column( Float, nullable=False, comment="Normalized score 1.0 to 5.0" ) raw_value: Mapped[str] = mapped_column( String(100), nullable=False, comment="Original value from document: 'Baik', 'Ya', '4', etc." ) notes: Mapped[Optional[str]] = mapped_column( Text, nullable=True, comment="Catatan / notes from the Ghost Shopper" ) description: Mapped[Optional[str]] = mapped_column( Text, nullable=True, comment="Detailed evaluator description / observation for this item" ) created_at: Mapped[datetime] = mapped_column( DateTime, server_default=func.now() ) # Relationships visit = relationship("Visit", back_populates="assessments") photos = relationship( "Photo", back_populates="assessment", cascade="all, delete-orphan", foreign_keys="Photo.assessment_id", ) def __repr__(self) -> str: return ( f"" ) # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # Pydantic Schemas # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ class AssessmentCreate(BaseModel): """Schema for creating an assessment item.""" category: str = Field(..., min_length=1, max_length=1, description="Category A-E") item_no: int = Field(..., ge=1, le=10, description="Item number within category") criteria: str = Field(..., min_length=1, max_length=500, description="Criteria text") score: float = Field(..., ge=1.0, le=5.0, description="Normalized score 1.0-5.0") raw_value: str = Field(..., max_length=100, description="Original raw value") notes: Optional[str] = Field(None, description="Catatan from evaluator") description: Optional[str] = Field(None, description="Detailed observation/description for this item") class AssessmentResponse(BaseModel): """Schema for an assessment item in API responses.""" model_config = ConfigDict(from_attributes=True) id: uuid.UUID visit_id: uuid.UUID category: str item_no: int criteria: str score: float raw_value: str notes: Optional[str] description: Optional[str] created_at: datetime