• 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

0.0
/apps/dashboard/views.py
1
import json
×
2
from urllib import parse
×
3

4
from django.apps import apps
×
5
from django.contrib import messages
×
6
from django.contrib.messages.views import SuccessMessageMixin
×
7
from django.http import HttpResponseRedirect
×
8
from django.urls import resolve
×
9
from django.urls import reverse
×
10
from django.utils.translation import gettext_lazy as _
×
11
from django.views import generic
×
12
from django.views.generic.detail import SingleObjectMixin
×
13

14
from adhocracy4.dashboard import get_project_dashboard
×
15
from adhocracy4.dashboard import mixins
×
16
from adhocracy4.dashboard import signals
×
17
from adhocracy4.dashboard.blueprints import get_blueprints
×
18
from adhocracy4.modules import models as module_models
×
19
from adhocracy4.phases import models as phase_models
×
20
from adhocracy4.projects import models as project_models
×
21
from adhocracy4.projects.mixins import ProjectMixin
×
22

23
from .forms import DashboardProjectCreateForm
×
24

25

26
class ModuleBlueprintListView(
×
27
    ProjectMixin,
28
    mixins.DashboardBaseMixin,
29
    mixins.DashboardComponentMixin,
30
    generic.DetailView,
31
):
32
    template_name = "a4_candy_dashboard/module_blueprint_list.html"
×
33
    permission_required = "a4projects.change_project"
×
34
    model = project_models.Project
×
35
    slug_url_kwarg = "project_slug"
×
36
    menu_item = "project"
×
37

38
    @property
×
39
    def blueprints(self):
×
40
        return [(key, data) for key, data in get_blueprints()]
×
41

42
    def get_permission_object(self):
×
43
        return self.project
×
44

45

46
class ModuleCreateView(
×
47
    ProjectMixin,
48
    mixins.DashboardBaseMixin,
49
    mixins.BlueprintMixin,
50
    SingleObjectMixin,
51
    generic.View,
52
):
53
    permission_required = "a4projects.change_project"
×
54
    model = project_models.Project
×
55
    slug_url_kwarg = "project_slug"
×
56
    success_message = _("The module was created")
×
57

58
    def post(self, request, *args, **kwargs):
×
59
        project = self.get_object()
×
60
        weight = 1
×
61
        if project.modules:
×
62
            weight = max(project.modules.values_list("weight", flat=True)) + 1
×
63
        module = module_models.Module(
×
64
            name=self.blueprint.title,
65
            weight=weight,
66
            project=project,
67
            is_draft=True,
68
            blueprint_type=self.blueprint.type,
69
        )
70
        module.save()
×
71
        signals.module_created.send(sender=None, module=module, user=self.request.user)
×
72

73
        self._create_module_settings(module)
×
74
        self._create_phases(module, self.blueprint.content)
×
75
        messages.success(self.request, self.success_message)
×
76

77
        cookie = request.COOKIES.get("dashboard_projects_closed_accordions", "[]")
×
78
        ids = json.loads(parse.unquote(cookie))
×
79
        if self.project.id not in ids:
×
80
            ids.append(self.project.id)
×
81

82
        cookie = parse.quote(json.dumps(ids))
×
83

84
        response = HttpResponseRedirect(self.get_next(module))
×
85
        response.set_cookie("dashboard_projects_closed_accordions", cookie)
×
86
        return response
×
87

88
    def _create_module_settings(self, module):
×
89
        if self.blueprint.settings_model:
×
90
            settings_model = apps.get_model(*self.blueprint.settings_model)
×
91
            module_settings = settings_model(module=module)
×
92
            module_settings.save()
×
93

94
    def _create_phases(self, module, blueprint_phases):
×
95
        for index, phase_content in enumerate(blueprint_phases):
×
96
            phase = phase_models.Phase(
×
97
                type=phase_content.identifier,
98
                name=phase_content.name,
99
                description=phase_content.description,
100
                weight=index,
101
                module=module,
102
            )
103
            phase.save()
×
104

105
    def get_next(self, module):
×
106
        return reverse(
×
107
            "a4dashboard:dashboard-module_basic-edit",
108
            kwargs={
109
                "organisation_slug": module.project.organisation.slug,
110
                "module_slug": module.slug,
111
            },
112
        )
113

114
    def get_permission_object(self):
×
115
        return self.project
×
116

117

118
class ModulePublishView(SingleObjectMixin, mixins.DashboardBaseMixin, generic.View):
×
119
    permission_required = "a4projects.change_project"
×
120
    model = module_models.Module
×
121
    slug_url_kwarg = "module_slug"
×
122

123
    def get_permission_object(self):
×
124
        return self.get_object().project
×
125

126
    def post(self, request, *args, **kwargs):
×
127
        action = request.POST.get("action", None)
×
128
        if action == "publish":
×
129
            self.publish_module()
×
130
        elif action == "unpublish":
×
131
            self.unpublish_module()
×
132
        else:
133
            messages.warning(self.request, _("Invalid action"))
×
134

135
        return HttpResponseRedirect(self.get_next())
×
136

137
    def get_next(self):
×
138
        if "referrer" in self.request.POST:
