Compare commits
No commits in common. "d9065f9f6513eb31eba19e6fd2cf94a37f01cfc0" and "0ee973051641e3c7c50aaf1bd02a56048b00283d" have entirely different histories.
d9065f9f65
...
0ee9730516
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,7 +5,6 @@ build/
|
|||||||
dist/
|
dist/
|
||||||
wheels/
|
wheels/
|
||||||
*.egg-info
|
*.egg-info
|
||||||
.mypy_cache
|
|
||||||
|
|
||||||
# Virtual environments
|
# Virtual environments
|
||||||
.venv
|
.venv
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
set -o pipefail
|
|
||||||
|
|
||||||
uv run mypy src/
|
|
||||||
uv run ruff check "$@"
|
|
||||||
git diff --exit-code
|
|
||||||
@ -17,31 +17,3 @@ build-backend = "uv_build"
|
|||||||
dev = [
|
dev = [
|
||||||
"ruff>=0.15.1",
|
"ruff>=0.15.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.mypy]
|
|
||||||
disable_error_code = ["import-untyped"]
|
|
||||||
|
|
||||||
[tool.ruff.lint]
|
|
||||||
select = [
|
|
||||||
"E", # standard style checks
|
|
||||||
"F", # standard code checks
|
|
||||||
"FAST", # for fastAPI
|
|
||||||
"ANN", # for annotations
|
|
||||||
"B", # for common bug causes
|
|
||||||
"A", # for builtin variable shadowing
|
|
||||||
"PIE", # for unnecessary code
|
|
||||||
"SIM", # for simplifications
|
|
||||||
"TID", # for tidy improts
|
|
||||||
"TC" , # for type checking
|
|
||||||
#"ARG", # for unused arguments
|
|
||||||
#"TD", # checks for TODOs
|
|
||||||
"N", # naming conventions
|
|
||||||
"W", # whitespace warnings (use --fix)
|
|
||||||
"PLC", # common conventions
|
|
||||||
"PLE", # common errors
|
|
||||||
"PLR", # common refactors
|
|
||||||
"PLW", # common warnings
|
|
||||||
"UP", # for old style
|
|
||||||
"RUF", # other RUF specific rules
|
|
||||||
]
|
|
||||||
extend-ignore = ["TC", "PLR2004"]
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
from pydantic import BaseModel, UUID7
|
|
||||||
|
|
||||||
from .refs import CollectionName
|
|
||||||
|
|
||||||
class FiberBaseModel(BaseModel):
|
|
||||||
uuid: UUID7
|
|
||||||
collection_name: CollectionName
|
|
||||||
@ -15,7 +15,7 @@ class Gender(StrEnum):
|
|||||||
nonbinary = "n"
|
nonbinary = "n"
|
||||||
|
|
||||||
class HonoraryStatus(StrEnum):
|
class HonoraryStatus(StrEnum):
|
||||||
merit = "merit"
|
merit = ("merit",)
|
||||||
honorary = "honorary"
|
honorary = "honorary"
|
||||||
|
|
||||||
class OnlineAlmanacField(StrEnum):
|
class OnlineAlmanacField(StrEnum):
|
||||||
@ -1,12 +1,13 @@
|
|||||||
|
from typing import Literal
|
||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
|
|
||||||
from .non_members import FormerMemberModel
|
from .non_members import FormerMemberModel
|
||||||
from .enums import OnlineAlmanacField
|
from .enums import Membership, OnlineAlmanacField
|
||||||
from .submodels import Address, LoginInfo
|
from .submodels import Address, LoginInfo
|
||||||
|
|
||||||
class MemberModel(FormerMemberModel):
|
class MemberModel(FormerMemberModel):
|
||||||
# requires that Membership equals member
|
# requires that Membership equals member
|
||||||
# membership: Literal[Membership.member] = Membership.member
|
membership: Literal[Membership.member] = Membership.member
|
||||||
|
|
||||||
login: LoginInfo
|
login: LoginInfo
|
||||||
address: Address
|
address: Address
|
||||||
@ -22,4 +23,4 @@ class MemberModel(FormerMemberModel):
|
|||||||
online_almanac_fields: list[OnlineAlmanacField] = Field(default_factory=list)
|
online_almanac_fields: list[OnlineAlmanacField] = Field(default_factory=list)
|
||||||
# IT stuff
|
# IT stuff
|
||||||
shell: str = "/bin/bash" # TODO: support other shells?
|
shell: str = "/bin/bash" # TODO: support other shells?
|
||||||
mail_token: str | None = None
|
mail_token: str | None = None
|
||||||
@ -1,6 +1,8 @@
|
|||||||
|
from typing import Literal
|
||||||
|
from pydantic import Field
|
||||||
|
|
||||||
from .person import PersonModel
|
from .person import PersonModel
|
||||||
from .enums import HonoraryStatus, Gender
|
from .enums import HonoraryStatus, Membership, Gender
|
||||||
|
|
||||||
class FormerMemberModel(PersonModel):
|
class FormerMemberModel(PersonModel):
|
||||||
title: str | None = None
|
title: str | None = None
|
||||||
@ -11,13 +13,12 @@ class FormerMemberModel(PersonModel):
|
|||||||
# company emails?
|
# company emails?
|
||||||
|
|
||||||
class AlumnModel(FormerMemberModel):
|
class AlumnModel(FormerMemberModel):
|
||||||
# membership: Literal[Membership.alumn] = Membership.alumn
|
membership: Literal[Membership.alumn] = Membership.alumn
|
||||||
|
|
||||||
graduated: bool = False
|
graduated: bool = False
|
||||||
receive_spam_mail: bool = True
|
receive_spam_mail: bool = True
|
||||||
|
|
||||||
class ExternalModel(PersonModel):
|
class ExternalModel(PersonModel):
|
||||||
# membership: Literal[Membership.extern] = Membership.extern
|
membership: Literal[Membership.extern] = Membership.extern
|
||||||
pass
|
|
||||||
# TODO... payment???
|
# TODO... payment???
|
||||||
# TODO require login info??
|
# TODO require login info??
|
||||||
@ -1,13 +1,10 @@
|
|||||||
from pydantic import model_validator, EmailStr
|
from pydantic import BaseModel, model_validator, EmailStr
|
||||||
from typing import Self, Literal
|
from typing import Self
|
||||||
|
|
||||||
from fiber_package.base import FiberBaseModel
|
|
||||||
from .enums import Membership, LanguageOption
|
from .enums import Membership, LanguageOption
|
||||||
from .submodels import Address
|
from .submodels import Address
|
||||||
|
|
||||||
class PersonModel(FiberBaseModel):
|
class PersonModel(BaseModel):
|
||||||
collection_name: Literal["entity"] = "entity"
|
|
||||||
|
|
||||||
membership: Membership
|
membership: Membership
|
||||||
first_name: str
|
first_name: str
|
||||||
tussenvoegsels: str | None = None # only in Dutch names
|
tussenvoegsels: str | None = None # only in Dutch names
|
||||||
@ -1,20 +1,17 @@
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# always use @dataclass with slots=True for better performance
|
# always use @dataclass with slots=True for better performance
|
||||||
# unless if you have unions like LoginInfo and Login...
|
|
||||||
|
|
||||||
class LoginInfo(BaseModel):
|
@dataclass(slots=True)
|
||||||
|
class LoginInfo:
|
||||||
username: str
|
username: str
|
||||||
hashed_password: str
|
hashed_password: str
|
||||||
refresh_tokens: list[str] = field(default_factory=list)
|
refresh_tokens: list[str] = field(default_factory=list)
|
||||||
disabled: bool = False
|
disabled: bool = False
|
||||||
|
|
||||||
class LoginInfoResponse(BaseModel):
|
@dataclass(slots=True)
|
||||||
|
class LoginInfoResponse:
|
||||||
username: str
|
username: str
|
||||||
|
|
||||||
@dataclass(slots=True)
|
@dataclass(slots=True)
|
||||||
@ -32,4 +29,4 @@ class PublicKey:
|
|||||||
name: str
|
name: str
|
||||||
key_type: str
|
key_type: str
|
||||||
key: str
|
key: str
|
||||||
expires: date
|
expires: date
|
||||||
6
src/fiber_package/entities/system.py
Normal file
6
src/fiber_package/entities/system.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from .submodels import LoginInfo, LoginInfoResponse
|
||||||
|
|
||||||
|
class LoginModel(BaseModel):
|
||||||
|
login: LoginInfo | LoginInfoResponse
|
||||||
@ -1,7 +0,0 @@
|
|||||||
|
|
||||||
from .submodels import LoginInfo, LoginInfoResponse
|
|
||||||
|
|
||||||
from fiber_package.base import FiberBaseModel
|
|
||||||
|
|
||||||
class LoginModel(FiberBaseModel):
|
|
||||||
login: LoginInfo | LoginInfoResponse
|
|
||||||
12
src/fiber_package/groups/association.py
Normal file
12
src/fiber_package/groups/association.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from fiber_package.util import EntityList, GroupList
|
||||||
|
|
||||||
|
class AssociationModel(BaseModel):
|
||||||
|
full_name: str
|
||||||
|
members: EntityList
|
||||||
|
committees: GroupList
|
||||||
|
guilds: GroupList
|
||||||
|
# events: GroupList
|
||||||
|
# activities: GroupList
|
||||||
|
# mailings: GroupList
|
||||||
@ -1,13 +1,7 @@
|
|||||||
from pydantic import model_validator, UUID7
|
from pydantic import BaseModel, model_validator
|
||||||
|
from fiber_package.util import EntityList, GroupList, GroupRef
|
||||||
|
|
||||||
from fiber_package.refs import (
|
from typing import Self
|
||||||
CommitteeEditionRef,
|
|
||||||
CommitteeEditionRefList,
|
|
||||||
EntityRefList
|
|
||||||
)
|
|
||||||
from fiber_package.base import FiberBaseModel
|
|
||||||
|
|
||||||
from typing import Self, Literal
|
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
from .enums import CommitteeType
|
from .enums import CommitteeType
|
||||||
@ -18,33 +12,31 @@ Every committee has a list of editions.
|
|||||||
An edition is a definite group of members with functions and a timespan.
|
An edition is a definite group of members with functions and a timespan.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class CommitteeModel(FiberBaseModel):
|
class CommitteeModel(BaseModel):
|
||||||
collection_name: Literal["committee"] = "committee"
|
full_name_EN: str
|
||||||
|
full_name_NL: str
|
||||||
full_name_en: str
|
description_EN: str = ""
|
||||||
full_name_nl: str
|
description_NL: str = ""
|
||||||
description_en: str = ""
|
|
||||||
description_nl: str = ""
|
|
||||||
committee_type: CommitteeType
|
committee_type: CommitteeType
|
||||||
|
|
||||||
active: bool = True
|
active: bool = True
|
||||||
|
|
||||||
editions: CommitteeEditionRefList
|
editions: GroupList
|
||||||
current_edition: CommitteeEditionRef | None
|
current_edition: GroupRef | None
|
||||||
|
|
||||||
@model_validator(mode='after')
|
@model_validator(mode='after')
|
||||||
def current_edition_in_editions(self) -> Self:
|
def current_edition_in_editions(self) -> Self:
|
||||||
if self.current_edition is None:
|
uuid = self.current_edition
|
||||||
|
if uuid is None:
|
||||||
return self
|
return self
|
||||||
uuid = self.current_edition.uuid
|
|
||||||
if uuid not in self.editions:
|
if uuid not in self.editions:
|
||||||
raise ValueError("Current edition must be in list of editions")
|
raise ValueError("Current edition must be in list of editions")
|
||||||
return self
|
return self
|
||||||
|
|
||||||
class CommitteeEditionModel(FiberBaseModel):
|
# TODO... maybe require that an active board always has a current_edition that is non-empty?
|
||||||
collection_name: Literal["committee_edition"] = "committee_edition"
|
|
||||||
|
|
||||||
members: EntityRefList
|
class CommitteeEditionModel(BaseModel):
|
||||||
|
members: EntityList
|
||||||
functions: dict[UUID7, str]
|
functions: dict[UUID7, str]
|
||||||
|
|
||||||
start_date: date
|
start_date: date
|
||||||
@ -52,7 +44,7 @@ class CommitteeEditionModel(FiberBaseModel):
|
|||||||
|
|
||||||
@model_validator(mode='after')
|
@model_validator(mode='after')
|
||||||
def functions_to_members_only(self) -> Self:
|
def functions_to_members_only(self) -> Self:
|
||||||
for uuid in self.functions:
|
for uuid in self.functions.keys():
|
||||||
if uuid not in self.members:
|
if uuid not in self.members:
|
||||||
raise ValueError("Cannot only assign functions to committee members")
|
raise ValueError("Cannot only assign functions to members")
|
||||||
return self
|
return self
|
||||||
@ -1,11 +1,9 @@
|
|||||||
from pydantic import model_validator
|
from pydantic import BaseModel, model_validator
|
||||||
from fiber_package.refs import EntityRefList, EntityRef
|
from fiber_package.util import EntityList
|
||||||
|
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
from typing import Self, Literal
|
from typing import Self
|
||||||
|
|
||||||
from fiber_package.base import FiberBaseModel
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@ -15,18 +13,16 @@ They have a list of members.
|
|||||||
Removing old members from last_active is done in fiber
|
Removing old members from last_active is done in fiber
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class GuildModel(FiberBaseModel):
|
class GuildModel(BaseModel):
|
||||||
collection_name: Literal["guild"] = "guild"
|
full_name_EN: str
|
||||||
|
full_name_NL: str
|
||||||
|
description_EN: str
|
||||||
|
description_NL: str
|
||||||
|
|
||||||
full_name_en: str
|
members: EntityList
|
||||||
full_name_nl: str
|
previous_members: EntityList
|
||||||
description_en: str
|
|
||||||
description_nl: str
|
|
||||||
|
|
||||||
members: EntityRefList
|
guild_masters: EntityList
|
||||||
previous_members: EntityRefList
|
|
||||||
|
|
||||||
guild_masters: EntityRefList
|
|
||||||
|
|
||||||
last_active: dict[EntityRef, date]
|
last_active: dict[EntityRef, date]
|
||||||
|
|
||||||
@ -34,9 +30,7 @@ class GuildModel(FiberBaseModel):
|
|||||||
def current_members_not_previous(self) -> Self:
|
def current_members_not_previous(self) -> Self:
|
||||||
for member in self.members:
|
for member in self.members:
|
||||||
if member in self.previous_members:
|
if member in self.previous_members:
|
||||||
raise ValueError(
|
raise ValueError("There can be no overlap between members and previous members!")
|
||||||
"There can be no overlap between members and previous members!"
|
|
||||||
)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@model_validator(mode='after')
|
@model_validator(mode='after')
|
||||||
@ -45,3 +39,4 @@ class GuildModel(FiberBaseModel):
|
|||||||
if master not in self.members:
|
if master not in self.members:
|
||||||
raise ValueError("All guild masters must be members!")
|
raise ValueError("All guild masters must be members!")
|
||||||
return self
|
return self
|
||||||
|
|
||||||
6
src/fiber_package/groups/root.py
Normal file
6
src/fiber_package/groups/root.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
from fiber_package.util import GroupList, EntityList
|
||||||
|
|
||||||
|
class RootGroupModel(BaseModel):
|
||||||
|
associations: GroupList
|
||||||
|
sys_admins: EntityList
|
||||||
@ -1,42 +0,0 @@
|
|||||||
from dataclasses import dataclass
|
|
||||||
from pydantic import UUID7
|
|
||||||
from typing import Literal, TypeVar
|
|
||||||
from collections.abc import Iterator
|
|
||||||
|
|
||||||
'''
|
|
||||||
CollectionName provides the list of all allowed collections!
|
|
||||||
'''
|
|
||||||
CollectionName = Literal["system", "entity", "committee", "committee_edition", "guild"]
|
|
||||||
|
|
||||||
C = TypeVar("C", bound=str)
|
|
||||||
|
|
||||||
@dataclass(slots=True)
|
|
||||||
class Ref[C: str]:
|
|
||||||
uuid: UUID7
|
|
||||||
collection_name: C
|
|
||||||
|
|
||||||
@dataclass(slots=True)
|
|
||||||
class RefList[C: str]:
|
|
||||||
uuids: list[UUID7]
|
|
||||||
collection_name: C
|
|
||||||
|
|
||||||
def __iter__(self) -> Iterator[UUID7]:
|
|
||||||
return iter(self.uuids)
|
|
||||||
|
|
||||||
def __contains__(self, item: UUID7) -> bool:
|
|
||||||
return item in self.uuids
|
|
||||||
|
|
||||||
SystemRef = Ref[Literal["system"]]
|
|
||||||
SystemRefList = RefList[Literal["system"]]
|
|
||||||
|
|
||||||
EntityRef = Ref[Literal["entity"]]
|
|
||||||
EntityRefList = RefList[Literal["entity"]]
|
|
||||||
|
|
||||||
CommitteeRef = Ref[Literal["committee"]]
|
|
||||||
CommitteeRefList = RefList[Literal["committee"]]
|
|
||||||
|
|
||||||
CommitteeEditionRef = Ref[Literal["committee_edition"]]
|
|
||||||
CommitteeEditionRefList = RefList[Literal["committee_edition"]]
|
|
||||||
|
|
||||||
GuildRef = Ref[Literal["guild"]]
|
|
||||||
GuildRefList = RefList[Literal["guild"]]
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
from fiber_package.base import FiberBaseModel
|
|
||||||
from fiber_package.refs import EntityRefList, CommitteeRefList, GuildRefList
|
|
||||||
|
|
||||||
|
|
||||||
class AssociationModel(FiberBaseModel):
|
|
||||||
full_name: str
|
|
||||||
members: EntityRefList
|
|
||||||
committees: CommitteeRefList
|
|
||||||
guilds: GuildRefList
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: also add former_members, alumns, etc... ?
|
|
||||||
# events: GroupList
|
|
||||||
# activities: GroupList
|
|
||||||
# mailings: GroupList
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
from fiber_package.base import FiberBaseModel
|
|
||||||
from fiber_package.refs import SystemRefList, EntityRefList
|
|
||||||
|
|
||||||
class RootGroupModel(FiberBaseModel):
|
|
||||||
associations: SystemRefList
|
|
||||||
sys_admins: EntityRefList
|
|
||||||
9
src/fiber_package/util.py
Normal file
9
src/fiber_package/util.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from typing import Annotated
|
||||||
|
from pydantic import RootModel, Field, UUID7
|
||||||
|
|
||||||
|
|
||||||
|
EntityList = Annotated[list[UUID7], Field(default_factory=list)]
|
||||||
|
GroupList = Annotated[list[UUID7], Field(default_factory=list)]
|
||||||
|
|
||||||
|
EntityRef = UUID7
|
||||||
|
GroupRef = UUID7
|
||||||
Loading…
x
Reference in New Issue
Block a user