from datetime import datetime, timezone
from pydantic import AwareDatetime, Field, field_validator
from ska_oso_pdm._shared import Metadata, PanelID, PdmObject, ProposalID
from ska_oso_pdm._shared.atoms import TerseStrEnum
from ska_oso_pdm._shared.ids import ReviewerID
[docs]
class ReviewerStatus(TerseStrEnum):
ACCEPTED = "Accepted"
DECLINED = "Declined"
PENDING = "Pending"
[docs]
class ProposalAssignment(PdmObject):
prsl_id: ProposalID
assigned_on: AwareDatetime = Field(
default_factory=lambda: datetime.now(timezone.utc)
)
[docs]
class ReviewerAssignment(PdmObject):
reviewer_id: ReviewerID
assigned_on: AwareDatetime = Field(
default_factory=lambda: datetime.now(timezone.utc)
)
status: ReviewerStatus = ReviewerStatus.PENDING
[docs]
class Panel(PdmObject):
metadata: Metadata = Field(
default_factory=Metadata, description="The metadata of this Panel."
)
panel_id: PanelID
name: str
cycle: str | None = None
expiry_date: AwareDatetime | None = None
proposals: list[ProposalAssignment] = Field(default_factory=list)
sci_reviewers: list[ReviewerAssignment] = Field(default_factory=list)
tech_reviewers: list[ReviewerAssignment] = Field(default_factory=list)
[docs]
@field_validator("proposals", mode="after")
@classmethod
def ensure_unique_proposals(
cls, proposals: list[ProposalAssignment]
) -> list[ProposalAssignment]:
"""
Ensure that `proposals` list contains only unique prsl_ids,
and preserves the first occurrence.
"""
seen: set[str] = set()
unique: list[ProposalAssignment] = []
for assignment in proposals:
if assignment.prsl_id not in seen:
unique.append(assignment)
seen.add(assignment.prsl_id)
return unique
[docs]
def add_proposal(self, prsl_id: ProposalID) -> None:
"""Appends a proposal only if not already present."""
if all(p.prsl_id != prsl_id for p in self.proposals):
self.proposals.append(ProposalAssignment(prsl_id=prsl_id))