"""Measurement model for storing individual measurements.""" from datetime import datetime from typing import Optional from sqlalchemy import ( BigInteger, Boolean, DECIMAL, DateTime, Enum, ForeignKey, Integer, String, func, ) from sqlalchemy.orm import Mapped, mapped_column from src.backend.database import Base class Measurement(Base): __tablename__ = "measurements" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) subtask_id: Mapped[int] = mapped_column( Integer, ForeignKey("recipe_subtasks.id"), nullable=False, index=True ) version_id: Mapped[int] = mapped_column( Integer, ForeignKey("recipe_versions.id"), nullable=False, index=True ) measured_by: Mapped[int] = mapped_column( Integer, ForeignKey("users.id"), nullable=False, index=True ) # Measurement data value: Mapped[float] = mapped_column(DECIMAL(12, 6), nullable=False) pass_fail: Mapped[str] = mapped_column( Enum("pass", "warning", "fail", name="pass_fail_enum"), nullable=False ) deviation: Mapped[Optional[float]] = mapped_column(DECIMAL(12, 6), nullable=True) # Traceability lot_number: Mapped[Optional[str]] = mapped_column(String(100), nullable=True, index=True) serial_number: Mapped[Optional[str]] = mapped_column(String(100), nullable=True, index=True) # Input method input_method: Mapped[str] = mapped_column( Enum("usb_caliper", "manual", "camera", name="input_method_enum"), nullable=False, default="manual", ) # The vision execution that produced this value, when there was one. Null for # everything taken by caliper or typed by hand. vision_result_id: Mapped[Optional[int]] = mapped_column( Integer, ForeignKey("vision_results.id"), nullable=True ) # Time spent by the operator entering this value (client-reported) input_duration_ms: Mapped[Optional[int]] = mapped_column( Integer, nullable=True ) # The production this was taken during, when there was one. Nullable: rows # predating production runs, and measurements taken outside a production, have # none. This is what makes "the measurements of this production" a query, which # fine produzione needs to emit the statistics file. production_run_id: Mapped[Optional[int]] = mapped_column( Integer, ForeignKey("production_runs.id", ondelete="SET NULL"), nullable=True, index=True, ) # Who let an out-of-tolerance value stand, and when. Null on a value that is # within tolerance, and null on one that is not and has not been authorised - # which is exactly the state that stops the operator moving on to the next # quote. Before this the supervisor's approval was a modal that closed: nothing # recorded it, and nothing depended on it. supervisor_id: Mapped[Optional[int]] = mapped_column( Integer, ForeignKey("users.id"), nullable=True, index=True ) authorised_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) # Timestamp measured_at: Mapped[datetime] = mapped_column( DateTime, nullable=False, server_default=func.now(), index=True ) # CSV sync flag synced_to_csv: Mapped[bool] = mapped_column(Boolean, default=False) def __repr__(self) -> str: return f""