×
139
            return self.request.POST["referrer"]
×
140
        elif "Referer" in self.request.headers:
×
141
            return self.request.headers["Referer"]
×
142

143
        return reverse(
×
144
            "a4dashboard:project-edit",
145
            kwargs={
146
                "project_slug": self.get_object().project.slug,
147
                "organisation_slug": self.get_object().project.organisation.slug,
148
            },
149
        )
150

151
    def publish_module(self):
×
152
        module = self.get_object()
×
153
        if not module.is_draft:
×
154
            messages.info(self.request, _("Module is already added"))
×
155
            return
×
156

157
        dashboard = get_project_dashboard(module.project)
×
158
        num_valid, num_required = dashboard.get_module_progress(module)
×
159
        is_complete = num_valid == num_required
×
160

161
        if not is_complete:
×
162
            messages.error(
×
163
                self.request,
164
                _("Module cannot be added. " "Required fields are missing."),
165
            )
166
            return
×
167

168
        module.is_draft = False
×
169
        module.save()
×
170

171
        signals.module_published.send(
×
172
            sender=None, module=module, user=self.request.user
173
        )
174

175
        messages.success(self.request, _("The module is displayed in the project."))
×
176

177
    def unpublish_module(self):
×
178
        module = self.get_object()
×
179
        if module.is_draft:
×
180
            messages.info(self.request, _("Module is already removed"))
×
181
            return
×
182
        if not module.project.is_draft:
×
183
            messages.error(
×
184
                self.request, _("Module cannot be removed " "from a published project.")
185
            )
186
            return
×
187

188
        module.is_draft = True
×
189
        module.save()
×
190

191
        signals.module_unpublished.send(
×
192
            sender=None, module=module, user=self.request.user
193
        )
194

195
        messages.success(
×
196
            self.request, _("The module is no longer displayed in the project.")
197
        )
198

199

200
class ModuleDeleteView(mixins.DashboardBaseMixin, generic.DeleteView):
×
201
    permission_required = "a4projects.change_project"
×
202
    model = module_models.Module
×
203
    success_message = _("The module has been deleted")
×
204

205
    def form_valid(self, request, *args, **kwargs):
×
206
        if not self.get_object().is_draft:
×
207
            messages.error(
×
208
                self.request,
209
                _(
210
                    "Module cannot be deleted. "
211
                    "It has to be removed from the project first."
212
                ),
213
            )
214
            failure_url = self.get_failure_url()
×
215
            return HttpResponseRedirect(failure_url)
×
216
        messages.success(self.request, self.success_message)
×
217
        return super().form_valid(request, *args, **kwargs)
×
218

219
    def get_permission_object(self):
×
220
        return self.get_object().project
×
221

222
    def get_success_url(self):
×
223
        referrer = self.request.POST.get("referrer", None) or self.request.headers.get(
×
224
            "Referer", None
225
        )
226
        if referrer:
×
227
            view, args, kwargs = resolve(referrer)
×
228
            if (
×
229
                "module_slug" not in kwargs
230
                or not kwargs["module_slug"] == self.get_object().slug
231
            ):
232
                return referrer
×
233

234
        return reverse(
×
235
            "a4dashboard:project-edit",
236
            kwargs={
237
                "project_slug": self.get_object().project.slug,
238
                "organisation_slug": self.get_object().project.organisation.slug,
239
            },
240
        )
241

242
    def get_failure_url(self):
×
243
        if "referrer" in self.request.POST:
×
244
            return self.request.POST["referrer"]
×
245
        elif "Referer" in self.request.headers:
×
246
            return self.request.headers["Referer"]
×
247

248
        return reverse(
×
249
            "a4dashboard:project-edit",
250
            kwargs={
251
                "project_slug": self.get_object().project.slug,
252
                "organisation_slug": self.get_object().project.organisation.slug,
253
            },
254
        )
255

256

257
class ProjectCreateView(
×
258
    mixins.DashboardBaseMixin, SuccessMessageMixin, generic.CreateView
259
):
260
    model = project_models.Project
×
261
    slug_url_kwarg = "project_slug"
×
262
    form_class = DashboardProjectCreateForm
×
263
    template_name = "a4dashboard/project_create_form.html"
×
264
    permission_required = "a4projects.add_project"
×
265
    menu_item = "project"
×
266
    success_message = _("Project was created.")
×
267

268
    def get_permission_object(self):
×
269
        return self.organisation
×
270

271
    def get_form_kwargs(self):
×
272
        kwargs = super().get_form_kwargs()
×
273
        kwargs["organisation"] = self.organisation
×
274
        kwargs["creator"] = self.request.user
×
275
        return kwargs
×
276

277
    def get_success_url(self):
×
278
        return reverse(
×
279
            "a4dashboard:project-edit",
280
            kwargs={
281
                "project_slug": self.object.slug,
282
                "organisation_slug": self.organisation.slug,
283
            },
284
        )
285

286
    def form_valid(self, form):
×
287
        response = super().form_valid(form)
×
288
        signals.project_created.send(
×
289
            sender=None, project=self.object, user=self.request.user
290
        )
291

292
        return response
×
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