95 lines
3.7 KiB
Python
95 lines
3.7 KiB
Python
|
|
from datetime import timedelta
|
||
|
|
from uuid import uuid4
|
||
|
|
from django.utils import timezone
|
||
|
|
from django.utils.translation import gettext_lazy as _
|
||
|
|
from django.db.models import BigAutoField, BooleanField, CASCADE, CharField, DateTimeField, ForeignKey, ManyToManyField, Model, TextField, UUIDField
|
||
|
|
from apps.users.mixins import TimeStampMixin
|
||
|
|
from apps.users.models import User
|
||
|
|
|
||
|
|
class Organization(TimeStampMixin, Model):
|
||
|
|
|
||
|
|
id = BigAutoField(primary_key = True)
|
||
|
|
uuid = UUIDField(default = uuid4, unique = True, editable = False)
|
||
|
|
name = CharField(max_length = 255, unique = True)
|
||
|
|
|
||
|
|
description = TextField(blank = True, default = '')
|
||
|
|
|
||
|
|
owner = ForeignKey(User, on_delete = CASCADE, related_name = 'owned_organizations')
|
||
|
|
members = ManyToManyField(User, through = 'OrganizationMembership', related_name = 'organizations')
|
||
|
|
|
||
|
|
class Meta:
|
||
|
|
verbose_name = _('Organization')
|
||
|
|
verbose_name_plural = _('Organizations')
|
||
|
|
|
||
|
|
def __str__(self) -> str:
|
||
|
|
return self.name
|
||
|
|
|
||
|
|
class OrganizationMembership(TimeStampMixin, Model):
|
||
|
|
|
||
|
|
id = BigAutoField(primary_key = True)
|
||
|
|
user = ForeignKey(User, on_delete = CASCADE, related_name = 'organization_memberships')
|
||
|
|
organization = ForeignKey(Organization, on_delete = CASCADE, related_name = 'memberships')
|
||
|
|
is_manager = BooleanField(default = False)
|
||
|
|
|
||
|
|
class Meta:
|
||
|
|
verbose_name = _('Organization Membership')
|
||
|
|
verbose_name_plural = _('Organization Memberships')
|
||
|
|
unique_together = [['user', 'organization']]
|
||
|
|
|
||
|
|
def __str__(self) -> str:
|
||
|
|
return f'{self.user.full_name} - {self.organization.name} ({self.is_manager})'
|
||
|
|
|
||
|
|
class OrganizationInvitation(TimeStampMixin, Model):
|
||
|
|
|
||
|
|
id = BigAutoField(primary_key = True)
|
||
|
|
token = UUIDField(default = uuid4, unique = True, editable = False)
|
||
|
|
organization = ForeignKey(Organization, on_delete = CASCADE, related_name = "invite_tokens")
|
||
|
|
created_by = ForeignKey(User, on_delete = CASCADE, related_name = "created_invites")
|
||
|
|
expires_at = DateTimeField()
|
||
|
|
used_by = ForeignKey(User, on_delete = CASCADE, null = True, blank = True, related_name = "used_invites")
|
||
|
|
used_at = DateTimeField(null = True, blank = True)
|
||
|
|
is_active = BooleanField(default = True)
|
||
|
|
|
||
|
|
class Meta:
|
||
|
|
verbose_name = _("Invite Token")
|
||
|
|
verbose_name_plural = _("Invite Tokens")
|
||
|
|
|
||
|
|
def save(self, *args, **kwargs):
|
||
|
|
if not self.expires_at:
|
||
|
|
self.expires_at = timezone.now() + timedelta(days=7)
|
||
|
|
super().save(*args, **kwargs)
|
||
|
|
|
||
|
|
def is_valid(self):
|
||
|
|
return self.is_active and not self.used_by and timezone.now() < self.expires_at
|
||
|
|
|
||
|
|
def __str__(self) -> str:
|
||
|
|
return f"Invite for {self.organization.name} by {self.created_by.full_name} (expires {self.expires_at})"
|
||
|
|
|
||
|
|
class Role(TimeStampMixin, Model):
|
||
|
|
|
||
|
|
id = BigAutoField(primary_key = True)
|
||
|
|
name = CharField(max_length = 100, unique = True)
|
||
|
|
uuid = UUIDField(default = uuid4, editable = False, unique = True)
|
||
|
|
organization = ForeignKey(Organization, on_delete = CASCADE, related_name = "roles")
|
||
|
|
members = ManyToManyField(User, through = "RoleMembership", related_name = "roles")
|
||
|
|
|
||
|
|
class Meta:
|
||
|
|
verbose_name = _('Role')
|
||
|
|
verbose_name_plural = _('Roles')
|
||
|
|
|
||
|
|
def __str__(self) -> str:
|
||
|
|
return self.name
|
||
|
|
|
||
|
|
class RoleMembership(TimeStampMixin, Model):
|
||
|
|
|
||
|
|
user = ForeignKey(User, on_delete = CASCADE, related_name = "role_memberships")
|
||
|
|
role = ForeignKey(Role, on_delete = CASCADE, related_name = "memberships")
|
||
|
|
|
||
|
|
class Meta:
|
||
|
|
verbose_name = _("Role Membership")
|
||
|
|
verbose_name_plural = _("Role Memberships")
|
||
|
|
unique_together = [["user", "role"]]
|
||
|
|
|
||
|
|
def __str__(self) -> str:
|
||
|
|
return f"{self.user.full_name} - {self.role.name}"
|