From 649f10be3f1701c5f320b648ebcfa8b7d49361bd Mon Sep 17 00:00:00 2001 From: Tiago Scholten Date: Thu, 9 Apr 2026 22:55:01 +0200 Subject: [PATCH 1/9] added basic committees --- src/fiber_package/entities/member.py | 1 + .../groups/committees/committee.py | 39 +++++++++++++++++++ src/fiber_package/groups/committees/enums.py | 9 +++++ 3 files changed, 49 insertions(+) create mode 100644 src/fiber_package/groups/committees/committee.py create mode 100644 src/fiber_package/groups/committees/enums.py diff --git a/src/fiber_package/entities/member.py b/src/fiber_package/entities/member.py index d30dba3..1f32690 100644 --- a/src/fiber_package/entities/member.py +++ b/src/fiber_package/entities/member.py @@ -6,6 +6,7 @@ from .enums import Membership, OnlineAlmanacField from .submodels import Address, LoginInfo class MemberModel(FormerMemberModel): + # requires that Membership equals member membership: Annotated[Membership, Field(pattern="member")] login: LoginInfo diff --git a/src/fiber_package/groups/committees/committee.py b/src/fiber_package/groups/committees/committee.py new file mode 100644 index 0000000..d81b183 --- /dev/null +++ b/src/fiber_package/groups/committees/committee.py @@ -0,0 +1,39 @@ +from pydantic import BaseModel +from fiber_package.util import EntityList, GroupList, GroupRef + +from .enums import CommitteeType + +''' +A committee represents any group of members +''' + +class CommitteeModel(BaseModel): + full_name_EN: str + full_name_NL: str + description_EN: str + description_NL: str + + active: bool = True + + editions: GroupList("committee_edition", []) + current_edition: GroupRef("committee_edition", None) + + @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 = EntityList("member", []) + functions: dict[UUID4, str] + + @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..fe78120 --- /dev/null +++ b/src/fiber_package/groups/committees/enums.py @@ -0,0 +1,9 @@ +from enum import StrEnum + +class CommitteeType(StrEnum): + board_committee: "Board committee" + GMM_committee: "GMM committee" + guild: "Guild" + subassociation_board: "Subassociation board" + + From 56b5415bc9bfc98b8124a65a113d6232a50020fe Mon Sep 17 00:00:00 2001 From: Tiago Scholten Date: Sun, 7 Jun 2026 15:59:17 +0000 Subject: [PATCH 2/9] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 From 1e1676952d35a16614b59ab57f106bc6c64d963c Mon Sep 17 00:00:00 2001 From: Tiago Scholten Date: Sun, 7 Jun 2026 21:36:06 +0200 Subject: [PATCH 3/9] added committees and guilds --- src/fiber_package/entities/member.py | 4 +- src/fiber_package/entities/non_members.py | 6 +-- src/fiber_package/groups/association.py | 12 +++--- .../groups/committees/committee.py | 18 +++++--- src/fiber_package/groups/committees/enums.py | 5 +-- src/fiber_package/groups/committees/guild.py | 42 +++++++++++++++++++ src/fiber_package/groups/root.py | 4 +- src/fiber_package/util.py | 22 +++++++--- 8 files changed, 86 insertions(+), 27 deletions(-) create mode 100644 src/fiber_package/groups/committees/guild.py diff --git a/src/fiber_package/entities/member.py b/src/fiber_package/entities/member.py index 1f32690..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 @@ -7,7 +7,7 @@ from .submodels import Address, LoginInfo class MemberModel(FormerMemberModel): # requires that Membership equals member - membership: Annotated[Membership, Field(pattern="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 index d81b183..a236cb4 100644 --- a/src/fiber_package/groups/committees/committee.py +++ b/src/fiber_package/groups/committees/committee.py @@ -1,10 +1,15 @@ -from pydantic import BaseModel +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 ''' -A committee represents any group of members +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): @@ -15,8 +20,8 @@ class CommitteeModel(BaseModel): active: bool = True - editions: GroupList("committee_edition", []) - current_edition: GroupRef("committee_edition", None) + editions: GroupList + current_edition: GroupRef @model_validator(mode='after') def current_edition_in_editions(self) -> Self: @@ -28,9 +33,12 @@ class CommitteeModel(BaseModel): return self class CommitteeEditionModel(BaseModel): - members: EntityList = EntityList("member", []) + 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(): diff --git a/src/fiber_package/groups/committees/enums.py b/src/fiber_package/groups/committees/enums.py index fe78120..182ed6c 100644 --- a/src/fiber_package/groups/committees/enums.py +++ b/src/fiber_package/groups/committees/enums.py @@ -2,8 +2,7 @@ from enum import StrEnum class CommitteeType(StrEnum): board_committee: "Board committee" - GMM_committee: "GMM committee" - guild: "Guild" - subassociation_board: "Subassociation board" + 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 From 5d40e4f358be907d830b75cf95b89e456a4b11c2 Mon Sep 17 00:00:00 2001 From: Tiago Scholten Date: Sun, 7 Jun 2026 21:38:48 +0200 Subject: [PATCH 4/9] updated uv-build verison --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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] From 456bdbf63af33fa8b2db0f26f34ca5c1fd865ba2 Mon Sep 17 00:00:00 2001 From: Tiago Scholten Date: Sun, 7 Jun 2026 22:24:38 +0200 Subject: [PATCH 5/9] Simplified Group/Entity Lists/Ref to simple type aliases --- .../groups/committees/committee.py | 5 ++- src/fiber_package/util.py | 36 ++++--------------- 2 files changed, 10 insertions(+), 31 deletions(-) diff --git a/src/fiber_package/groups/committees/committee.py b/src/fiber_package/groups/committees/committee.py index a236cb4..7d9786f 100644 --- a/src/fiber_package/groups/committees/committee.py +++ b/src/fiber_package/groups/committees/committee.py @@ -17,11 +17,12 @@ class CommitteeModel(BaseModel): full_name_NL: str description_EN: str description_NL: str + committee_type: CommitteeType active: bool = True editions: GroupList - current_edition: GroupRef + current_edition: GroupRef | None @model_validator(mode='after') def current_edition_in_editions(self) -> Self: @@ -32,6 +33,8 @@ class CommitteeModel(BaseModel): 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] diff --git a/src/fiber_package/util.py b/src/fiber_package/util.py index a851a38..2c6c325 100644 --- a/src/fiber_package/util.py +++ b/src/fiber_package/util.py @@ -1,33 +1,9 @@ -from dataclasses import dataclass, field +from typing import Annotated +from pydantic import RootModel, Field, UUID4 -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. +EntityList = Annotated[list[UUID4], Field(default_factory=list)] +GroupList = Annotated[list[UUID4], Field(default_factory=list)] -the __iter__ function allows us to do things as - "for member in edition.members" -''' - -@dataclass(slots=True) -class EntityList: - uuids: list[UUID4] = field(default_factory=list) - - def __iter__(self): - return iter(self.uuids) - -@dataclass(slots=True) -class GroupList: - uuids: list[UUID4] = field(default_factory=list) - - def __iter__(self): - return iter(self.uuids) - -@dataclass(slots=True) -class EntityRef: - uuids: UUID4 - -@dataclass(slots=True) -class GroupRef: - uuids: UUID4 \ No newline at end of file +EntityRef = UUID4 +GroupRef = UUID4 \ No newline at end of file From 926d5a5afbefe29bcc2a3bbace019d955d8438ef Mon Sep 17 00:00:00 2001 From: Tiago Scholten Date: Sun, 7 Jun 2026 22:50:33 +0200 Subject: [PATCH 6/9] forgot to import GroupList --- src/fiber_package/groups/association.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fiber_package/groups/association.py b/src/fiber_package/groups/association.py index 97c9753..d5fcea7 100644 --- a/src/fiber_package/groups/association.py +++ b/src/fiber_package/groups/association.py @@ -1,6 +1,6 @@ from pydantic import BaseModel -from fiber_package.util import EntityList +from fiber_package.util import EntityList, GroupList class AssociationModel(BaseModel): full_name: str From 12bb23278509d05e627a0b6c4158a64c8de8af81 Mon Sep 17 00:00:00 2001 From: Tiago Scholten Date: Sun, 7 Jun 2026 23:02:21 +0200 Subject: [PATCH 7/9] added defaults --- src/fiber_package/groups/committees/committee.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fiber_package/groups/committees/committee.py b/src/fiber_package/groups/committees/committee.py index 7d9786f..317e668 100644 --- a/src/fiber_package/groups/committees/committee.py +++ b/src/fiber_package/groups/committees/committee.py @@ -15,8 +15,8 @@ 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 + description_EN: str = "" + description_NL: str = "" committee_type: CommitteeType active: bool = True From f5e165aa11e544e79984df2a6e983d0f34d93ea6 Mon Sep 17 00:00:00 2001 From: Tiago Scholten Date: Sun, 7 Jun 2026 23:09:40 +0200 Subject: [PATCH 8/9] huh I didnt know how StrEnums worked --- src/fiber_package/groups/committees/enums.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fiber_package/groups/committees/enums.py b/src/fiber_package/groups/committees/enums.py index 182ed6c..0c1234d 100644 --- a/src/fiber_package/groups/committees/enums.py +++ b/src/fiber_package/groups/committees/enums.py @@ -1,8 +1,8 @@ from enum import StrEnum class CommitteeType(StrEnum): - board_committee: "Board committee" - gmm_committee: "GMM committee" - board: "board" + board_committee = "Board committee" + gmm_committee = "GMM committee" + board = "board" From 57fc551e64a2ea9f060961b1ab9cc835d6adc15f Mon Sep 17 00:00:00 2001 From: Tiago Scholten Date: Sun, 7 Jun 2026 23:11:38 +0200 Subject: [PATCH 9/9] fixed remnants of old system --- src/fiber_package/groups/committees/committee.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fiber_package/groups/committees/committee.py b/src/fiber_package/groups/committees/committee.py index 317e668..ff071f8 100644 --- a/src/fiber_package/groups/committees/committee.py +++ b/src/fiber_package/groups/committees/committee.py @@ -26,10 +26,10 @@ class CommitteeModel(BaseModel): @model_validator(mode='after') def current_edition_in_editions(self) -> Self: - uuid = self.current_edition.uuid + uuid = self.current_edition if uuid is None: return self - if uuid not in self.editions.uuids: + if uuid not in self.editions: raise ValueError("Current edition must be in list of editions") return self @@ -45,6 +45,6 @@ class CommitteeEditionModel(BaseModel): @model_validator(mode='after') def functions_to_members_only(self) -> Self: for uuid in self.functions.keys(): - if uuid not in self.members.uuids: + if uuid not in self.members: raise ValueError("Cannot only assign functions to members") return self \ No newline at end of file