• 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/projects/serializers.py
1
from functools import lru_cache
×
2

3
from django.urls import reverse
×
4
from django.utils.translation import gettext_lazy as _
×
5
from easy_thumbnails.files import get_thumbnailer
×
6
from rest_framework import serializers
×
7

8
from adhocracy4.api.dates import get_date_display
×
9
from adhocracy4.api.dates import get_datetime_display
×
10
from adhocracy4.categories.models import Category
×
11
from adhocracy4.labels.models import Label
×
12
from adhocracy4.maps.mixins import PointSerializerMixin
×
13
from adhocracy4.modules.models import Module
×
14
from adhocracy4.phases.models import Phase
×
15
from adhocracy4.projects.models import Project
×
16
from apps.projects import helpers
×
17

18

19
class AppProjectSerializer(PointSerializerMixin, serializers.ModelSerializer):
×
20
    information = serializers.SerializerMethodField()
×
21
    result = serializers.SerializerMethodField()
×
22
    # todo: remove many=True once AppProjects are restricted to single module
23
    published_modules = serializers.PrimaryKeyRelatedField(read_only=True, many=True)
×
24
    organisation = serializers.SerializerMethodField()
×
25
    organisation_logo = serializers.SerializerMethodField()
×
26
    access = serializers.SerializerMethodField()
×
27
    single_idea_collection_module = serializers.SerializerMethodField()
×
28
    single_poll_module = serializers.SerializerMethodField()
×
29
    participation_time_display = serializers.SerializerMethodField()
×
30
    has_contact_info = serializers.SerializerMethodField()
×
31
    url = serializers.SerializerMethodField()
×
32

33
    def get_geojson_properties(self):
×
34
        return {"strname": "street_name", "hsnr": "house_number", "plz": "zip_code"}
×
35

36
    class Meta:
×
37
        geo_field = "point"
×
38
        model = Project
×
39
        fields = (
×
40
            "pk",
41
            "slug",
42
            "point",
43
            "name",
44
            "url",
45
            "description",
46
            "information",
47
            "result",
48
            "organisation",
49
            "organisation_logo",
50
            "published_modules",
51
            "access",
52
            "image",
53
            "single_idea_collection_module",
54
            "single_poll_module",
55
            "participation_time_display",
56
            "module_running_progress",
57
            "has_contact_info",
58
            "contact_name",
59
            "contact_address_text",
60
            "contact_phone",
61
            "contact_email",
62
            "contact_url",
63
        )
64
        read_only_fields = ["point", "published_modules"]
×
65

66
    def get_url(self, project: Project) -> str:
×
67
        return project.get_absolute_url()
×
68

69
    def get_information(self, project):
×
70
        return project.information.strip()
×
71

72
    def get_result(self, project):
×
73
        return project.result.strip()
×
74

75
    def get_organisation(self, project):
×
76
        if project.organisation.title:
×
77
            return project.organisation.title
×
78
        else:
79
            return project.organisation.name
×
80

81
    def get_organisation_logo(self, project):
×
82
        if project.organisation.logo:
×
83
            return project.organisation.logo.url
×
84
        return None
×
85

86
    def get_access(self, project):
×
87
        return project.access.name
×
88

89
    def get_single_idea_collection_module(self, project):
×
90
        if (
×
91
            project.published_modules.count() == 1
92
            and project.published_modules.first().blueprint_type == "IC"
93
        ):
94
            return project.published_modules.first().pk
×
95
        return False
×
96

97
    def get_single_poll_module(self, project):
×
98
        if (
×
99
            project.published_modules.count() == 1
100
            and project.published_modules.first().blueprint_type == "PO"
101
        ):
102
            return project.published_modules.first().pk
×
103
        return False
×
104

105
    def get_participation_time_display(self, project):
×
106
        if project.running_modules:
×
107
            if project.module_running_days_left < 365:
×
108
                return _("%(time_left)s remaining") % {
×
109
                    "time_left": project.module_running_time_left
110
                }
111
            else:
112
                return _("more than 1 year remaining")
×
113
        elif project.future_modules:
