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

liqd / adhocracy-plus / 18908688697

29 Oct 2025 12:59PM UTC coverage: 44.622% (-44.5%) from 89.135%
18908688697

Pull #2986

github

web-flow
Merge 1dfde8ee7 into 445e1d498
Pull Request #2986: Draft: Speed up Github Ci Tests

3012 of 6750 relevant lines covered (44.62%)

0.45 hits per line

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

58.14
/apps/projects/models.py
1
import uuid
1✔
2

3
from django.conf import settings
1✔
4
from django.db import models
1✔
5
from django.urls import reverse
1✔
6
from django.utils.translation import gettext_lazy as _
1✔
7

8
from adhocracy4.models import base
1✔
9
from adhocracy4.projects.models import Project
1✔
10

11
from . import emails
1✔
12

13

14
class Invite(base.TimeStampedModel):
1✔
15
    creator = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
1✔
16
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
1✔
17
    email = models.EmailField()
1✔
18
    token = models.UUIDField(default=uuid.uuid4, unique=True)
1✔
19
    site = models.CharField(max_length=200)
1✔
20

21
    class Meta:
1✔
22
        abstract = True
1✔
23

24
    def accept(self, user):
1✔
25
        self.delete()
×
26

27
    def reject(self):
1✔
28
        self.delete()
×
29

30

31
class ParticipantInviteManager(models.Manager):
1✔
32
    def invite(self, creator, project, email, site):
1✔
33
        invite = super().create(
×
34
            project=project, creator=creator, email=email, site=site
35
        )
36
        emails.InviteParticipantEmail.send(invite)
×
37
        return invite
×
38

39

40
class ParticipantInvite(Invite):
1✔
41
    objects = ParticipantInviteManager()
1✔
42

43
    def __str__(self):
1✔
44
        return "Participation invite to {s.project} for {s.email}".format(s=self)
×
45

46
    def get_absolute_url(self):
1✔
47
        url_kwargs = {
×
48
            "organisation_slug": self.project.organisation.slug,
49
            "invite_token": self.token,
50
        }
51
        return reverse("project-participant-invite-detail", kwargs=url_kwargs)
×
52

53
    def accept(self, user):
1✔
54
        self.project.participants.add(user)
×
55
        super().accept(user)
×
56

57
    class Meta:
1✔
58
        unique_together = ("email", "project")
1✔
59

60

61
class ModeratorInviteManager(models.Manager):
1✔
62
    def invite(self, creator, project, email, site):
1✔
63
        invite = super().create(
×
64
            project=project, creator=creator, email=email, site=site
65
        )
66
        emails.InviteModeratorEmail.send(invite)
×
67
        return invite
×
68

69

70
class ModeratorInvite(Invite):
1✔
71
    objects = ModeratorInviteManager()
1✔
72

73
    def __str__(self):
1✔
74
        return "Moderation invite to {s.project} for {s.email}".format(s=self)
×
75

76
    def get_absolute_url(self):
1✔
77
        url_kwargs = {
×
78
            "organisation_slug": self.project.organisation.slug,
79
            "invite_token": self.token,
80
        }
81
        return reverse("project-moderator-invite-detail", kwargs=url_kwargs)
×
82

83
    def accept(self, user):
1✔
84
        self.project.moderators.add(user)
×
85
        super().accept(user)
×
86

87
    class Meta:
1✔
88
        unique_together = ("email", "project")
1✔
89

90

91
class ProjectInsight(base.TimeStampedModel):
1✔
92
    project = models.OneToOneField(
1✔
93
        Project, related_name="insight", on_delete=models.CASCADE
94
    )
95
    active_participants = models.ManyToManyField(settings.AUTH_USER_MODEL)
1✔
96
    unregistered_participants = models.PositiveIntegerField(default=0)
1✔
97
    comments = models.PositiveIntegerField(default=0)
1✔
98
    ratings = models.PositiveIntegerField(default=0)
1✔
99
    written_ideas = models.PositiveIntegerField(default=0)
1✔
100
    poll_answers = models.PositiveIntegerField(default=0)
1✔
101
    live_questions = models.PositiveIntegerField(default=0)
1✔
102
    display = models.BooleanField(default=False)
1✔
103

104
    @staticmethod
1✔
105
    def update_context(project, context, dashboard=False):
1✔
106
        insight, created = ProjectInsight.objects.get_or_create(project=project)
×
107
        if dashboard or insight.display:
×
108
            context.update(create_insight_context(insight=insight))
×
109
        return context
×
110

111
    def __str__(self):
1✔
112
        return "Insights for project %s" % self.project.name
×
113

114

115
def create_insight_context(insight: ProjectInsight) -> dict:
1✔
116
    """
117
    ("BS", _("brainstorming")),
118
    ("MBS", _("spatial brainstorming")),
119
    ("IC", _("idea challenge")),
120
    ("MIC", _("spatial idea challenge")),
121
    ("TR", _("text review")),
122
    ("PO", _("poll")),
123
    ("PB", _("participatory budgeting")),
124
    ("IE", _("interactive event")),
125
    ("TP", _("prioritization")),
126
    ("DB", _("debate")),
127
    """
128

129
    active_modules = [
×
130
        module for module in insight.project.modules if not module.is_draft
131
    ]
132
    blueprint_types = {module.blueprint_type for module in active_modules}
×
133
    show_polls = "PO" in blueprint_types
×
134
    show_live_questions = "IE" in blueprint_types
×
135
    show_ideas = bool(blueprint_types.intersection({"BS", "IC", "MBS", "MIC", "PB"}))
×
136

137
    counts = [
×
138
        (
139
            _("active participants"),
140
            insight.active_participants.count() + insight.unregistered_participants,
141
        ),
142
        (_("comments"), insight.comments),
143
        (_("ratings"), insight.ratings),
144
    ]
145

146
    if show_ideas:
×
147
        counts.append((_("written ideas"), insight.written_ideas))
×
148

149
    if show_polls:
×
150
        counts.append((_("poll answers"), insight.poll_answers))
×
151

152
    if show_live_questions:
×
153
        counts.append((_("interactive event questions"), insight.live_questions))
×
154

155
    return dict(
×
156
        insight_label=_(
157
            """This section provides an overview of the participation
158
            activities in this project."""
159
        ),
160
        counts=counts,
161
    )
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

© 2025 Coveralls, Inc