Skip to content

Commit 5ba438e

Browse files
committed
Standardize BaselineWealthGroupCharacteristicValue - see HEA-772
Standardize BaselineWealthGroupCharacteristicValue and CommunityWealthGroupCharacteristicValue by adding proxy models, factories, serializers, filtersets and viewsets. Also added livelihood_zone_baseline and wealth_group_category filters.
1 parent 97d57ae commit 5ba438e

8 files changed

Lines changed: 583 additions & 204 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Generated by Django 5.2.9 on 2026-01-13 17:58
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("baseline", "0022_alter_wealthgroup_options"),
10+
]
11+
12+
operations = [
13+
migrations.CreateModel(
14+
name="BaselineWealthGroupCharacteristicValue",
15+
fields=[],
16+
options={
17+
"verbose_name": "Baseline Wealth Group Characteristic Value",
18+
"verbose_name_plural": "Baseline Wealth Group Characteristic Values",
19+
"proxy": True,
20+
"indexes": [],
21+
"constraints": [],
22+
},
23+
bases=("baseline.wealthgroupcharacteristicvalue",),
24+
),
25+
migrations.CreateModel(
26+
name="CommunityWealthGroupCharacteristicValue",
27+
fields=[],
28+
options={
29+
"verbose_name": "Community Wealth Group Characteristic Value",
30+
"verbose_name_plural": "Community Wealth Group Characteristic Values",
31+
"proxy": True,
32+
"indexes": [],
33+
"constraints": [],
34+
},
35+
bases=("baseline.wealthgroupcharacteristicvalue",),
36+
),
37+
]

apps/baseline/models.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,56 @@ class Meta:
839839
]
840840

841841

