Skip to content

Commit

Permalink
move sort_groups() to group_utils.py
Browse files Browse the repository at this point in the history
  • Loading branch information
jnussbaum committed Oct 22, 2024
1 parent 83a2062 commit 6dc2ccc
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 78 deletions.
129 changes: 53 additions & 76 deletions dsp_permissions_scripts/models/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import re
from typing import Any
from typing import Iterable
from typing import Self
from typing import TypeAlias
from typing import Union
Expand All @@ -23,6 +22,59 @@
PREFIXED_IRI_REGEX = r"^[\w-]+:[\w -]+$"


class BuiltinGroup(BaseModel):
model_config = ConfigDict(frozen=True)

prefixed_iri: str

@model_validator(mode="after")
def _check_regex(self) -> Self:
valid_group_names = ["SystemAdmin", "Creator", "ProjectAdmin", "ProjectMember", "KnownUser", "UnknownUser"]
prefix, name = self.prefixed_iri.split(":")
if prefix != "knora-admin" or name not in valid_group_names:
raise InvalidGroupError(f"{self.prefixed_iri} is not a valid group IRI")
return self


class CustomGroup(BaseModel):
model_config = ConfigDict(frozen=True)

prefixed_iri: str

@model_validator(mode="after")
def _check_regex(self) -> Self:
if not is_prefixed_group_iri(self.prefixed_iri):
raise InvalidGroupError(f"{self.prefixed_iri} is not a valid group IRI")
if self.prefixed_iri.startswith(("knora-admin:", "knora-base:", "knora-api:")):
raise InvalidGroupError(f"{self.prefixed_iri} is not a custom group")
return self


def group_builder(prefixed_iri: str) -> BuiltinGroup | CustomGroup:
if prefixed_iri.startswith("knora-admin:"):
return BuiltinGroup(prefixed_iri=prefixed_iri)
elif re.search(PREFIXED_IRI_REGEX, prefixed_iri):
return CustomGroup(prefixed_iri=prefixed_iri)
else:
raise InvalidGroupError(f"{prefixed_iri} is not a valid group IRI")


def group_discriminator(v: Any) -> str:
if isinstance(v, dict):
return "builtin" if v["prefixed_iri"].startswith("knora-admin:") else "custom"
else:
return "builtin" if getattr(v, "prefixed_iri").startswith("knora-admin:") else "custom"


GroupType: TypeAlias = Annotated[
Union[
Annotated[BuiltinGroup, Tag("builtin")],
Annotated[CustomGroup, Tag("custom")],
],
Discriminator(group_discriminator),
]


