MicroFish/backend/app/models/interview.py

100 lines
2.9 KiB
Python

from __future__ import annotations
from enum import Enum
from typing import Optional
from pydantic import BaseModel, Field, field_validator, model_validator
class InterviewPhase(str, Enum):
T0 = "T0"
T1 = "T1"
class SubagentKind(str, Enum):
LONGITUDINAL = "longitudinal"
DIVERSITY = "diversity"
DELPHI = "delphi"
SCENARIO = "scenario"
class LikertItem(BaseModel):
item_id: str
de: str
en: str
scale: int = Field(ge=3, le=7)
family: Optional[str] = None
reverse_coded: bool = False
@field_validator("scale")
@classmethod
def odd_scale(cls, v: int) -> int:
if v not in (3, 5, 7):
raise ValueError("scale must be 3, 5, or 7")
return v
class LikertInstrument(BaseModel):
name: str
version: str = "1.0"
language_default: str = "de"
items: list[LikertItem]
@model_validator(mode="after")
def unique_item_ids(self) -> "LikertInstrument":
ids = [i.item_id for i in self.items]
if len(set(ids)) != len(ids):
raise ValueError("duplicate item_id in instrument")
return self
class LikertResponse(BaseModel):
agent_id: int
phase: InterviewPhase
responses: dict[str, int]
confidence: dict[str, float] = Field(default_factory=dict)
open_comment: Optional[str] = None
memory_available: bool = True
failed_items: list[str] = Field(default_factory=list)
@model_validator(mode="after")
def values_in_range(self) -> "LikertResponse":
for k, v in self.responses.items():
if not 1 <= v <= 5:
raise ValueError(f"response {k}={v} out of 1..5 range")
for k, v in self.confidence.items():
if not 0.0 <= v <= 1.0:
raise ValueError(f"confidence {k}={v} out of 0..1 range")
return self
class QSortStatement(BaseModel):
statement_id: str
de: str
en: str
class QSortInstrument(BaseModel):
name: str
version: str = "1.0"
statements: list[QSortStatement]
distribution: list[int] # e.g. [2,3,4,6,4,3,2] for -3..+3
class QSortResponse(BaseModel):
agent_id: int
placements: dict[str, int] # statement_id -> bucket (-3..+3)
likert_axes: dict[str, int] # axis_id -> 1..7
class DelphiOpenResponse(BaseModel):
agent_id: int
round: int = 1
answers: dict[str, str] # question_id -> free text
class DelphiRatingResponse(BaseModel):
agent_id: int
round: int
ratings: dict[str, dict[str, int]] # theme_id -> {importance, plausibility}
justification: Optional[str] = None
class ScenarioRating(BaseModel):
desirability: int = Field(ge=1, le=7)
plausibility: int = Field(ge=1, le=7)
impact_on_my_group: int = Field(ge=1, le=7)
fairness: int = Field(ge=1, le=7)
if_woke_up_response: str
class ScenarioResponse(BaseModel):
agent_id: int
ratings: dict[str, ScenarioRating] # scenario_id -> rating