diff --git a/README.md b/README.md index c5a7682..b741908 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,2 @@ -Use ```bash -uv build -``` +Use ``uv build`` to build package. Then git commit & push. \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 98983b5..223c779 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ dependencies = [ ] [build-system] -requires = ["uv_build>=0.10.2,<0.11.0"] +requires = ["uv_build>=0.10.2,<0.12"] build-backend = "uv_build" [dependency-groups] diff --git a/src/fiber_package/entities/member.py b/src/fiber_package/entities/member.py index d30dba3..3499199 100644 --- a/src/fiber_package/entities/member.py +++ b/src/fiber_package/entities/member.py @@ -1,4 +1,4 @@ -from typing import Annotated +from typing import Literal from pydantic import Field from .non_members import FormerMemberModel @@ -6,7 +6,8 @@ from .enums import Membership, OnlineAlmanacField from .submodels import Address, LoginInfo class MemberModel(FormerMemberModel): - membership: Annotated[Membership, Field(pattern="member")] + # requires that Membership equals member + membership: Literal[Membership.member] = Membership.member login: LoginInfo address: Address diff --git a/src/fiber_package/entities/non_members.py b/src/fiber_package/entities/non_members.py index afc29bb..7eb3856 100644 --- a/src/fiber_package/entities/non_members.py +++ b/src/fiber_package/entities/non_members.py @@ -1,4 +1,4 @@ -from typing import Annotated +from typing import Literal from pydantic import Field from .person import PersonModel @@ -13,12 +13,12 @@ class FormerMemberModel(PersonModel): # company emails? class AlumnModel(FormerMemberModel): - membership: Annotated[Membership, Field(pattern="alumn")] + membership: Literal[Membership.alumn] = Membership.alumn graduated: bool = False receive_spam_mail: bool = True class ExternalModel(PersonModel): - membership: Annotated[Membership, Field(pattern="extern")] + membership: Literal[Membership.extern] = Membership.extern # TODO... payment??? # TODO require login info?? diff --git a/src/fiber_package/groups/association.py b/src/fiber_package/groups/association.py index 6a1011a..d5fcea7 100644 --- a/src/fiber_package/groups/association.py +++ b/src/fiber_package/groups/association.py @@ -1,12 +1,12 @@ from pydantic import BaseModel -from fiber_package.util import EntityList +from fiber_package.util import EntityList, GroupList class AssociationModel(BaseModel): full_name: str - members: EntityList = EntityList("member", []) - # committees: GroupList("commmittee", 0) # type: ignore - # events: GroupList("event", 0) # type: ignore - # activities: GroupList("activity", 0) # type: ignore - # mailings: GroupList("mailing", 0) # type: ignore - # mailalias: GroupList("mailalias", 0) # type: ignore \ No newline at end of file + members: EntityList + committees: GroupList + guilds: GroupList + # events: GroupList + # activities: GroupList + # mailings: GroupList \ No newline at end of file diff --git a/src/fiber_package/groups/committees/committee.py b/src/fiber_package/groups/committees/committee.py new file mode 100644 index 0000000..ff071f8 --- /dev/null +++ b/src/fiber_package/groups/committees/committee.py @@ -0,0 +1,50 @@ +from pydantic import BaseModel, model_validator +from fiber_package.util import EntityList, GroupList, GroupRef + +from typing import Self +from datetime import date + +from .enums import CommitteeType + +''' +Committees are groups of members for a definite period. +Every committee has a list of editions. +An edition is a definite group of members with functions and a timespan. +''' + +class CommitteeModel(BaseModel): + full_name_EN: str + full_name_NL: str + description_EN: str = "" + description_NL: str = "" + committee_type: CommitteeType + + active: bool = True + + editions: GroupList + current_edition: GroupRef | None + + @model_validator(mode='after') + def current_edition_in_editions(self) -> Self: + uuid = self.current_edition + if uuid is None: + return self + if uuid not in self.editions: + raise ValueError("Current edition must be in list of editions") + return self + + # TODO... maybe require that an active board always has a current_edition that is non-empty? + +class CommitteeEditionModel(BaseModel): + members: EntityList + functions: dict[UUID4, str] + + start_date: date + end_date: date | None # None means ongoing + + @model_validator(mode='after') + def functions_to_members_only(self) -> Self: + for uuid in self.functions.keys(): + if uuid not in self.members: + raise ValueError("Cannot only assign functions to members") + return self \ No newline at end of file diff --git a/src/fiber_package/groups/committees/enums.py b/src/fiber_package/groups/committees/enums.py new file mode 100644 index 0000000..0c1234d --- /dev/null +++ b/src/fiber_package/groups/committees/enums.py @@ -0,0 +1,8 @@ +from enum import StrEnum + +class CommitteeType(StrEnum): + board_committee = "Board committee" + gmm_committee = "GMM committee" + board = "board" + + diff --git a/src/fiber_package/groups/committees/guild.py b/src/fiber_package/groups/committees/guild.py new file mode 100644 index 0000000..e21e107 --- /dev/null +++ b/src/fiber_package/groups/committees/guild.py @@ -0,0 +1,42 @@ +from pydantic import BaseModel, model_validator +from fiber_package.util import EntityList + +from datetime import date + +from typing import Self + + +''' +Guilds are groups of members for an indefinite period. +They have a list of members. + +Removing old members from last_active is done in fiber +''' + +class GuildModel(BaseModel): + full_name_EN: str + full_name_NL: str + description_EN: str + description_NL: str + + members: EntityList + previous_members: EntityList + + guild_masters: EntityList + + last_active: dict[EntityRef, date] + + @model_validator(mode='after') + def current_members_not_previous(self) -> Self: + for member in self.members: + if member in self.previous_members: + raise ValueError("There can be no overlap between members and previous members!") + return self + + @model_validator(mode='after') + def guild_masters_are_members(self) -> Self: + for master in self.guild_masters: + if master not in self.members: + raise ValueError("All guild masters must be members!") + return self + \ No newline at end of file diff --git a/src/fiber_package/groups/root.py b/src/fiber_package/groups/root.py index 594962e..2d3bfbd 100644 --- a/src/fiber_package/groups/root.py +++ b/src/fiber_package/groups/root.py @@ -2,5 +2,5 @@ from pydantic import BaseModel from fiber_package.util import GroupList, EntityList class RootGroupModel(BaseModel): - associations: GroupList = GroupList("association", []) - sys_admins: EntityList = EntityList("login", []) \ No newline at end of file + associations: GroupList + sys_admins: EntityList \ No newline at end of file diff --git a/src/fiber_package/util.py b/src/fiber_package/util.py index 6077523..76dbc08 100644 --- a/src/fiber_package/util.py +++ b/src/fiber_package/util.py @@ -1,23 +1,9 @@ -from dataclasses import dataclass, field +from typing import Annotated +from pydantic import RootModel, Field, UUID4 -from pydantic import UUID4 -@dataclass(slots=True) -class EntityList: - template: str - uuids: list[UUID4] = field(default_factory=list) +EntityList = Annotated[list[UUID4], Field(default_factory=list)] +GroupList = Annotated[list[UUID4], Field(default_factory=list)] -@dataclass(slots=True) -class GroupList: - template: str - uuids: list[UUID4] = field(default_factory=list) - -@dataclass(slots=True) -class EntityRef: - template: str - uuid: UUID4 | None = None - -@dataclass(slots=True) -class GroupRef: - template: str - uuid: UUID4 | None = None +EntityRef = UUID4 +GroupRef = UUID4