def is_prefixed_group_iri(iri: str) -> bool:
if iri.startswith((KNORA_ADMIN_ONTO_NAMESPACE, "http://rdfh.ch/groups/", "knora-base:", "knora-api:")):
return False
Expand Down Expand Up @@ -77,81 +129,6 @@ def _get_full_iri_from_custom_group(prefix: str, groupname: str, dsp_client: Dsp
return full_iri


def group_builder(prefixed_iri: str) -> BuiltinGroup | CustomGroup:
if prefixed_iri.startswith("knora-admin:"):
return BuiltinGroup(prefixed_iri=prefixed_iri)
elif re.search(PREFIXED_IRI_REGEX, prefixed_iri):
return CustomGroup(prefixed_iri=prefixed_iri)
else:
raise InvalidGroupError(f"{prefixed_iri} is not a valid group IRI")


class BuiltinGroup(BaseModel):
model_config = ConfigDict(frozen=True)

prefixed_iri: str

@model_validator(mode="after")
def _check_regex(self) -> Self:
valid_group_names = ["SystemAdmin", "Creator", "ProjectAdmin", "ProjectMember", "KnownUser", "UnknownUser"]
prefix, name = self.prefixed_iri.split(":")
if prefix != "knora-admin" or name not in valid_group_names:
raise InvalidGroupError(f"{self.prefixed_iri} is not a valid group IRI")
return self


class CustomGroup(BaseModel):
model_config = ConfigDict(frozen=True)

prefixed_iri: str

@model_validator(mode="after")
def _check_regex(self) -> Self:
if not is_prefixed_group_iri(self.prefixed_iri):
raise InvalidGroupError(f"{self.prefixed_iri} is not a valid group IRI")
if self.prefixed_iri.startswith(("knora-admin:", "knora-base:", "knora-api:")):
raise InvalidGroupError(f"{self.prefixed_iri} is not a custom group")
return self


def group_discriminator(v: Any) -> str:
if isinstance(v, dict):
return "builtin" if v["prefixed_iri"].startswith("knora-admin:") else "custom"
else:
return "builtin" if getattr(v, "prefixed_iri").startswith("knora-admin:") else "custom"


GroupType: TypeAlias = Annotated[
Union[
Annotated[BuiltinGroup, Tag("builtin")],
Annotated[CustomGroup, Tag("custom")],
],
Discriminator(group_discriminator),
]


def _get_sort_pos_of_custom_group(prefixed_iri: str) -> int:
alphabet = list("abcdefghijklmnopqrstuvwxyz")
relevant_letter = prefixed_iri.split(":")[-1][0]
return alphabet.index(relevant_letter.lower()) + 99 # must be higher than the highest index of the builtin groups


def sort_groups(groups_original: Iterable[GroupType]) -> list[GroupType]:
"""
Sorts groups:
- First according to their power (most powerful first - only applicable for built-in groups)
- Then alphabetically (custom groups)
"""
sort_key = [SYSTEM_ADMIN, CREATOR, PROJECT_ADMIN, PROJECT_MEMBER, KNOWN_USER, UNKNOWN_USER]
groups = list(groups_original)
groups.sort(
key=lambda x: sort_key.index(x)
if isinstance(x, BuiltinGroup)
else _get_sort_pos_of_custom_group(x.prefixed_iri)
)
return groups


UNKNOWN_USER = BuiltinGroup(prefixed_iri="knora-admin:UnknownUser")
KNOWN_USER = BuiltinGroup(prefixed_iri="knora-admin:KnownUser")
PROJECT_MEMBER = BuiltinGroup(prefixed_iri="knora-admin:ProjectMember")
Expand Down
32 changes: 32 additions & 0 deletions dsp_permissions_scripts/models/group_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from typing import Iterable

from dsp_permissions_scripts.models.group import CREATOR
from dsp_permissions_scripts.models.group import KNOWN_USER
from dsp_permissions_scripts.models.group import PROJECT_ADMIN
from dsp_permissions_scripts.models.group import PROJECT_MEMBER
from dsp_permissions_scripts.models.group import SYSTEM_ADMIN
from dsp_permissions_scripts.models.group import UNKNOWN_USER
from dsp_permissions_scripts.models.group import BuiltinGroup
from dsp_permissions_scripts.models.group import GroupType


def _get_sort_pos_of_custom_group(prefixed_iri: str) -> int:
alphabet = list("abcdefghijklmnopqrstuvwxyz")
relevant_letter = prefixed_iri.split(":")[-1][0]
return alphabet.index(relevant_letter.lower()) + 99 # must be higher than the highest index of the builtin groups


def sort_groups(groups_original: Iterable[GroupType]) -> list[GroupType]:
"""
Sorts groups:
- First according to their power (most powerful first - only applicable for built-in groups)
- Then alphabetically (custom groups)
"""
sort_key = [SYSTEM_ADMIN, CREATOR, PROJECT_ADMIN, PROJECT_MEMBER, KNOWN_USER, UNKNOWN_USER]
groups = list(groups_original)
groups.sort(
key=lambda x: sort_key.index(x)
if isinstance(x, BuiltinGroup)
else _get_sort_pos_of_custom_group(x.prefixed_iri)
)
return groups
2 changes: 1 addition & 1 deletion dsp_permissions_scripts/utils/scope_serialization.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Any

from dsp_permissions_scripts.models.group import get_full_iri_from_prefixed_iri
from dsp_permissions_scripts.models.group import sort_groups
from dsp_permissions_scripts.models.group_utils import sort_groups
from dsp_permissions_scripts.models.scope import PermissionScope
from dsp_permissions_scripts.utils.dsp_client import DspClient

Expand Down
2 changes: 1 addition & 1 deletion tests/test_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from dsp_permissions_scripts.models.group import get_prefixed_iri_from_full_iri
from dsp_permissions_scripts.models.group import group_builder
from dsp_permissions_scripts.models.group import is_prefixed_group_iri
from dsp_permissions_scripts.models.group import sort_groups
from dsp_permissions_scripts.models.group_utils import sort_groups
from dsp_permissions_scripts.utils.dsp_client import DspClient


Expand Down

0 comments on commit 6dc2ccc

Please sign in to comment.