from datetime import datetime from typing import ClassVar from ninja import ModelSchema, Schema from pydantic import Field from apps.reviews.models import ApproverGroup, ReviewSettings from apps.users.models import User class ApproverOut(ModelSchema): class Meta: model = User fields: ClassVar[tuple[str, ...]] = ( User.id.field.name, User.username.field.name, User.email.field.name, User.first_name.field.name, User.last_name.field.name, ) class ExperimenterOut(ModelSchema): class Meta: model = User fields: ClassVar[tuple[str, ...]] = ( User.id.field.name, User.username.field.name, User.email.field.name, ) class ApproverGroupOut(ModelSchema): experimenter: ExperimenterOut approvers: list[ApproverOut] min_approvals: int created_at: datetime updated_at: datetime class Meta: model = ApproverGroup fields: ClassVar[tuple[str, ...]] = ( ApproverGroup.id.field.name, ApproverGroup.experimenter.field.name, ApproverGroup.approvers.field.name, ApproverGroup.min_approvals.field.name, ApproverGroup.created_at.field.name, ApproverGroup.updated_at.field.name, ) class ApproverGroupCreateIn(Schema): experimenter_id: str = Field( ..., description="UUID of the experimenter user this group belongs to.", ) approver_ids: list[str] = Field( default_factory=list, description=( "List of user UUIDs to add as approvers. " "Each user must have the 'approver' role." ), ) min_approvals: int = Field( 1, ge=1, description=( "Number of distinct approvals required. " "Must be >= 1 and <= number of approvers (if any are provided)." ), ) class ApproverGroupUpdateIn(Schema): approver_ids: list[str] | None = Field( None, description=( "If provided, replaces the current set of approvers. " "Each user must have the 'approver' role." ), ) min_approvals: int | None = Field( None, ge=1, description="New minimum approval threshold.", ) class ApproverGroupListOut(Schema): count: int items: list[ApproverGroupOut] class ApproverGroupAddApproverIn(Schema): approver_id: str = Field( ..., description="UUID of the user to add as an approver.", ) class ApproverGroupRemoveApproverIn(Schema): approver_id: str = Field( ..., description="UUID of the approver to remove.", ) class ReviewSettingsOut(ModelSchema): default_min_approvals: int allow_any_approver: bool updated_at: datetime class Meta: model = ReviewSettings fields: ClassVar[tuple[str, ...]] = ( ReviewSettings.id.field.name, ReviewSettings.default_min_approvals.field.name, ReviewSettings.allow_any_approver.field.name, ReviewSettings.updated_at.field.name, ) class ReviewSettingsUpdateIn(Schema): default_min_approvals: int | None = Field( None, ge=1, description="New default minimum approval threshold.", ) allow_any_approver: bool | None = Field( None, description="New fallback policy for approver eligibility.", ) class EffectiveReviewPolicyOut(Schema): experimenter_id: str = Field(..., description="UUID of the experimenter.") min_approvals: int = Field( ..., description="Effective number of approvals required." ) approvers: list[ApproverOut] = Field( ..., description="List of users who are eligible to approve.", ) source: str = Field( ..., description=( "Where the policy comes from: " "'approver_group' or 'global_fallback'." ), ) has_explicit_group: bool = Field( ..., description="Whether the experimenter has an explicit approver group.", )