Coverage for sm / models.py: 92%
38 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-24 12:43 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-24 12:43 +0000
1from django.db import models
2from django.contrib.auth.models import Group, User
3from django.db.models.signals import post_save
4from django.dispatch import receiver
5from django.utils import timezone
6from typing import Any
7import uuid
10class GroupProfile(models.Model):
11 group = models.OneToOneField(
12 Group, on_delete=models.CASCADE, related_name="profile"
13 )
14 owner = models.ForeignKey(
15 User,
16 on_delete=models.SET_NULL,
17 null=True,
18 blank=True,
19 related_name="owned_groups",
20 )
21 max_items = models.PositiveIntegerField(
22 default=200,
23 help_text="Maximum number of items across all models for this group.",
24 )
25 max_users = models.PositiveIntegerField(
26 default=2, help_text="Maximum number of users allowed in this group."
27 )
29 def __str__(self) -> str:
30 return f"Profile for {self.group.name}"
32 class Meta:
33 verbose_name = "Group Profile"
34 verbose_name_plural = "Group Profiles"
37@receiver(post_save, sender=Group)
38def create_group_profile(
39 sender: Any, instance: Group, created: bool, **kwargs: Any
40) -> None:
41 if created:
42 GroupProfile.objects.get_or_create(group=instance)
43 from .utils_permissions import sync_group_permissions
45 sync_group_permissions(instance)
48class Invitation(models.Model):
49 email = models.EmailField()
50 group = models.ForeignKey(
51 Group, on_delete=models.CASCADE, related_name="invitations"
52 )
53 token = models.UUIDField(default=uuid.uuid4, unique=True)
54 created_at = models.DateTimeField(default=timezone.now)
55 created_by = models.ForeignKey(
56 User, on_delete=models.SET_NULL, null=True, related_name="invitations_sent"
57 )
58 accepted_at = models.DateTimeField(null=True, blank=True)
60 class Meta:
61 verbose_name = "Invitation"
62 verbose_name_plural = "Invitations"
63 unique_together = ["email", "group"]
65 def __str__(self) -> str:
66 return f"Invitation for {self.email} to {self.group.name}"
68 def is_expired(self) -> bool:
69 return timezone.now() - self.created_at > timezone.timedelta(hours=24)