842+
class BaselineWealthGroupCharacteristicValueManager(InheritanceManager):
843+
def get_queryset(self):
844+
return super().get_queryset().filter(wealth_group__community__isnull=True).select_subclasses()
845+
846+
847+
class BaselineWealthGroupCharacteristicValue(WealthGroupCharacteristicValue):
848+
"""
849+
An attribute of a Baseline Wealth Group such as the number of school-age children.
850+
"""
851+
852+
objects = BaselineWealthGroupCharacteristicValueManager()
853+
854+
def clean(self):
855+
if self.wealth_group.community:
856+
raise ValidationError(
857+
_("A Baseline Wealth Group Characteristic Value cannot be for a Community Wealth Group")
858+
)
859+
super().clean()
860+
861+
class Meta:
862+
verbose_name = _("Baseline Wealth Group Characteristic Value")
863+
verbose_name_plural = _("Baseline Wealth Group Characteristic Values")
864+
proxy = True
865+
866+
867+
class CommunityWealthGroupCharacteristicValueManager(InheritanceManager):
868+
def get_queryset(self):
869+
return super().get_queryset().exclude(wealth_group__community__isnull=True).select_subclasses()
870+
871+
872+
class CommunityWealthGroupCharacteristicValue(WealthGroupCharacteristicValue):
873+
"""
874+
An attribute of a Community Wealth Group such as the number of school-age children.
875+
"""
876+
877+
objects = CommunityWealthGroupCharacteristicValueManager()
878+
879+
def clean(self):
880+
if not self.wealth_group.community:
881+
raise ValidationError(
882+
_("A Community Wealth Group Characteristic Value must be for a Community Wealth Group")
883+
)
884+
super().clean()
885+
886+
class Meta:
887+
verbose_name = _("Community Wealth Group Characteristic Value")
888+
verbose_name_plural = _("Community Wealth Group Characteristic Values")
889+
proxy = True
890+
891+
842892
class LivelihoodStrategyManager(common_models.IdentifierManager):
843893
def get_by_natural_key(
844894
self,

apps/baseline/serializers.py

Lines changed: 116 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
from .models import (
1010
BaselineLivelihoodActivity,
1111
BaselineWealthGroup,
12+
BaselineWealthGroupCharacteristicValue,
1213
ButterProduction,
1314
Community,
1415
CommunityCropProduction,
1516
CommunityLivestock,
1617
CommunityWealthGroup,
18+
CommunityWealthGroupCharacteristicValue,
1719
CopingStrategy,
1820
CropProduction,
1921
Event,
@@ -357,6 +359,12 @@ class Meta:
357359
"wealth_characteristic",
358360
"wealth_characteristic_name",
359361
"wealth_characteristic_description",
362+
"variable_type",
363+
"characteristic_group",
364+
"product",
365+
"product_common_name",
366+
"unit_of_measure",
367+
"unit_of_measure_name",
360368
"value",
361369
"min_value",
362370
"max_value",
@@ -387,6 +395,7 @@ def get_wealth_group_label(self, obj):
387395
wealth_characteristic_description = serializers.CharField(
388396
source="wealth_characteristic.description", read_only=True
389397
)
398+
variable_type = serializers.CharField(source="wealth_characteristic.variable_type", read_only=True)
390399
livelihood_zone_baseline = serializers.IntegerField(
391400
source="wealth_group.community.livelihood_zone_baseline.pk", read_only=True
392401
)
@@ -410,52 +419,128 @@ def get_wealth_group_label(self, obj):
410419
source_organization_name = serializers.CharField(
411420
source="wealth_group.community.livelihood_zone_baseline.source_organization.name", read_only=True
412421
)
422+
characteristic_group = serializers.SerializerMethodField()
413423

424+
def get_characteristic_group(self, obj):
425+
"""
426+
Override the default characteristic_group for Livestock and Poultry.
414427
415-
class BaselineWealthCharacteristicsValueSerializer(serializers.ModelSerializer):
428+
This allows us to use the same wealth characteristic, e.g. 'Number owned' for both Livestock and Poultry,
429+
as well as other wealth characteristics.
430+
"""
431+
if obj.product and obj.product.cpc.startswith("L0215"):
432+
return "Poultry"
433+
if obj.product and obj.product.cpc.startswith("L02"):
434+
return "Livestock"
435+
return obj.wealth_characteristic.characteristic_group
436+
437+
product_common_name = serializers.CharField(source="product.common_name", read_only=True, allow_null=True)
438+
unit_of_measure_name = serializers.CharField(source="unit_of_measure.description", read_only=True, allow_null=True)
439+
440+
441+
class BaselineWealthGroupCharacteristicValueSerializer(serializers.ModelSerializer):
416442

417443
class Meta:
418-
model = WealthGroupCharacteristicValue
444+
model = BaselineWealthGroupCharacteristicValue
419445
fields = [
420446
"id",
421447
"wealth_group",
422-
"wealth_group_category",
423-
"wealth_group_category_name",
448+
"wealth_group_label",
449+
"source_organization",
450+
"source_organization_name",
424451
"livelihood_zone_baseline",
425452
"livelihood_zone_baseline_label",
453+
"livelihood_zone",
454+
"livelihood_zone_name",
455+
"livelihood_zone_country_code",
456+
"livelihood_zone_country_name",
457+
"wealth_group_category",
458+
"wealth_group_category_name",
459+
"wealth_group_category_description",
426460
"wealth_characteristic",
427461
"wealth_characteristic_name",
462+
"wealth_characteristic_description",
463+
"variable_type",
428464
"characteristic_group",
429465
"product",
430-
"product_name",
466+
"product_common_name",
431467
"unit_of_measure",
432468
"unit_of_measure_name",
433469
"value",
434470
"min_value",
435471
"max_value",
436472
]
437473

474+
livelihood_zone_baseline_label = serializers.SerializerMethodField()
475+
476+
def get_livelihood_zone_baseline_label(self, obj):
477+
return (
478+
str(obj.wealth_group.livelihood_zone_baseline)
479+
if obj.wealth_group.community
480+
else str(obj.wealth_group.livelihood_zone_baseline)
481+
)
482+
483+
wealth_group_label = serializers.SerializerMethodField()
484+
485+
def get_wealth_group_label(self, obj):
486+
return str(obj.wealth_group)
487+
438488
wealth_group_category = serializers.CharField(source="wealth_group.wealth_group_category.pk", read_only=True)
439489
wealth_group_category_name = serializers.CharField(
440490
source="wealth_group.wealth_group_category.name", read_only=True
441491
)
492+
wealth_group_category_description = serializers.CharField(
493+
source="wealth_group.wealth_group_category.description", read_only=True
494+
)
495+
wealth_characteristic_name = serializers.CharField(source="wealth_characteristic.name", read_only=True)
496+
wealth_characteristic_description = serializers.CharField(
497+
source="wealth_characteristic.description", read_only=True
498+
)
499+
variable_type = serializers.CharField(source="wealth_characteristic.variable_type", read_only=True)
442500
livelihood_zone_baseline = serializers.IntegerField(
443-
source="wealth_group.livelihood_zone_baseline.pk", read_only=True, allow_null=True
501+
source="wealth_group.livelihood_zone_baseline.pk", read_only=True
444502
)
445-
livelihood_zone_baseline_label = serializers.SerializerMethodField()
503+
livelihood_zone_name = serializers.CharField(
504+
source="wealth_group.livelihood_zone_baseline.livelihood_zone.name", read_only=True
505+
)
506+
livelihood_zone = serializers.CharField(
507+
source="wealth_group.livelihood_zone_baseline.livelihood_zone.pk", read_only=True
508+
)
509+
livelihood_zone_country_code = serializers.CharField(
510+
source="wealth_group.livelihood_zone_baseline.livelihood_zone.country.pk", read_only=True
511+
)
512+
livelihood_zone_country_name = serializers.CharField(
513+
source="wealth_group.livelihood_zone_baseline.livelihood_zone.country.name", read_only=True
514+
)
515+
source_organization = serializers.IntegerField(
516+
source="wealth_group.livelihood_zone_baseline.source_organization.pk", read_only=True
517+
)
518+
source_organization_name = serializers.CharField(
519+
source="wealth_group.livelihood_zone_baseline.source_organization.name", read_only=True
520+
)
521+
characteristic_group = serializers.SerializerMethodField()
446522

447-
def get_livelihood_zone_baseline_label(self, obj):
448-
"""Get label for livelihood zone baseline."""
449-
if hasattr(obj.wealth_group, "livelihood_zone_baseline") and obj.wealth_group.livelihood_zone_baseline:
450-
return str(obj.wealth_group.livelihood_zone_baseline)
451-
return None
523+
def get_characteristic_group(self, obj):
524+
"""
525+
Override the default characteristic_group for Livestock and Poultry.
452526
453-
wealth_characteristic_name = serializers.CharField(source="wealth_characteristic.name", read_only=True)
454-
characteristic_group = serializers.CharField(
455-
source="wealth_characteristic.characteristic_group", read_only=True, allow_null=True
456-
)
457-
product_name = serializers.CharField(source="product.name", read_only=True, allow_null=True)
458-
unit_of_measure_name = serializers.CharField(source="unit_of_measure.name", read_only=True, allow_null=True)
527+
This allows us to use the same wealth characteristic, e.g. 'Number owned' for both Livestock and Poultry,
528+
as well as other wealth characteristics.
529+
"""
530+
if obj.product and obj.product.cpc.startswith("L0215"):
531+
return "Poultry"
532+
if obj.product and obj.product.cpc.startswith("L02"):
533+
return "Livestock"
534+
return obj.wealth_characteristic.characteristic_group
535+
536+
product_common_name = serializers.CharField(source="product.common_name", read_only=True, allow_null=True)
537+
unit_of_measure_name = serializers.CharField(source="unit_of_measure.description", read_only=True, allow_null=True)
538+
539+
540+
class CommunityWealthGroupCharacteristicValueSerializer(WealthGroupCharacteristicValueSerializer):
541+
class Meta:
542+
model = CommunityWealthGroupCharacteristicValue
543+
fields = WealthGroupCharacteristicValueSerializer.Meta.fields
459544

460545

461546
class LivelihoodStrategySerializer(serializers.ModelSerializer):
@@ -504,7 +589,7 @@ class Meta:
504589
source_organization_name = serializers.CharField(
505590
source="livelihood_zone_baseline.source_organization.name", read_only=True
506591
)
507-
unit_of_measure_name = serializers.CharField(source="unit_of_measure.name", read_only=True)
592+
unit_of_measure_name = serializers.CharField(source="unit_of_measure.description", read_only=True)
508593
unit_of_measure_description = serializers.CharField(source="unit_of_measure.description", read_only=True)
509594
product_common_name = serializers.CharField(source="product.common_name", read_only=True)
510595
product_description = serializers.CharField(source="product.description", read_only=True)
@@ -613,7 +698,9 @@ def get_livelihood_zone_baseline_label(self, obj):
613698
community = serializers.IntegerField(source="wealth_group.community.pk", read_only=True)
614699
community_name = serializers.CharField(source="wealth_group.community.name", read_only=True)
615700
unit_of_measure = serializers.CharField(source="livelihood_strategy.unit_of_measure.pk", read_only=True)
616-
unit_of_measure_name = serializers.CharField(source="livelihood_strategy.unit_of_measure.name", read_only=True)
701+
unit_of_measure_name = serializers.CharField(
702+
source="livelihood_strategy.unit_of_measure.description", read_only=True
703+
)
617704
unit_of_measure_description = serializers.CharField(
618705
source="livelihood_strategy.unit_of_measure.description", read_only=True
619706
)
@@ -959,9 +1046,9 @@ class Meta:
9591046
crop_common_name = serializers.CharField(source="crop.common_name", read_only=True)
9601047
crop_description = serializers.CharField(source="crop.description", read_only=True)
9611048
community_name = serializers.CharField(source="community.name", read_only=True)
962-
crop_unit_of_measure_name = serializers.CharField(source="crop_unit_of_measure.name", read_only=True)
1049+
crop_unit_of_measure_name = serializers.CharField(source="crop_unit_of_measure.description", read_only=True)
9631050
crop_unit_of_measure_description = serializers.CharField(source="crop_unit_of_measure.description", read_only=True)
964-
land_unit_of_measure_name = serializers.CharField(source="land_unit_of_measure.name", read_only=True)
1051+
land_unit_of_measure_name = serializers.CharField(source="land_unit_of_measure.description", read_only=True)
9651052
land_unit_of_measure_description = serializers.CharField(source="land_unit_of_measure.description", read_only=True)
9661053
season_name = serializers.CharField(source="season.name", read_only=True)
9671054
season_description = serializers.CharField(source="season.description", read_only=True)
@@ -1096,7 +1183,7 @@ class Meta:
10961183
product = serializers.CharField(source="product.pk", read_only=True)
10971184
product_common_name = serializers.CharField(source="product.common_name", read_only=True)
10981185
product_description = serializers.CharField(source="product.description", read_only=True)
1099-
unit_of_measure_name = serializers.CharField(source="unit_of_measure.name", read_only=True)
1186+
unit_of_measure_name = serializers.CharField(source="unit_of_measure.description", read_only=True)
11001187
unit_of_measure_description = serializers.CharField(source="unit_of_measure.description", read_only=True)
11011188
livelihood_zone_baseline = serializers.IntegerField(source="community.livelihood_zone_baseline.pk", read_only=True)
11021189
livelihood_zone_name = serializers.CharField(
@@ -1368,7 +1455,9 @@ def get_livelihood_zone_baseline_label(self, obj):
13681455
community = serializers.IntegerField(source="wealth_group.community.pk", read_only=True)
13691456
community_name = serializers.CharField(source="wealth_group.community.name", read_only=True)
13701457
unit_of_measure = serializers.CharField(source="livelihood_strategy.unit_of_measure.pk", read_only=True)
1371-
unit_of_measure_name = serializers.CharField(source="livelihood_strategy.unit_of_measure.name", read_only=True)
1458+
unit_of_measure_name = serializers.CharField(
1459+
source="livelihood_strategy.unit_of_measure.description", read_only=True
1460+
)
13721461
unit_of_measure_description = serializers.CharField(
13731462
source="livelihood_strategy.unit_of_measure.description", read_only=True
13741463
)
@@ -1484,7 +1573,9 @@ def get_livelihood_zone_baseline_label(self, obj):
14841573
additional_identifier = serializers.CharField(source="livelihood_strategy.additional_identifier", read_only=True)
14851574
currency = serializers.CharField(source="livelihood_strategy.currency.pk", read_only=True)
14861575
unit_of_measure = serializers.CharField(source="livelihood_strategy.unit_of_measure.pk", read_only=True)
1487-
unit_of_measure_name = serializers.CharField(source="livelihood_strategy.unit_of_measure.name", read_only=True)
1576+
unit_of_measure_name = serializers.CharField(
1577+
source="livelihood_strategy.unit_of_measure.description", read_only=True
1578+
)
14881579
unit_of_measure_description = serializers.CharField(
14891580
source="livelihood_strategy.unit_of_measure.description", read_only=True
14901581
)

apps/baseline/tests/factories.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
from baseline.models import (
99
BaselineLivelihoodActivity,
1010
BaselineWealthGroup,
11+
BaselineWealthGroupCharacteristicValue,
1112
ButterProduction,
1213
Community,
1314
CommunityCropProduction,
1415
CommunityLivestock,
1516
CommunityWealthGroup,
17+
CommunityWealthGroupCharacteristicValue,
1618
CopingStrategy,
1719
CropProduction,
1820
Event,
@@ -218,6 +220,34 @@ def unit_of_measure(self):
218220
return None
219221

220222

223+
class BaselineWealthGroupCharacteristicValueFactory(WealthGroupCharacteristicValueFactory):
224+
class Meta:
225+
model = BaselineWealthGroupCharacteristicValue
226+
django_get_or_create = [
227+
"wealth_group",
228+
"wealth_characteristic",
229+
]
230+
231+
wealth_group = factory.SubFactory(WealthGroupFactory, community=None)
232+
233+
234+
class CommunityWealthGroupCharacteristicValueFactory(WealthGroupCharacteristicValueFactory):
235+
class Meta:
236+
model = CommunityWealthGroupCharacteristicValue
237+
django_get_or_create = [
238+
"wealth_group",
239+
"wealth_characteristic",
240+
]
241+
242+
wealth_group = factory.SubFactory(
243+
WealthGroupFactory,
244+
community=factory.SubFactory(
245+
CommunityFactory,
246+
livelihood_zone_baseline=factory.SelfAttribute("..livelihood_zone_baseline"),
247+
),
248+
)
249+
250+
221251
class LivelihoodStrategyFactory(factory.django.DjangoModelFactory):
222252
class Meta:
223253
model = LivelihoodStrategy

0 commit comments

Comments
 (0)