Add basis workspace filters, stale-on-reimport, and variant usage metrics
This commit is contained in:
@@ -5,6 +5,7 @@ Exports all SQLAlchemy ORM models for use in the application.
|
||||
"""
|
||||
|
||||
from app.database import Base
|
||||
from app.models.ai_generation_run import AIGenerationRun
|
||||
from app.models.item import Item
|
||||
from app.models.session import Session
|
||||
from app.models.tryout import Tryout
|
||||
@@ -17,6 +18,7 @@ from app.models.website import Website
|
||||
|
||||
__all__ = [
|
||||
"Base",
|
||||
"AIGenerationRun",
|
||||
"User",
|
||||
"Website",
|
||||
"Tryout",
|
||||
|
||||
72
app/models/ai_generation_run.py
Normal file
72
app/models/ai_generation_run.py
Normal file
@@ -0,0 +1,72 @@
|
||||
"""
|
||||
AI generation run model.
|
||||
|
||||
Represents one admin generation request that can produce one or many variants.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import DateTime, ForeignKey, Integer, String, Text, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class AIGenerationRun(Base):
|
||||
__tablename__ = "ai_generation_runs"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
basis_item_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("items.id", ondelete="CASCADE", onupdate="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
comment="Basis item ID",
|
||||
)
|
||||
source_snapshot_question_id: Mapped[Optional[int]] = mapped_column(
|
||||
ForeignKey("tryout_snapshot_questions.id", ondelete="SET NULL", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
comment="Source snapshot question ID",
|
||||
)
|
||||
target_level: Mapped[str] = mapped_column(
|
||||
String(50),
|
||||
nullable=False,
|
||||
comment="Target level (mudah/sulit)",
|
||||
)
|
||||
requested_count: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
nullable=False,
|
||||
default=1,
|
||||
comment="Requested output count",
|
||||
)
|
||||
model: Mapped[str] = mapped_column(
|
||||
String(255),
|
||||
nullable=False,
|
||||
comment="Model identifier",
|
||||
)
|
||||
prompt_version: Mapped[str] = mapped_column(
|
||||
String(50),
|
||||
nullable=False,
|
||||
default="v1",
|
||||
comment="Prompt template version",
|
||||
)
|
||||
operator_notes: Mapped[Optional[str]] = mapped_column(
|
||||
Text,
|
||||
nullable=True,
|
||||
comment="Optional admin notes",
|
||||
)
|
||||
created_by: Mapped[str] = mapped_column(
|
||||
String(255),
|
||||
nullable=False,
|
||||
comment="Admin username",
|
||||
)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now()
|
||||
)
|
||||
|
||||
generated_items: Mapped[list["Item"]] = relationship(
|
||||
"Item",
|
||||
back_populates="generation_run",
|
||||
lazy="selectin",
|
||||
)
|
||||
@@ -155,6 +155,39 @@ class Item(Base):
|
||||
nullable=True,
|
||||
comment="Original item ID (for AI variants)",
|
||||
)
|
||||
generation_run_id: Mapped[Union[int, None]] = mapped_column(
|
||||
ForeignKey("ai_generation_runs.id", ondelete="SET NULL", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
comment="AI generation run ID",
|
||||
)
|
||||
source_snapshot_question_id: Mapped[Union[int, None]] = mapped_column(
|
||||
ForeignKey("tryout_snapshot_questions.id", ondelete="SET NULL", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
comment="Source snapshot question ID",
|
||||
)
|
||||
variant_status: Mapped[str] = mapped_column(
|
||||
String(50),
|
||||
nullable=False,
|
||||
default="active",
|
||||
comment="Lifecycle status (active/draft/approved/rejected/archived/stale)",
|
||||
)
|
||||
reviewed_by: Mapped[Union[str, None]] = mapped_column(
|
||||
String(255),
|
||||
nullable=True,
|
||||
comment="Reviewer username",
|
||||
)
|
||||
reviewed_at: Mapped[Union[datetime, None]] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
nullable=True,
|
||||
comment="Review timestamp",
|
||||
)
|
||||
review_notes: Mapped[Union[str, None]] = mapped_column(
|
||||
Text,
|
||||
nullable=True,
|
||||
comment="Review notes",
|
||||
)
|
||||
|
||||
# Timestamps
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
@@ -187,6 +220,11 @@ class Item(Base):
|
||||
lazy="selectin",
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
generation_run: Mapped[Union["AIGenerationRun", None]] = relationship(
|
||||
"AIGenerationRun",
|
||||
back_populates="generated_items",
|
||||
lazy="selectin",
|
||||
)
|
||||
|
||||
# Constraints and indexes
|
||||
__table_args__ = (
|
||||
@@ -203,10 +241,11 @@ class Item(Base):
|
||||
"website_id",
|
||||
"slot",
|
||||
"level",
|
||||
unique=True,
|
||||
unique=False,
|
||||
),
|
||||
Index("ix_items_calibrated", "calibrated"),
|
||||
Index("ix_items_basis_item_id", "basis_item_id"),
|
||||
Index("ix_items_variant_status", "variant_status"),
|
||||
# IRT b parameter constraint [-3, +3]
|
||||
CheckConstraint(
|
||||
"irt_b IS NULL OR (irt_b >= -3 AND irt_b <= 3)",
|
||||
|
||||
Reference in New Issue
Block a user