Checkpoint React frontend migration

This commit is contained in:
Dwindi Ramadhana
2026-06-20 01:43:39 +07:00
parent ab86c254d1
commit b8e201b45f
173 changed files with 34116 additions and 782 deletions

View File

@@ -0,0 +1,200 @@
"""
Tryout model with configuration for assessment sessions.
Represents tryout exams with configurable scoring, selection, and normalization modes.
"""
from datetime import datetime
from typing import Literal, Union
from sqlalchemy import (
Boolean,
CheckConstraint,
DateTime,
Float,
ForeignKey,
Integer,
String,
UniqueConstraint,
func,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
class Tryout(Base):
"""
Tryout model with configuration for assessment sessions.
Supports multiple scoring modes (CTT, IRT, hybrid), selection strategies
(fixed, adaptive, hybrid), and normalization modes (static, dynamic, hybrid).
Attributes:
id: Primary key
website_id: Website identifier
tryout_id: Tryout identifier (unique per website)
name: Tryout name
description: Tryout description
scoring_mode: Scoring algorithm (ctt, irt, hybrid)
selection_mode: Item selection strategy (fixed, adaptive, hybrid)
normalization_mode: Normalization method (static, dynamic, hybrid)
min_sample_for_dynamic: Minimum sample size for dynamic normalization
static_rataan: Static mean value for manual normalization
static_sb: Static standard deviation for manual normalization
AI_generation_enabled: Enable/disable AI question generation
hybrid_transition_slot: Slot number to transition from fixed to adaptive
min_calibration_sample: Minimum responses needed for IRT calibration
theta_estimation_method: Method for estimating theta (mle, map, eap)
fallback_to_ctt_on_error: Fallback to CTT if IRT fails
created_at: Record creation timestamp
updated_at: Record update timestamp
website: Website relationship
items: Items in this tryout
sessions: Sessions for this tryout
stats: Tryout statistics
"""
__tablename__ = "tryouts"
# Primary key
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
# Foreign keys
website_id: Mapped[int] = mapped_column(
ForeignKey("websites.id", ondelete="CASCADE", onupdate="CASCADE"),
nullable=False,
index=True,
comment="Website identifier",
)
# Tryout identifier (unique per website)
tryout_id: Mapped[str] = mapped_column(
String(255),
nullable=False,
index=True,
comment="Tryout identifier (unique per website)",
)
# Basic information
name: Mapped[str] = mapped_column(
String(255), nullable=False, comment="Tryout name"
)
description: Mapped[Union[str, None]] = mapped_column(
String(1000), nullable=True, comment="Tryout description"
)
# Scoring mode: ctt (Classical Test Theory), irt (Item Response Theory), hybrid
scoring_mode: Mapped[Literal["ctt", "irt", "hybrid"]] = mapped_column(
String(50), nullable=False, default="ctt", comment="Scoring mode"
)
# Selection mode: fixed (slot order), adaptive (CAT), hybrid (mixed)
selection_mode: Mapped[Literal["fixed", "adaptive", "hybrid"]] = mapped_column(
String(50), nullable=False, default="fixed", comment="Item selection mode"
)
# Normalization mode: static (hardcoded), dynamic (real-time), hybrid
normalization_mode: Mapped[Literal["static", "dynamic", "hybrid"]] = mapped_column(
String(50), nullable=False, default="static", comment="Normalization mode"
)
# Normalization settings
min_sample_for_dynamic: Mapped[int] = mapped_column(
Integer,
nullable=False,
default=100,
comment="Minimum sample size for dynamic normalization",
)
static_rataan: Mapped[float] = mapped_column(
Float,
nullable=False,
default=500.0,
comment="Static mean value for manual normalization",
)
static_sb: Mapped[float] = mapped_column(
Float,
nullable=False,
default=100.0,
comment="Static standard deviation for manual normalization",
)
# AI generation settings
ai_generation_enabled: Mapped[bool] = mapped_column(
Boolean,
nullable=False,
default=False,
comment="Enable/disable AI question generation",
)
# Hybrid mode settings
hybrid_transition_slot: Mapped[Union[int, None]] = mapped_column(
Integer,
nullable=True,
comment="Slot number to transition from fixed to adaptive (hybrid mode)",
)
# IRT settings
min_calibration_sample: Mapped[int] = mapped_column(
Integer,
nullable=False,
default=100,
comment="Minimum responses needed for IRT calibration",
)
theta_estimation_method: Mapped[Literal["mle", "map", "eap"]] = mapped_column(
String(50),
nullable=False,
default="mle",
comment="Method for estimating theta",
)
fallback_to_ctt_on_error: Mapped[bool] = mapped_column(
Boolean,
nullable=False,
default=True,
comment="Fallback to CTT if IRT fails",
)
# Timestamps
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
nullable=False,
server_default=func.now(),
onupdate=func.now(),
)
# Relationships
website: Mapped["Website"] = relationship(
"Website", back_populates="tryouts", lazy="selectin"
)
items: Mapped[list["Item"]] = relationship(
"Item", back_populates="tryout", lazy="selectin", cascade="all, delete-orphan"
)
sessions: Mapped[list["Session"]] = relationship(
"Session",
back_populates="tryout",
lazy="selectin",
cascade="all, delete-orphan",
overlaps="user",
)
stats: Mapped["TryoutStats"] = relationship(
"TryoutStats", back_populates="tryout", lazy="selectin", uselist=False
)
# Constraints and indexes
__table_args__ = (
UniqueConstraint(
"website_id",
"tryout_id",
name="uq_tryouts_website_id_tryout_id",
),
CheckConstraint("min_sample_for_dynamic > 0", "ck_min_sample_positive"),
CheckConstraint("static_rataan > 0", "ck_static_rataan_positive"),
CheckConstraint("static_sb > 0", "ck_static_sb_positive"),
CheckConstraint("min_calibration_sample > 0", "ck_min_calibration_positive"),
)
def __repr__(self) -> str:
return f"<Tryout(id={self.id}, tryout_id={self.tryout_id}, website_id={self.website_id})>"