×
114
            return _("Participation: from %(project_start)s") % {
×
115
                "project_start": get_date_display(
116
                    project.future_modules.first().module_start
117
                )
118
            }
119
        elif project.past_modules:
×
120
            return _("Participation ended. Read result.")
×
121
        return ""
×
122

123
    def get_has_contact_info(self, project):
×
124
        if (
×
125
            project.contact_name
126
            or project.contact_address_text
127
            or project.contact_phone
128
            or project.contact_email
129
            or project.contact_url
130
        ):
131
            return True
×
132
        return False
×
133

134

135
class AppPhaseSerializer(serializers.ModelSerializer):
×
136
    start_date = serializers.SerializerMethodField()
×
137
    end_date = serializers.SerializerMethodField()
×
138

139
    class Meta:
×
140
        model = Phase
×
141
        fields = ("name", "description", "type", "start_date", "end_date")
×
142

143
    def get_start_date(self, phase):
×
144
        return get_datetime_display(phase.start_date)
×
145

146
    def get_end_date(self, phase):
×
147
        return get_datetime_display(phase.end_date)
×
148

149

150
class AppModuleSerializer(serializers.ModelSerializer):
×
151
    active_phase = serializers.SerializerMethodField()
×
152
    future_phases = serializers.SerializerMethodField()
×
153
    past_phases = serializers.SerializerMethodField()
×
154
    labels = serializers.SerializerMethodField()
×
155
    categories = serializers.SerializerMethodField()
×
156
    has_idea_adding_permission = serializers.SerializerMethodField()
×
157

158
    class Meta:
×
159
        model = Module
×
160
        fields = (
×
161
            "pk",
162
            "active_phase",
163
            "future_phases",
164
            "past_phases",
165
            "labels",
166
            "categories",
167
            "has_idea_adding_permission",
168
        )
169

170
    def get_active_phase(self, module):
×
171
        if module.active_phase:
×
172
            serializer = AppPhaseSerializer(instance=module.active_phase)
×
173
            return serializer.data
×
174
        return None
×
175

176
    def get_future_phases(self, module):
×
177
        if module.future_phases:
×
178
            serializer = AppPhaseSerializer(instance=module.future_phases, many=True)
×
179
            return serializer.data
×
180
        return None
×
181

182
    def get_past_phases(self, module):
×
183
        if module.past_phases:
×
184
            serializer = AppPhaseSerializer(instance=module.past_phases, many=True)
×
185
            return serializer.data
×
186
        return None
×
187

188
    def get_labels(self, instance):
×
189
        labels = Label.objects.filter(module=instance)
×
190
        if labels:
×
191
            return [{"id": label.pk, "name": label.name} for label in labels]
×
192
        return False
×
193

194
    def get_categories(self, instance):
×
195
        categories = Category.objects.filter(module=instance)
×
196
        if categories:
×
197
            return [
×
198
                {"id": category.pk, "name": category.name} for category in categories
199
            ]
200
        return False
×
201

202
    def get_has_idea_adding_permission(self, instance):
×
203
        request = self.context.get("request")
×
204
        if request and hasattr(request, "user"):
×
205
            user = request.user
×
206
            return user.has_perm("a4_candy_ideas.add_idea", instance)
×
207
        return False
×
208

209

210
class ModerationProjectSerializer(serializers.ModelSerializer):
×
211
    title = serializers.SerializerMethodField()
×
212
    organisation = serializers.SerializerMethodField()
×
213
    tile_image = serializers.SerializerMethodField()
×
214
    tile_image_copyright = serializers.SerializerMethodField()
×
215
    status = serializers.SerializerMethodField()
×
216
    participation_active = serializers.SerializerMethodField()
×
217
    participation_string = serializers.SerializerMethodField()
×
218
    future_phase = serializers.SerializerMethodField()
×
219
    active_phase = serializers.SerializerMethodField()
×
220
    past_phase = serializers.SerializerMethodField()
×
221
    num_reported_unread_comments = serializers.SerializerMethodField()
×
222
    comment_count = serializers.SerializerMethodField()
×
223
    moderation_detail_url = serializers.SerializerMethodField()
