• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Brunowar12 / TaskManagerSystem / 14985374433

13 May 2025 12:25AM UTC coverage: 93.727%. First build
14985374433

push

github

web-flow
Polishing & refinement (#22)

* update

+ optimized categories, security, tasks and users tests
+ api status version modified
+ custom middleware for control large request

* update

project update:
+ role permissions dictionary
+ dynamic api version update for urls
+ use error_response util
for users app intregrated error_response util
tasks:
+ split the responsibility of the save method (model)
+ reduce move task to proj service
+ integrate error_response ta status_response
+ changes for project logic
api:
+ adding global mixins

* projects update

+ post_migrate creating roles
+ fixed roles
+ clean method for role
+ is_active field for sharelink
+ settings for sharelink
+ new classes for check roles in permissions
+ description field in serializer
+ reduced project service
+ created share link service with validation and create share link methods
+ transaction atomic for assign role
+ nested router for access tasks crud in project instead of tasks actions in projects
+ roles settings
+ now user can be invited in project only by share link
+ generate sharelink reduce
+ kick method
+ roleviewset and projectmembershipviewset now only readonly
+ atomic transaction for join project

* tests update

+ permission tests update (+4 new tests)
+ fixed task test
+ a very extensive work on test projects, almost everything's been remodeled.

* feat

+ gh actions upload to coveralls
+ new updated readme file
+ swagger\redoc api documentation

* fix

+ api_status with no permissions
+ requirements

* feat

+ split userviewset
+ auth endpoint become account

* Update test_setup.py

* feat projects

+ deleted permissionviewset
+ serializer for kick user action and assign role for correct swagger
+ endpoints fixed from projects/projects/

* fix

* feat, fix

+ feat urls for projects, tasks
+ new serializers for tasks actions

699 of 735 new or added lines in 31 files covered. (95.1%)

1509 of 1610 relevant lines covered (93.73%)

9.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.46
/projects/models.py
1
# mypy: disable-error-code=var-annotated
2

3
import uuid
10✔
4
from django.contrib.auth.models import Permission
10✔
5
from django.core.exceptions import ValidationError
10✔
6
from django.db import models
10✔
7
from django.conf import settings
10✔
8
from django.utils import timezone
10✔
9

10
from api.validators import TEXT_FIELD_VALIDATOR
10✔
11

12

13
class Project(models.Model):
10✔
14
    name = models.CharField(max_length=128, validators=[TEXT_FIELD_VALIDATOR])
10✔
15
    description = models.TextField(blank=True)
10✔
16
    owner = models.ForeignKey(
10✔
17
        settings.AUTH_USER_MODEL,
18
        on_delete=models.CASCADE,
19
        related_name="projects",
20
    )
21
    created_at = models.DateTimeField(auto_now_add=True)
10✔
22

23
    class Meta:
10✔
24
        ordering = ["id"]
10✔
25

26
    def __str__(self):
10✔
27
        return f"{self.name} (Owner: {self.owner.username})"
×
28

29

30
class Role(models.Model):
10✔
31
    ADMIN, MODERATOR, MEMBER, VIEWER = "Admin", "Moderator", "Member", "Viewer"
10✔
32
    FIXED_ROLES = [ADMIN, MODERATOR, MEMBER, VIEWER]
10✔
33
    
34
    name = models.CharField(max_length=64, unique=True)
10✔
35
    permissions = models.ManyToManyField(Permission, blank=True)
10✔
36
    
37
    def clean(self):
10✔
38
        if self.name not in self.FIXED_ROLES:
10✔
NEW
39
            raise ValidationError(
×
40
                f"Custom roles are not allowed. Use one of: {', '.join(self.FIXED_ROLES)}"
41
            )
42
            
43
    def save(self, *args, **kwargs):
10✔
44
        self.full_clean()
10✔
45
        super().save(*args, **kwargs)
10✔
46

47
    def __str__(self):
10✔
48
        return self.name
×
49

50

51
class ProjectMembership(models.Model):
10✔
52
    user = models.ForeignKey(
10✔
53
        settings.AUTH_USER_MODEL,
54
        on_delete=models.CASCADE,
55
        related_name="project_memberships",
56
    )
57
    project = models.ForeignKey(
10✔
58
        Project, on_delete=models.CASCADE, related_name="memberships"
59
    )
60
    role = models.ForeignKey(
10✔
61
        Role, on_delete=models.CASCADE, related_name="members"
62
    )
63

64
    class Meta:
10✔
65
        unique_together = ("user", "project")
10✔
66

67
    def __str__(self):
10✔
68
        return f"{self.user.username} - {self.project.name} ({self.role.name})"
×
69

70

71
class ProjectShareLink(models.Model):
10✔
72
    token = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
10✔
73
    project = models.ForeignKey(
10✔
74
        Project, on_delete=models.CASCADE, related_name="share_links"
75
    )
76
    role = models.ForeignKey(
10✔
77
        Role,
78
        on_delete=models.CASCADE,
79
        related_name="share_links",
80
        help_text="Role for invited user",
81
    )
82
    max_uses = models.PositiveIntegerField(
10✔
83
        null=True,
84
        blank=True,
85
        help_text="Maximum number of uses. Leave blank for unlimited uses",
86
    )
87
    used_count = models.PositiveIntegerField(default=0)
10✔
88
    is_active = models.BooleanField(default=True)
10✔
89
    expires_at = models.DateTimeField()
10✔
90
    created_by = models.ForeignKey(
10✔
91
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE
92
    )
93
    created_at = models.DateTimeField(auto_now_add=True)
10✔
94

95
    def clean(self):
10✔
96
        errors = {}
×
97
        if self.max_uses is not None and self.max_uses <= 0:
×
NEW
98
            errors["max_uses"] = "The max_uses value must be a positive number or left blank"
×
99
        if self.max_uses is not None and self.used_count > self.max_uses:
×
100
            errors["used_count"] = "The number of uses exceeds the set limit"
×
NEW
101
        if self.expires_at <= timezone.now():
×
NEW
102
            errors["expires_at"] = "The expiration date must be in the future"
×
103

104
        if errors:
×
105
            raise ValidationError(errors)
×
106

107
    def is_expired(self):
10✔
108
        return timezone.now() >= self.expires_at
10✔
109

110
    def is_usage_exceeded(self):
10✔
111
        return self.max_uses is not None and self.used_count >= self.max_uses
10✔
112

113
    def is_valid(self):
10✔
114
        return self.is_active and not self.is_expired() and not self.is_usage_exceeded()
10✔
115

116
    def __str__(self):
10✔
117
        return f"Link to {self.project.name} ({self.role.name})"
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc