255 lines
8.5 KiB
Python
255 lines
8.5 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Any, Literal
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
|
|
|
|
PollKind = Literal["single_choice", "multiple_choice", "yes_no", "yes_no_maybe", "ranked_choice", "availability"]
|
|
PollStatus = Literal["draft", "open", "closed", "decided", "archived"]
|
|
PollTransitionAction = Literal["open", "draft", "close", "decide", "archive", "unarchive"]
|
|
PollVisibility = Literal["private", "tenant", "public", "unlisted"]
|
|
PollResultVisibility = Literal["organizer", "after_response", "after_close", "public"]
|
|
AvailabilityValue = Literal["available", "maybe", "unavailable"]
|
|
|
|
|
|
class PollOptionInput(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
key: str | None = Field(default=None, min_length=1, max_length=120)
|
|
label: str = Field(min_length=1, max_length=500)
|
|
description: str | None = None
|
|
value: dict[str, Any] | None = None
|
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class PollCreateRequest(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
title: str = Field(min_length=1, max_length=500)
|
|
slug: str | None = Field(default=None, max_length=120)
|
|
description: str | None = None
|
|
kind: PollKind = "single_choice"
|
|
status: PollStatus = "draft"
|
|
visibility: PollVisibility = "private"
|
|
result_visibility: PollResultVisibility = "after_close"
|
|
context_module: str | None = Field(default=None, max_length=100)
|
|
context_resource_type: str | None = Field(default=None, max_length=100)
|
|
context_resource_id: str | None = Field(default=None, max_length=255)
|
|
workflow_state: str | None = Field(default=None, max_length=100)
|
|
workflow_steps: list[dict[str, Any]] = Field(default_factory=list)
|
|
allow_anonymous: bool = False
|
|
allow_response_update: bool = True
|
|
min_choices: int = Field(default=1, ge=0)
|
|
max_choices: int | None = Field(default=None, ge=1)
|
|
opens_at: datetime | None = None
|
|
closes_at: datetime | None = None
|
|
options: list[PollOptionInput] = Field(default_factory=list)
|
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
@model_validator(mode="after")
|
|
def validate_window(self) -> "PollCreateRequest":
|
|
if self.opens_at is not None and self.closes_at is not None and self.closes_at <= self.opens_at:
|
|
raise ValueError("closes_at must be after opens_at")
|
|
return self
|
|
|
|
|
|
class PollUpdateRequest(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
title: str | None = Field(default=None, min_length=1, max_length=500)
|
|
description: str | None = None
|
|
visibility: PollVisibility | None = None
|
|
result_visibility: PollResultVisibility | None = None
|
|
context_module: str | None = Field(default=None, max_length=100)
|
|
context_resource_type: str | None = Field(default=None, max_length=100)
|
|
context_resource_id: str | None = Field(default=None, max_length=255)
|
|
workflow_state: str | None = Field(default=None, max_length=100)
|
|
workflow_steps: list[dict[str, Any]] | None = None
|
|
allow_anonymous: bool | None = None
|
|
allow_response_update: bool | None = None
|
|
min_choices: int | None = Field(default=None, ge=0)
|
|
max_choices: int | None = Field(default=None, ge=1)
|
|
opens_at: datetime | None = None
|
|
closes_at: datetime | None = None
|
|
metadata: dict[str, Any] | None = None
|
|
|
|
|
|
class PollOptionResponse(BaseModel):
|
|
id: str
|
|
key: str
|
|
label: str
|
|
description: str | None = None
|
|
position: int
|
|
value: dict[str, Any] | None = None
|
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class PollTransitionAvailabilityResponse(BaseModel):
|
|
action: PollTransitionAction
|
|
target_status: PollStatus | None = None
|
|
available: bool
|
|
reason: str | None = None
|
|
|
|
|
|
class PollResponseItem(BaseModel):
|
|
id: str
|
|
respondent_id: str | None = None
|
|
respondent_label: str | None = None
|
|
answers: list[dict[str, Any]] = Field(default_factory=list)
|
|
submitted_at: datetime
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class PollResponse(BaseModel):
|
|
id: str
|
|
tenant_id: str
|
|
slug: str
|
|
title: str
|
|
description: str | None = None
|
|
kind: str
|
|
status: str
|
|
visibility: str
|
|
result_visibility: str
|
|
context_module: str | None = None
|
|
context_resource_type: str | None = None
|
|
context_resource_id: str | None = None
|
|
workflow_state: str | None = None
|
|
workflow_steps: list[dict[str, Any]] = Field(default_factory=list)
|
|
allow_anonymous: bool
|
|
allow_response_update: bool
|
|
min_choices: int
|
|
max_choices: int | None = None
|
|
opens_at: datetime | None = None
|
|
closes_at: datetime | None = None
|
|
closed_at: datetime | None = None
|
|
decided_at: datetime | None = None
|
|
decided_option_id: str | None = None
|
|
archived_from_status: str | None = None
|
|
created_by_user_id: str | None = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
options: list[PollOptionResponse] = Field(default_factory=list)
|
|
lifecycle_actions: list[PollTransitionAvailabilityResponse] = Field(default_factory=list)
|
|
|
|
|
|
class PollListResponse(BaseModel):
|
|
polls: list[PollResponse] = Field(default_factory=list)
|
|
|
|
|
|
class PollAnswerInput(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
option_id: str | None = None
|
|
option_key: str | None = None
|
|
value: Any = None
|
|
rank: int | None = Field(default=None, ge=1)
|
|
|
|
|
|
class PollSubmitResponseRequest(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
respondent_id: str | None = Field(default=None, max_length=255)
|
|
respondent_label: str | None = Field(default=None, max_length=500)
|
|
answers: list[PollAnswerInput] = Field(default_factory=list)
|
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class PollOptionResultResponse(BaseModel):
|
|
option_id: str
|
|
option_key: str
|
|
label: str
|
|
count: int = 0
|
|
score: int = 0
|
|
values: dict[str, int] = Field(default_factory=dict)
|
|
ranks: dict[int, int] = Field(default_factory=dict)
|
|
|
|
|
|
class PollResultSummaryResponse(BaseModel):
|
|
poll_id: str
|
|
kind: str
|
|
status: str
|
|
response_count: int
|
|
option_results: list[PollOptionResultResponse] = Field(default_factory=list)
|
|
leading_option_ids: list[str] = Field(default_factory=list)
|
|
|
|
|
|
class PollDecisionRequest(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
option_id: str | None = None
|
|
option_key: str | None = None
|
|
|
|
|
|
class PollTransitionRequest(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
action: PollTransitionAction
|
|
option_id: str | None = None
|
|
option_key: str | None = None
|
|
|
|
|
|
class PollLifecycleTransitionResponse(BaseModel):
|
|
id: str
|
|
action: PollTransitionAction
|
|
from_status: PollStatus
|
|
to_status: PollStatus
|
|
decision_option_id: str | None = None
|
|
previous_decision_option_id: str | None = None
|
|
idempotency_key: str | None = None
|
|
actor_user_id: str | None = None
|
|
actor_api_key_id: str | None = None
|
|
created_at: datetime
|
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class PollStatusResponse(BaseModel):
|
|
poll: PollResponse
|
|
transition: PollLifecycleTransitionResponse | None = None
|
|
replayed: bool = False
|
|
|
|
|
|
class PollLifecycleResponse(BaseModel):
|
|
poll_id: str
|
|
status: PollStatus
|
|
archived_from_status: str | None = None
|
|
actions: list[PollTransitionAvailabilityResponse] = Field(default_factory=list)
|
|
history: list[PollLifecycleTransitionResponse] = Field(default_factory=list)
|
|
|
|
|
|
class PollResponseListResponse(BaseModel):
|
|
responses: list[PollResponseItem] = Field(default_factory=list)
|
|
|
|
|
|
class PollInvitationCreateRequest(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
respondent_id: str | None = Field(default=None, max_length=255)
|
|
respondent_label: str | None = Field(default=None, max_length=500)
|
|
email: str | None = Field(default=None, max_length=320)
|
|
expires_at: datetime | None = None
|
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class PollInvitationResponse(BaseModel):
|
|
id: str
|
|
poll_id: str
|
|
respondent_id: str | None = None
|
|
respondent_label: str | None = None
|
|
email: str | None = None
|
|
token: str | None = None
|
|
expires_at: datetime | None = None
|
|
revoked_at: datetime | None = None
|
|
last_used_at: datetime | None = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class PollInvitationListResponse(BaseModel):
|
|
invitations: list[PollInvitationResponse] = Field(default_factory=list)
|