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..97c9753 100644 --- a/src/fiber_package/groups/association.py +++ b/src/fiber_package/groups/association.py @@ -4,9 +4,9 @@ from fiber_package.util import EntityList 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..a236cb4 --- /dev/null +++ b/src/fiber_package/groups/committees/committee.py @@ -0,0 +1,47 @@ +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 + + active: bool = True + + editions: GroupList + current_edition: GroupRef + + @model_validator(mode='after') + def current_edition_in_editions(self) -> Self: + uuid = self.current_edition.uuid + if uuid is None: + return self + if uuid not in self.editions.uuids: + raise ValueError("Current edition must be in list of editions") + return self + +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.uuids: + 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..182ed6c --- /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 8973966..a851a38 100644 --- a/src/fiber_package/util.py +++ b/src/fiber_package/util.py @@ -2,22 +2,32 @@ from dataclasses import dataclass, field from pydantic import UUID4 +''' +(Entity/Group)(List/Ref) represents a list / single instance of entities / groups. +This is for fiber to know in which Mongo collection to look. + +the __iter__ function allows us to do things as + "for member in edition.members" +''' + @dataclass(slots=True) class EntityList: - template: str uuids: list[UUID4] = field(default_factory=list) + def __iter__(self): + return iter(self.uuids) + @dataclass(slots=True) class GroupList: - template: str uuids: list[UUID4] = field(default_factory=list) + def __iter__(self): + return iter(self.uuids) + @dataclass(slots=True) class EntityRef: - template: str - uuids: UUID4 | None = None + uuids: UUID4 @dataclass(slots=True) class GroupRef: - template: str - uuids: UUID4 | None = None \ No newline at end of file + uuids: UUID4 \ No newline at end of file