aboutsummaryrefslogtreecommitdiff
path: root/generalresearch/models/thl/profiling/upk_question.py
diff options
context:
space:
mode:
Diffstat (limited to 'generalresearch/models/thl/profiling/upk_question.py')
-rw-r--r--generalresearch/models/thl/profiling/upk_question.py80
1 files changed, 40 insertions, 40 deletions
diff --git a/generalresearch/models/thl/profiling/upk_question.py b/generalresearch/models/thl/profiling/upk_question.py
index 5c908b0..307bc33 100644
--- a/generalresearch/models/thl/profiling/upk_question.py
+++ b/generalresearch/models/thl/profiling/upk_question.py
@@ -5,7 +5,7 @@ import json
import re
from enum import Enum
from functools import cached_property
-from typing import Any, Dict, List, Literal, Optional, Set, Tuple, Union
+from typing import Any, List, Literal, Union
from pydantic import (
BaseModel,
@@ -24,21 +24,21 @@ from generalresearch.models.thl.category import Category
class UPKImportance(BaseModel):
- task_count: Optional[int] = Field(
+ task_count: int | None = Field(
ge=0,
default=None,
examples=[47],
description="The number of live Tasks that use this UPK Question",
)
- task_score: Optional[float] = Field(
+ task_score: float | None = Field(
ge=0,
default=None,
examples=[0.11175522477414712],
description="GRL's internal ranked score for the UPK Question",
)
- marketplace_task_count: Optional[Dict[Source, NonNegativeInt]] = Field(
+ marketplace_task_count: dict[Source, NonNegativeInt] | None = Field(
default=None,
examples=[{Source.DYNATA: 23, Source.SPECTRUM: 24}],
description="The number of live Tasks that use this UPK Question per marketplace",
@@ -80,14 +80,14 @@ class UpkQuestionChoice(BaseModel):
order: NonNegativeInt = Field()
# Allows you to group answer choices together (used for display or extra logic)
- group: Optional[int] = Field(default=None)
+ group: int | None = Field(default=None)
exclusive: bool = Field(
default=False,
description="If answer is exclusive, it can be the only option selected",
)
- importance: Optional[UPKImportance] = Field(default=None)
+ importance: UPKImportance | None = Field(default=None)
def __hash__(self):
# We don't know the question ID!! Unique within a question only!
@@ -151,7 +151,7 @@ class UpkQuestionConfigurationMC(BaseModel):
exclude=True, default=UpkQuestionType.MULTIPLE_CHOICE
)
- max_select: Optional[int] = Field(gt=0, default=None)
+ max_select: int | None = Field(gt=0, default=None)
class UpkQuestionConfigurationTE(BaseModel):
@@ -163,7 +163,7 @@ class UpkQuestionConfigurationTE(BaseModel):
)
# Sets input form attribute; not the same as regex validation
- max_length: Optional[PositiveInt] = Field(
+ max_length: PositiveInt | None = Field(
default=None,
description="Maximum str length of any input. Meant as an easy, non"
"regex based check.",
@@ -171,7 +171,7 @@ class UpkQuestionConfigurationTE(BaseModel):
# The text input box must contain this number of chars before submission
# is allowed
- min_length: Optional[PositiveInt] = Field(
+ min_length: PositiveInt | None = Field(
default=None,
description="Minimum str length of any input. Meant as an easy, non"
"regex based check.",
@@ -195,25 +195,25 @@ class UpkQuestionConfigurationSLIDER(BaseModel):
)
# TODO: constraints. we don't have any of these so not wasting time on this
- slider_min: Optional[float] = Field(default=None)
- slider_max: Optional[float] = Field(default=None)
- slider_start: Optional[float] = Field(default=None)
- slider_step: Optional[float] = Field(default=None)
+ slider_min: float | None = Field(default=None)
+ slider_max: float | None = Field(default=None)
+ slider_start: float | None = Field(default=None)
+ slider_step: float | None = Field(default=None)
class UpkQuestionValidation(BaseModel):
model_config = ConfigDict(frozen=True)
# --- UpkQuestionType.TEXT_ENTRY Options ---
- patterns: Optional[List[PatternValidation]] = Field(min_length=1)
+ patterns: list[PatternValidation] | None = Field(min_length=1)
-SelectorType = Union[
- UpkQuestionSelectorMC,
- UpkQuestionSelectorTE,
- UpkQuestionSelectorSLIDER,
- UpkQuestionSelectorHIDDEN,
-]
+SelectorType = (
+ UpkQuestionSelectorMC
+ | UpkQuestionSelectorTE
+ | UpkQuestionSelectorSLIDER
+ | UpkQuestionSelectorHIDDEN
+)
Configuration = Annotated[
Union[
UpkQuestionConfigurationMC,
@@ -276,11 +276,11 @@ class UpkQuestion(BaseModel):
)
# The id is globally unique
- id: Optional[UUIDStr] = Field(default=None, alias="question_id")
+ id: UUIDStr | None = Field(default=None, alias="question_id")
# The format is "{Source}:{question_id}" where Source is 1 or 2 chars, and
# question_id is the marketplace's ID for this question.
- ext_question_id: Optional[str] = Field(
+ ext_question_id: str | None = Field(
default=None,
description="what marketplace question this question links to",
pattern=r"^[a-z]{1,2}\:.*",
@@ -301,25 +301,25 @@ class UpkQuestion(BaseModel):
# Don't set a min_length=1 here. We'll allow this to be created, but it
# won't be askable with empty choices.
- choices: Optional[List[UpkQuestionChoice]] = Field(default=None)
+ choices: list[UpkQuestionChoice] | None = Field(default=None)
selector: SelectorType = Field()
- configuration: Optional[Configuration] = Field(default=None)
- validation: Optional[UpkQuestionValidation] = Field(default=None)
- importance: Optional[UPKImportance] = Field(default=None)
+ configuration: Configuration | None = Field(default=None)
+ validation: UpkQuestionValidation | None = Field(default=None)
+ importance: UPKImportance | None = Field(default=None)
- categories: List[Category] = Field(
+ categories: list[Category] = Field(
default_factory=list,
description="Categories associated with this question",
)
- explanation_template: Optional[str] = Field(
+ explanation_template: str | None = Field(
description="Human-readable template for explaining how a user's answer to this question affects eligibility",
examples=[
"The company that administers your employer's retirement plan is {answer}."
],
default=None,
)
- explanation_fragment_template: Optional[str] = Field(
+ explanation_fragment_template: str | None = Field(
description="A very short, natural-language explanation fragment that can be combined with others into a single sentence",
examples=["whose employer's retirement plan is {answer}"],
default=None,
@@ -336,7 +336,7 @@ class UpkQuestion(BaseModel):
return self.country_iso + "_" + self.language_iso
@property
- def source(self) -> Optional[Source]:
+ def source(self) -> Source | None:
if self.ext_question_id:
return Source(self.ext_question_id.split(":", 1)[0])
@@ -348,7 +348,7 @@ class UpkQuestion(BaseModel):
@model_validator(mode="before")
@classmethod
- def check_configuration_type(cls, data: Dict[str, Any]) -> Dict[str, Any]:
+ def check_configuration_type(cls, data: dict[str, Any]) -> dict[str, Any]:
# The model knows what the type of Configuration to grab depending on
# the key 'type' which it expects inside the configuration object.
# Here, we grab the type from the top-level model instead.
@@ -441,8 +441,8 @@ class UpkQuestion(BaseModel):
@field_validator("choices")
@classmethod
def validate_choices(
- cls, choices: Optional[List[UpkQuestionChoice]]
- ) -> Optional[List[UpkQuestionChoice]]:
+ cls, choices: list[UpkQuestionChoice] | None
+ ) -> list[UpkQuestionChoice] | None:
if choices:
ids = {x.id for x in choices}
assert len(ids) == len(choices), "choices.id must be unique"
@@ -507,7 +507,7 @@ class UpkQuestion(BaseModel):
d.update(d.pop("importance", {}))
return d
- def validate_question_answer(self, answer: Tuple[str, ...]) -> Tuple[bool, str]:
+ def validate_question_answer(self, answer: tuple[str, ...]) -> tuple[bool, str]:
"""
Returns (is_valid, error_message).
"""
@@ -518,7 +518,7 @@ class UpkQuestion(BaseModel):
else:
return True, ""
- def _validate_question_answer(self, answer: Tuple[str, ...]) -> None:
+ def _validate_question_answer(self, answer: tuple[str, ...]) -> None:
"""
If the question is MC, validate:
- validate selector SA vs MA (1 selected vs >1 selected)
@@ -578,24 +578,24 @@ class UpkQuestion(BaseModel):
class UpkQuestionOut(UpkQuestion):
- choices: Optional[List[UpkQuestionChoiceOut]] = Field(default=None)
+ choices: list[UpkQuestionChoiceOut] | None = Field(default=None)
# Return both importance top-level model and extracted keys for now.
# Eventually deprecate one way.
- task_count: Optional[int] = Field(
+ task_count: int | None = Field(
ge=0,
default=None,
examples=[47],
description="The number of live Tasks that use this UPK Question",
)
- task_score: Optional[float] = Field(
+ task_score: float | None = Field(
ge=0,
default=None,
examples=[0.11175522477414712],
description="GRL's internal ranked score for the UPK Question",
)
- marketplace_task_count: Optional[Dict[Source, NonNegativeInt]] = Field(
+ marketplace_task_count: dict[Source, NonNegativeInt] | None = Field(
default=None,
examples=[{Source.DYNATA: 23, Source.SPECTRUM: 24}],
description="The number of live Tasks that use this UPK Question per marketplace",
@@ -649,7 +649,7 @@ def trim_options(q: UpkQuestion, max_options: int = 50) -> UpkQuestion:
if q.ext_question_id.startswith("gr:") or q.ext_question_id.startswith("g:"):
return q
- special_choices: Set[UpkQuestionChoice] = {
+ special_choices: set[UpkQuestionChoice] = {
c for c in q.choices if option_is_catch_all(c)
}