×
224

225
    class Meta:
×
226
        model = Project
×
227
        fields = [
×
228
            "title",
229
            "organisation",
230
            "tile_image",
231
            "tile_image_alt_text",
232
            "tile_image_copyright",
233
            "status",
234
            "access",
235
            "participation_active",
236
            "participation_string",
237
            "future_phase",
238
            "active_phase",
239
            "past_phase",
240
            "num_reported_unread_comments",
241
            "comment_count",
242
            "moderation_detail_url",
243
        ]
244

245
    @lru_cache(maxsize=1)
×
246
    def _get_participation_status_project(self, instance):
×
247
        project_phases = instance.phases
×
248

249
        if project_phases.active_phases():
×
250
            return _("running"), True
×
251

252
        if project_phases.future_phases():
×
253
            try:
×
254
                return (
×
255
                    _("starts on {}").format(
256
                        project_phases.future_phases()
257
                        .first()
258
                        .start_date.strftime("%d.%m.%y")
259
                    ),
260
                    True,
261
                )
262
            except AttributeError:
×
263
                return (_("starts in the future"), True)
×
264
        else:
265
            return _("completed"), False
×
266

267
    def get_type(self, instance):
×
268
        return "project"
×
269

270
    def get_title(self, instance):
×
271
        return instance.name
×
272

273
    def get_organisation(self, instance):
×
274
        return instance.organisation.name
×
275

276
    def get_tile_image(self, instance):
×
277
        image_url = ""
×
278
        if instance.tile_image:
×
279
            image = get_thumbnailer(instance.tile_image)["project_thumbnail"]
×
280
            image_url = image.url
×
281
        elif instance.image:
×
282
            image = get_thumbnailer(instance.image)["project_thumbnail"]
×
283
            image_url = image.url
×
284
        return image_url
×
285

286
    def get_tile_image_alt_text(self, instance):
×
287
        if instance.tile_image:
×
288
            return instance.tile_image_alt_text
×
289
        elif instance.image:
×
290
            return instance.image_alt_text
×
291
        else:
292
            return None
×
293

294
    def get_tile_image_copyright(self, instance):
×
295
        if instance.tile_image:
×
296
            return instance.tile_image_copyright
×
297
        elif instance.image:
×
298
            return instance.image_copyright
×
299
        else:
300
            return None
×
301

302
    def get_status(self, instance):
×
303
        project_phases = instance.phases
×
304
        if project_phases.active_phases() or project_phases.future_phases():
×
305
            return 0
×
306
        return 1
×
307

308
    def get_participation_active(self, instance):
×
309
        (
×
310
            participation_string,
311
            participation_active,
312
        ) = self._get_participation_status_project(instance)
313
        return participation_active
×
314

315
    def get_participation_string(self, instance):
×
316
        (
×
317
            participation_string,
318
            participation_active,
319
        ) = self._get_participation_status_project(instance)
320
        return str(participation_string)
×
321

322
    def get_future_phase(self, instance):
×
323
        if instance.future_modules and instance.future_modules.first().module_start:
×
324
            return str(instance.future_modules.first().module_start)
×
325
        return False
×
326

327
    def get_active_phase(self, instance):
×
328
        if instance.active_phase_ends_next:
×
329
            progress = instance.module_running_progress
×
330
            time_left = instance.module_running_time_left
×
331
            end_date = str(instance.running_module_ends_next.module_end)
×
332
            return [progress, time_left, end_date]
×
333
        return False
×
334

335
    def get_past_phase(self, instance):
×
336
        if instance.past_modules and instance.past_modules.first().module_end:
×
337
            return str(instance.past_modules.first().module_end)
×
338
        return False
×
339

340
    def get_num_reported_unread_comments(self, instance):
×
341
        return helpers.get_num_reported_unread_comments(instance)
×
342

343
    def get_comment_count(self, instance):
×
344
        return helpers.get_num_comments_project(instance)
×
345

346
    def get_moderation_detail_url(self, instance):
×
347
        return reverse(
×
348
            "userdashboard-moderation-detail", kwargs={"slug": instance.slug}
349
        )
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