Skip to content
Snippets Groups Projects
Commit 15a87853 authored by Roang's avatar Roang
Browse files

Merge branch 'docs/invitation' into 'develop'

Docs/invitation

See merge request hub/hub!1060
parents e52e633b 7bb4f294
No related branches found
No related tags found
No related merge requests found
......@@ -1170,6 +1170,12 @@ msgstr "Team Mitglied"
msgid "Habitat"
msgstr "Habitatszuordnung"
msgid "Invitation__name"
msgstr "Einladung"
msgid "Invitation__name__plural"
msgstr "Einladungen"
msgid "Invitation__comment"
msgstr "Kommentar"
......
......@@ -1170,6 +1170,12 @@ msgstr "Team member"
msgid "Habitat"
msgstr "Habitat assignment"
msgid "Invitation__name"
msgstr "invitation"
msgid "Invitation__name__plural"
msgstr "invitations"
msgid "Invitation__comment"
msgstr "comment"
......
# Generated by Django 5.1.3 on 2024-11-30 12:58
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("core", "0162_remove_workadventure"),
]
operations = [
migrations.AlterModelOptions(
name="invitation",
options={
"verbose_name": "Invitation__name",
"verbose_name_plural": "Invitation__name__plural",
},
),
]
......@@ -18,6 +18,10 @@ if TYPE_CHECKING: # pragma: no cover
class InvitationAlreadyExists(ValidationError):
"""
This exception is raised when an invitation already exists between a requester and a requested entity.
"""
def __init__(self, invitation: 'Invitation') -> None:
self.invitation = invitation
super().__init__(
......@@ -28,6 +32,10 @@ class InvitationAlreadyExists(ValidationError):
class InvitationRejectedTimeout(ValidationError):
"""
This exception is raised when an invitation has been rejected and the timeout has not yet expired.
"""
def __init__(self) -> None:
super().__init__(
_('Invitation__error__rejected_timeout'),
......@@ -36,6 +44,16 @@ class InvitationRejectedTimeout(ValidationError):
class RequestsState(models.TextChoices):
"""
Enumeration for the state of requests.
Attributes:
REQUESTED: The request has been made.
WITHDRAWN: The request has been withdrawn.
ACCEPTED: The request has been accepted.
REJECTED: The request has been rejected.
"""
REQUESTED = 'requested', _('Invitation__requests_state-requested')
WITHDRAWN = 'withdrawn', _('Invitation__requests_state-withdrawn')
ACCEPTED = 'accepted', _('Invitation__requests_state-accepted')
......@@ -43,18 +61,42 @@ class RequestsState(models.TextChoices):
class InvitationType(models.TextChoices):
"""
Enumeration for the type of invitations.
Attributes:
ASSEMBLY_MEMBER: Invitation for an Conferencemember to join an assembly.
TEAM_MEMBER: Invitation for an Conferencemember to join a team.
HABITAT: Invitation of ah Assembly for a habitat.
"""
ASSEMBLY_MEMBER = 'ASSEMBLY_MEMBER', _('Assembly Member')
TEAM_MEMBER = 'TEAM_MEMBER', _('Team Member')
HABITAT = 'HABITAT', _('Habitat')
class Invitation(models.Model):
"""
Model representing an invitation.
Attributes:
requester_type: The type of the requester (ContentType).
requester_id: The ID of the requester.
requester: The requester (GenericForeignKey).
requested_type: The type of the requested entity (ContentType).
requested_id: The ID of the requested entity.
requested: The requested entity (GenericForeignKey).
comment: A comment associated with the invitation.
"""
class Meta:
indexes = [
models.Index(fields=['requester_id', 'requested_id']),
models.Index(fields=['requester_type', 'requester_id'], name='requester_idx'),
models.Index(fields=['requested_type', 'requested_id'], name='requested_idx'),
]
verbose_name = _('Invitation__name')
verbose_name_plural = _('Invitation__name__plural')
# Make the enums available through the class
InvitationType = InvitationType
......@@ -108,9 +150,20 @@ class Invitation(models.Model):
@classmethod
def type_is(cls, obj: object) -> TypeIs['Invitation']:
"""Check if the object is an Invitation.
Returns:
TypeIs[Invitation]: True if the object is an Invitation, False otherwise.
"""
return isinstance(obj, cls)
def clean(self) -> None:
"""Validation for the invitation class.
Raises:
InvitationAlreadyExists: Is raised when an invitation already exists between a requester and a requested entity.
InvitationRejectedTimeout: Is raised when an invitation has been rejected and the timeout has not yet expired.
"""
qs = Invitation.objects.filter(requester_id=self.requester_id, requested_id=self.requested_id).exclude(id=self.id)
if qs.exists():
if qs.filter(state=RequestsState.REQUESTED).exists():
......@@ -123,9 +176,17 @@ class Invitation(models.Model):
hours=settings.INVITATION_REJECTION_DEFAULT_TIMEOUT
):
raise InvitationRejectedTimeout
return super().clean()
super().clean()
def accept(self, user: 'PlatformUser'):
"""Accept the invitation.
Args:
user (PlatformUser): The user who accepts the invitation.
Raises:
NotImplementedError: Raied when the invitation type is not yet implemented.
"""
self.decision_by = user
match self.type:
case InvitationType.HABITAT:
......@@ -134,6 +195,11 @@ class Invitation(models.Model):
raise NotImplementedError
def accept_habitat(self):
"""Accept the invitation for a joining a habitat.
Raises:
ValueError: I raised when the requested or requester type is not a habitat.
"""
from core.models import Assembly # pylint: disable=import-outside-toplevel
if not Assembly.type_is(self.requested): # pragma: no cover
......@@ -165,11 +231,21 @@ class Invitation(models.Model):
)
def reject(self, user: 'PlatformUser'):
"""Reject the invitation.
Args:
user (PlatformUser): The user rejecting the invitation.
"""
self.decision_by = user
self.state = RequestsState.REJECTED
self.save()
def withdraw(self, user: 'PlatformUser'):
"""Withdraw the invitation.
Args:
user (PlatformUser): The user withdrawing the invitation.
"""
self.decision_by = user
self.state = RequestsState.WITHDRAWN
self.save()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment