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

akvo / iwsims-demo / #93

30 Apr 2025 07:36AM UTC coverage: 86.373% (+0.07%) from 86.3%
#93

Pull #29

coveralls-python

ifirmawan
[#28] Remove any county, sub-County terms in the backend
Pull Request #29: Feature/28 eng 1232 data submission by admin super admin

2591 of 3118 branches covered (83.1%)

Branch coverage included in aggregate %.

5915 of 6730 relevant lines covered (87.89%)

0.88 hits per line

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

96.87
backend/api/v1/v1_forms/serializers.py
1
import numpy as np
1✔
2
from collections import OrderedDict
1✔
3

4
from drf_spectacular.types import OpenApiTypes
1✔
5
from drf_spectacular.utils import extend_schema_field, inline_serializer
1✔
6
from rest_framework import serializers
1✔
7

8
from api.v1.v1_forms.constants import QuestionTypes, AttributeTypes
1✔
9
from api.v1.v1_forms.models import (
1✔
10
    Forms,
11
    QuestionGroup,
12
    Questions,
13
    QuestionOptions,
14
    QuestionAttribute,
15
)
16
from api.v1.v1_profile.constants import UserRoleTypes
1✔
17
from api.v1.v1_profile.models import Administration, Entity
1✔
18
from api.v1.v1_users.models import SystemUser
1✔
19
from iwsims.settings import FORM_GEO_VALUE
1✔
20
from utils.custom_serializer_fields import (
1✔
21
    CustomChoiceField,
22
    CustomPrimaryKeyRelatedField,
23
    CustomMultipleChoiceField,
24
)
25
from utils.default_serializers import CommonDataSerializer, GeoFormatSerializer
1✔
26

27

28
class ListOptionSerializer(serializers.ModelSerializer):
1✔
29
    def to_representation(self, instance):
1✔
30
        result = super(ListOptionSerializer, self).to_representation(instance)
1✔
31
        return OrderedDict(
1✔
32
            [(key, result[key]) for key in result if result[key] is not None]
33
        )
34

35
    class Meta:
1✔
36
        model = QuestionOptions
1✔
37
        fields = ["id", "value", "label", "order", "color"]
1✔
38

39

40
class ListQuestionSerializer(serializers.ModelSerializer):
1✔
41
    option = serializers.SerializerMethodField()
1✔
42
    type = serializers.SerializerMethodField()
1✔
43
    center = serializers.SerializerMethodField()
1✔
44
    api = serializers.SerializerMethodField()
1✔
45
    rule = serializers.SerializerMethodField()
1✔
46
    extra = serializers.SerializerMethodField()
1✔
47
    source = serializers.SerializerMethodField()
1✔
48
    tooltip = serializers.SerializerMethodField()
1✔
49
    fn = serializers.SerializerMethodField()
1✔
50
    pre = serializers.SerializerMethodField()
1✔
51
    displayOnly = serializers.BooleanField(source="display_only")
1✔
52

53
    @extend_schema_field(ListOptionSerializer(many=True))
1✔
54
    def get_option(self, instance: Questions):
1✔
55
        if instance.type in [
1✔
56
            QuestionTypes.option,
57
            QuestionTypes.multiple_option,
58
        ]:
59
            return ListOptionSerializer(
1✔
60
                instance=instance.options.all(), many=True
61
            ).data
62
        return None
1✔
63

64
    @extend_schema_field(OpenApiTypes.STR)
1✔
65
    def get_type(self, instance: Questions):
1✔
66
        if instance.type == QuestionTypes.administration:
1✔
67
            return QuestionTypes.FieldStr.get(QuestionTypes.cascade).lower()
1✔
68
        return QuestionTypes.FieldStr.get(instance.type).lower()
1✔
69

70
    @extend_schema_field(
1✔
71
        inline_serializer(
72
            "CascadeApiFormat",
73
            fields={
74
                "endpoint": serializers.CharField(),
75
                "list": serializers.CharField(),
76
                "initial": serializers.CharField(),
77
                "query_params": serializers.CharField(),
78
            },
79
        )
80
    )
81
    def get_api(self, instance: Questions):
1✔
82
        if instance.type == QuestionTypes.administration:
1✔
83
            user = self.context.get("user")
1✔
84
            administration = user.user_access.administration
1✔
85
            # max depth for cascade question in national form
86
            max_level = False
1✔
87
            extra_objects = {}
1✔
88
            if max_level:
1!
89
                extra_objects = {
×
90
                    "query_params": "?max_level=1",
91
                }
92
            if user.user_access.role is not UserRoleTypes.super_admin:
1✔
93
                if max_level:
1!
94
                    extra_objects = {
×
95
                        "query_params": "&max_level=1",
96
                    }
97
                return {
1✔
98
                    "endpoint": "/api/v1/administration",
99
                    "list": "children",
100
                    "initial": "{0}?filter={1}".format(
101
                        administration.parent_id, administration.id
102
                    ),
103
                    **extra_objects,
104
                }
105
            return {
1✔
106
                "endpoint": "/api/v1/administration",
107
                "list": "children",
108
                "initial": administration.id,
109
                **extra_objects,
110
            }
111
        if instance.type == QuestionTypes.cascade:
1✔
112
            return instance.api
1✔
113
        return None
1✔
114

115
    @extend_schema_field(GeoFormatSerializer)
1✔
116
    def get_center(self, instance: Questions):
1✔
117
        if instance.type == QuestionTypes.geo:
1✔
118
            return FORM_GEO_VALUE
1✔
119
        return None
1✔
120

121
    @extend_schema_field(
1✔
122
        inline_serializer(
123
            "QuestionRuleFormat",
124
            fields={
125
                "min": serializers.FloatField(),
126
                "max": serializers.FloatField(),
127
                "allowDecimal": serializers.BooleanField(),
128
            },
129
        )
130
    )
131
    def get_rule(self, instance: Questions):
1✔
132
        return instance.rule
1✔
133

134
    @extend_schema_field(
1✔
135
        inline_serializer(
136
            "QuestionExtraFormat",
137
            fields={"allowOther": serializers.BooleanField()},
138
        )
139
    )
140
    def get_extra(self, instance: Questions):
1✔
141
        return instance.extra
1✔
142

143
    @extend_schema_field(
1✔
144
        inline_serializer(
145
            "QuestionTooltipFormat", fields={"text": serializers.CharField()}
146
        )
147
    )
148
    def get_tooltip(self, instance: Questions):
1✔
149
        return instance.tooltip
1✔
150

151
    @extend_schema_field(
1✔
152
        inline_serializer(
153
            "QuestionFnFormat",
154
            fields={
155
                "fnColor": serializers.JSONField(),
156
                "fnString": serializers.CharField(),
157
                "multiline": serializers.BooleanField(),
158
            },
159
        )
160
    )
161
    def get_fn(self, instance: Questions):
1✔
162
        return instance.fn
1✔
163

164
    @extend_schema_field(
1✔
165
        inline_serializer(
166
            "QuestionPreFormat",
167
            fields={
168
                "answer": serializers.CharField(),
169
                "fill": serializers.JSONField(),
170
            },
171
        )
172
    )
173
    def get_pre(self, instance: Questions):
1✔
174
        return instance.pre
1✔
175

176
    def to_representation(self, instance):
1✔
177
        result = super(ListQuestionSerializer, self).to_representation(
1✔
178
            instance
179
        )
180
        return OrderedDict(
1✔
181
            [(key, result[key]) for key in result if result[key] is not None]
182
        )
183

184
    @extend_schema_field(
1✔
185
        inline_serializer(
186
            "QuestionSourceFormat",
187
            fields={
188
                "file": serializers.CharField(),
189
                "parent": serializers.ListField(
190
                    child=serializers.IntegerField()
191
                ),
192
                "max_level": serializers.IntegerField(),
193
            },
194
        )
195
    )
196
    def get_source(self, instance: Questions):
1✔
197
        user = self.context.get("user")
1✔
198
        assignment = self.context.get("mobile_assignment")
1✔
199
        max_level = False
1✔
200
        extra_objects = {}
1✔
201
        if instance.type == QuestionTypes.cascade:
1✔
202
            if instance.extra:
1!
203
                cascade_type = instance.extra.get("type")
1✔
204
                cascade_name = instance.extra.get("name")
1✔
205
                if cascade_type == "entity":
1!
206
                    entity_type = Entity.objects.filter(
1✔
207
                        name=cascade_name
208
                    ).first()
209
                    entity_id = entity_type.id if entity_type else None
1✔
210
                    return {
1✔
211
                        "file": "entity_data.sqlite",
212
                        "cascade_type": entity_id,
213
                        "cascade_parent": "administrator.sqlite",
214
                    }
215
            return {"file": "organisation.sqlite", "parent_id": [0]}
×
216
        if instance.type == QuestionTypes.administration:
1✔
217
            if max_level:
1!
218
                extra_objects = {
×
219
                    "max_level": 1,
220
                }
221
            return {
1✔
222
                "file": "administrator.sqlite",
223
                "parent_id": [a.id for a in assignment.administrations.all()]
224
                if assignment
225
                else [user.user_access.administration.id],
226
                **extra_objects,
227
            }
228
        return None
1✔
229

230
    class Meta:
1✔
231
        model = Questions
1✔
232
        fields = [
1✔
233
            "id",
234
            "order",
235
            "name",
236
            "label",
237
            "short_label",
238
            "type",
239
            "required",
240
            "dependency",
241
            "option",
242
            "center",
243
            "api",
244
            "meta",
245
            "meta_uuid",
246
            "rule",
247
            "extra",
248
            "source",
249
            "tooltip",
250
            "fn",
251
            "pre",
252
            "hidden",
253
            "displayOnly",
254
            "default_value",
255
            "disabled",
256
        ]
257

258

259
# TODO: confirm Order in QuestionGroup model
260
class ListQuestionGroupSerializer(serializers.ModelSerializer):
1✔
261
    question = serializers.SerializerMethodField()
1✔
262

263
    @extend_schema_field(ListQuestionSerializer(many=True))
1✔
264
    def get_question(self, instance: QuestionGroup):
1✔
265
        return ListQuestionSerializer(
1✔
266
            instance=instance.question_group_question.all().order_by("order"),
267
            context=self.context,
268
            many=True,
269
        ).data
270

271
    class Meta:
1✔
272
        model = QuestionGroup
1✔
273
        fields = ["name", "label", "question"]
1✔
274

275

276
class ListAdministrationCascadeSerializer(serializers.ModelSerializer):
1✔
277
    value = serializers.ReadOnlyField(source="id")
1✔
278
    label = serializers.ReadOnlyField(source="name")
1✔
279
    children = serializers.SerializerMethodField()
1✔
280

281
    @extend_schema_field(
1✔
282
        inline_serializer(
283
            "children",
284
            fields={
285
                "value": serializers.IntegerField(),
286
                "label": serializers.CharField(),
287
            },
288
            many=True,
289
        )
290
    )
291
    def get_children(self, instance: Administration):
1✔
292
        return ListAdministrationCascadeSerializer(
×
293
            instance=instance.parent_administration.all(), many=True
294
        ).data
295

296
    class Meta:
1✔
297
        model = Administration
1✔
298
        fields = ["value", "label", "children"]
1✔
299

300

301
class WebFormDetailSerializer(serializers.ModelSerializer):
1✔
302
    question_group = serializers.SerializerMethodField()
1✔
303
    cascades = serializers.SerializerMethodField()
1✔
304

305
    @extend_schema_field(ListQuestionGroupSerializer(many=True))
1✔
306
    def get_question_group(self, instance: Forms):
1✔
307
        return ListQuestionGroupSerializer(
1✔
308
            instance=instance.form_question_group.all().order_by("order"),
309
            many=True,
310
            context=self.context,
311
        ).data
312

313
    @extend_schema_field(serializers.ListField())
1✔
314
    def get_cascades(self, instance: Forms):
1✔
315
        cascade_questions = Questions.objects.filter(
1✔
316
            type__in=[QuestionTypes.cascade, QuestionTypes.administration],
317
            form=instance,
318
        ).all()
319
        source = []
1✔
320
        for cascade_question in cascade_questions:
1✔
321
            if cascade_question.type == QuestionTypes.administration:
1✔
322
                source.append("/sqlite/administrator.sqlite")
1✔
323
            if (
1✔
324
                cascade_question.extra
325
                and cascade_question.extra.get("type") == "entity"
326
            ):
327
                source.append("/sqlite/entity_data.sqlite")
1✔
328
            else:
329
                source.append("/sqlite/organisation.sqlite")
1✔
330
        return np.unique(source)
1✔
331

332
    class Meta:
1✔
333
        model = Forms
1✔
334
        fields = [
1✔
335
            "id",
336
            "name",
337
            "version",
338
            "cascades",
339
            "approval_instructions",
340
            "submission_types",
341
            "question_group",
342
        ]
343

344

345
class ListFormSerializer(serializers.ModelSerializer):
1✔
346
    class Meta:
1✔
347
        model = Forms
1✔
348
        fields = [
1✔
349
            "id",
350
            "name",
351
            "version",
352
            "submission_types",
353
        ]
354

355

356
class FormDataListQuestionSerializer(serializers.ModelSerializer):
1✔
357
    option = serializers.SerializerMethodField()
1✔
358
    type = serializers.SerializerMethodField()
1✔
359
    attributes = serializers.SerializerMethodField()
1✔
360

361
    @extend_schema_field(ListOptionSerializer(many=True))
1✔
362
    def get_option(self, instance: Questions):
1✔
363
        if instance.type in [
1✔
364
            QuestionTypes.geo,
365
            QuestionTypes.option,
366
            QuestionTypes.multiple_option,
367
        ]:
368
            return ListOptionSerializer(
1✔
369
                instance=instance.options.all(), many=True
370
            ).data
371
        return None
1✔
372

373
    @extend_schema_field(
1✔
374
        CustomChoiceField(
375
            choices=[
376
                QuestionTypes.FieldStr[d].lower()
377
                for d in QuestionTypes.FieldStr
378
            ]
379
        )
380
    )
381
    def get_type(self, instance: Questions):
1✔
382
        if instance.type == QuestionTypes.administration:
1✔
383
            return QuestionTypes.FieldStr.get(QuestionTypes.cascade).lower()
1✔
384
        return QuestionTypes.FieldStr.get(instance.type).lower()
1✔
385

386
    @extend_schema_field(
1✔
387
        CustomMultipleChoiceField(
388
            choices=[
389
                AttributeTypes.FieldStr[a] for a in AttributeTypes.FieldStr
390
            ]
391
        )
392
    )
393
    def get_attributes(self, instance: Questions):
1✔
394
        attribute_ids = (
1✔
395
            QuestionAttribute.objects.filter(question=instance)
396
            .values_list("attribute", flat=True)
397
            .distinct()
398
        )
399
        if attribute_ids:
1✔
400
            return [AttributeTypes.FieldStr.get(a) for a in attribute_ids]
1✔
401
        return []
1✔
402

403
    @extend_schema_field(GeoFormatSerializer)
1✔
404
    def to_representation(self, instance):
1✔
405
        result = super(FormDataListQuestionSerializer, self).to_representation(
1✔
406
            instance
407
        )
408
        return OrderedDict(
1✔
409
            [(key, result[key]) for key in result if result[key] is not None]
410
        )
411

412
    class Meta:
1✔
413
        model = Questions
1✔
414
        fields = [
1✔
415
            "id",
416
            "form",
417
            "question_group",
418
            "name",
419
            "label",
420
            "short_label",
421
            "order",
422
            "meta",
423
            "api",
424
            "type",
425
            "required",
426
            "rule",
427
            "option",
428
            "dependency",
429
            "display_only",
430
            "default_value",
431
            "attributes",
432
            "disabled",
433
        ]
434

435

436
class FormDataQuestionGroupSerializer(serializers.ModelSerializer):
1✔
437
    question = serializers.SerializerMethodField()
1✔
438

439
    @extend_schema_field(FormDataListQuestionSerializer(many=True))
1✔
440
    def get_question(self, instance: QuestionGroup):
1✔
441
        return FormDataListQuestionSerializer(
1✔
442
            instance=instance.question_group_question.all().order_by("order"),
443
            many=True,
444
        ).data
445

446
    class Meta:
1✔
447
        model = QuestionGroup
1✔
448
        fields = ["id", "label", "name", "question"]
1✔
449

450

451
class FormDataSerializer(serializers.ModelSerializer):
1✔
452
    question_group = serializers.SerializerMethodField()
1✔
453

454
    @extend_schema_field(FormDataQuestionGroupSerializer(many=True))
1✔
455
    def get_question_group(self, instance: Forms):
1✔
456
        return FormDataQuestionGroupSerializer(
1✔
457
            instance=instance.form_question_group.all().order_by("order"),
458
            many=True,
459
        ).data
460

461
    class Meta:
1✔
462
        model = Forms
1✔
463
        fields = [
1✔
464
            "id",
465
            "name",
466
            "question_group",
467
            "approval_instructions",
468
            "submission_types",
469
        ]
470

471

472
class FormApproverRequestSerializer(serializers.Serializer):
1✔
473
    administration_id = CustomPrimaryKeyRelatedField(
1✔
474
        queryset=Administration.objects.none()
475
    )
476
    form_id = CustomPrimaryKeyRelatedField(queryset=Forms.objects.none())
1✔
477

478
    def __init__(self, **kwargs):
1✔
479
        super().__init__(**kwargs)
1✔
480
        self.fields.get("form_id").queryset = Forms.objects.all()
1✔
481
        self.fields.get(
1✔
482
            "administration_id"
483
        ).queryset = Administration.objects.all()
484

485

486
class FormApproverUserSerializer(serializers.ModelSerializer):
1✔
487
    class Meta:
1✔
488
        model = SystemUser
1✔
489
        fields = ["id", "first_name", "last_name", "email"]
1✔
490

491

492
class FormApproverResponseSerializer(serializers.ModelSerializer):
1✔
493
    user = serializers.SerializerMethodField()
1✔
494
    administration = serializers.SerializerMethodField()
1✔
495

496
    @extend_schema_field(FormApproverUserSerializer(many=True))
1✔
497
    def get_user(self, instance: Administration):
1✔
498
        assignment = instance.administration_data_approval.filter(
1✔
499
            form=self.context.get("form")
500
        ).first()
501
        if assignment:
1✔
502
            return FormApproverUserSerializer(instance=assignment.user).data
1✔
503
        return None
1✔
504

505
    @extend_schema_field(CommonDataSerializer)
1✔
506
    def get_administration(self, instance: Administration):
1✔
507
        return {"id": instance.id, "name": instance.name}
1✔
508

509
    class Meta:
1✔
510
        model = Administration
1✔
511
        fields = ["user", "administration"]
1✔
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