Skip to content

Commit b6d2c2d

Browse files
committed
Initial working draft of key parameters model, serializer, viewset, admin, factory, tests and ingestion job, see #HEA-459
1 parent 1296645 commit b6d2c2d

File tree

10 files changed

+441
-7
lines changed

10 files changed

+441
-7
lines changed

apps/baseline/admin.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
FoodPurchase,
2929
Hazard,
3030
Hunting,
31+
KeyParameter,
3132
LivelihoodActivity,
3233
LivelihoodStrategy,
3334
LivelihoodZone,
@@ -204,7 +205,6 @@ def bss_uploaded_date_time(self, instance):
204205
return ""
205206

206207
def get_fieldsets(self, request, obj=None):
207-
208208
fieldsets = super().get_fieldsets(request, obj=obj)
209209
if obj and obj.geography:
210210
# Check if 'geography' field has a value
@@ -1013,7 +1013,6 @@ class EventAdmin(admin.ModelAdmin):
10131013

10141014

10151015
class ExpandabilityFactorAdmin(admin.ModelAdmin):
1016-
10171016
fields = (
10181017
"livelihood_strategy",
10191018
"wealth_group",
@@ -1046,7 +1045,6 @@ class ExpandabilityFactorAdmin(admin.ModelAdmin):
10461045

10471046

10481047
class CopingStrategyAdmin(admin.ModelAdmin):
1049-
10501048
fields = (
10511049
"community",
10521050
"leaders",
@@ -1075,6 +1073,20 @@ class CopingStrategyAdmin(admin.ModelAdmin):
10751073
)
10761074

10771075

1076+
class KeyParameterAdmin(admin.ModelAdmin):
1077+
list_display = (
1078+
"livelihood_zone_baseline",
1079+
"strategy_type",
1080+
"key_parameter_type",
1081+
"name",
1082+
"description",
1083+
)
1084+
search_fields = [
1085+
*translation_fields("name"),
1086+
*translation_fields("description"),
1087+
]
1088+
1089+
10781090
admin.site.register(SourceOrganization, SourceOrganizationAdmin)
10791091
admin.site.register(LivelihoodZone, LivelihoodZoneAdmin)
10801092
admin.site.register(LivelihoodZoneBaseline, LivelihoodZoneBaselineAdmin)
@@ -1098,3 +1110,5 @@ class CopingStrategyAdmin(admin.ModelAdmin):
10981110

10991111
admin.site.register(LivelihoodActivity, LivelihoodActivityAdmin)
11001112
admin.site.register(WealthGroupCharacteristicValue, WealthGroupCharacteristicValueAdmin)
1113+
1114+
admin.site.register(KeyParameter, KeyParameterAdmin)
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import django.db.models.deletion
2+
import django.utils.timezone
3+
import model_utils.fields
4+
from django.db import migrations, models
5+
6+
import common.fields
7+
8+
9+
class Migration(migrations.Migration):
10+
dependencies = [
11+
("baseline", "0016_alter_livelihoodstrategy_additional_identifier_and_more"),
12+
]
13+
14+
operations = [
15+
migrations.CreateModel(
16+
name="KeyParameter",
17+
fields=[
18+
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
19+
(
20+
"created",
21+
model_utils.fields.AutoCreatedField(
22+
default=django.utils.timezone.now, editable=False, verbose_name="created"
23+
),
24+
),
25+
(
26+
"modified",
27+
model_utils.fields.AutoLastModifiedField(
28+
default=django.utils.timezone.now, editable=False, verbose_name="modified"
29+
),
30+
),
31+
(
32+
"strategy_type",
33+
models.CharField(
34+
choices=[
35+
("MilkProduction", "Milk Production"),
36+
("ButterProduction", "Butter Production"),
37+
("MeatProduction", "Meat Production"),
38+
("LivestockSale", "Livestock Sale"),
39+
("CropProduction", "Crop Production"),
40+
("FoodPurchase", "Food Purchase"),
41+
("PaymentInKind", "Payment in Kind"),
42+
("ReliefGiftOther", "Relief, Gift or Other Food"),
43+
("Hunting", "Hunting"),
44+
("Fishing", "Fishing"),
45+
("WildFoodGathering", "Wild Food Gathering"),
46+
("OtherCashIncome", "Other Cash Income"),
47+
("OtherPurchase", "Other Purchase"),
48+
],
49+
db_index=True,
50+
help_text="The type of livelihood strategy, such as crop production, or wild food gathering.",
51+
max_length=30,
52+
verbose_name="Strategy Type",
53+
),
54+
),
55+
(
56+
"key_parameter_type",
57+
models.CharField(
58+
choices=[("price", "Price"), ("quantity", "Quantity")],
59+
help_text="The type of key parameter, such as quantity or price.",
60+
max_length=30,
61+
verbose_name="Key Parameter Type",
62+
),
63+
),
64+
("name_en", common.fields.NameField(max_length=200, verbose_name="Name")),
65+
("name_fr", common.fields.NameField(blank=True, max_length=200, verbose_name="Name")),
66+
("name_es", common.fields.NameField(blank=True, max_length=200, verbose_name="Name")),
67+
("name_pt", common.fields.NameField(blank=True, max_length=200, verbose_name="Name")),
68+
("name_ar", common.fields.NameField(blank=True, max_length=200, verbose_name="Name")),
69+
(
70+
"description_en",
71+
common.fields.DescriptionField(
72+
blank=True,
73+
help_text="Any extra information or detail that is relevant to the object.",
74+
max_length=2000,
75+
verbose_name="Description",
76+
),
77+
),
78+
(
79+
"description_fr",
80+
common.fields.DescriptionField(
81+
blank=True,
82+
help_text="Any extra information or detail that is relevant to the object.",
83+
max_length=2000,
84+
verbose_name="Description",
85+
),
86+
),
87+
(
88+
"description_es",
89+
common.fields.DescriptionField(
90+
blank=True,
91+
help_text="Any extra information or detail that is relevant to the object.",
92+
max_length=2000,
93+
verbose_name="Description",
94+
),
95+
),
96+
(
97+
"description_pt",
98+
common.fields.DescriptionField(
99+
blank=True,
100+
help_text="Any extra information or detail that is relevant to the object.",
101+
max_length=2000,
102+
verbose_name="Description",
103+
),
104+
),
105+
(
106+
"description_ar",
107+
common.fields.DescriptionField(
108+
blank=True,
109+
help_text="Any extra information or detail that is relevant to the object.",
110+
max_length=2000,
111+
verbose_name="Description",
112+
),
113+
),
114+
(
115+
"livelihood_zone_baseline",
116+
models.ForeignKey(
117+
on_delete=django.db.models.deletion.CASCADE,
118+
to="baseline.livelihoodzonebaseline",
119+
verbose_name="Livelihood Zone Baseline",
120+
),
121+
),
122+
],
123+
options={
124+
"abstract": False,
125+
},
126+
),
127+
]

apps/baseline/models.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2360,3 +2360,48 @@ class Strategy(models.TextChoices):
23602360
class Meta:
23612361
verbose_name = _("Coping Strategy")
23622362
verbose_name_plural = _("Coping Strategies")
2363+
2364+
2365+
class KeyParameter(common_models.Model):
2366+
"""
2367+
These are 'key parameters' for a given BSS, as reported in the baseline Fact Sheets.
2368+
2369+
These are defined as:
2370+
2371+
> a source that contributes at least 10% of the kilocalories of one wealth group’s total food or income,
2372+
or at least 5% of two wealth groups' total food or income
2373+
2374+
These are entered manually for three reasons:
2375+
1. We expect to have to support manual overrides anyway.
2376+
2. We lack sufficient data (eg, product kcals per unit, strategy sub-type production figures) [1]
2377+
3. We haven't yet reverse-engineered the formulae in the LIAS for making kcals and cash comparable.
2378+
2379+
[1] A price on a purchase LS is presumably what the Fact Sheets call a 'consumer price'. A price on a
2380+
livestock sale is presumably always a 'producer price'. Labor figures are presumably the ones from the
2381+
livelihood activity detail. And animal numbers and school stats are presumably wealth group characteristics.
2382+
"""
2383+
2384+
class KeyParameterType(models.TextChoices):
2385+
PRICE = "price", _("Price")
2386+
QUANTITY = "quantity", _("Quantity")
2387+
2388+
livelihood_zone_baseline = models.ForeignKey(
2389+
LivelihoodZoneBaseline,
2390+
on_delete=models.CASCADE,
2391+
verbose_name=_("Livelihood Zone Baseline"),
2392+
)
2393+
strategy_type = models.CharField(
2394+
max_length=30,
2395+
choices=LivelihoodStrategyType.choices,
2396+
db_index=True,
2397+
verbose_name=_("Strategy Type"),
2398+
help_text=_("The type of livelihood strategy, such as crop production, or wild food gathering."),
2399+
)
2400+
key_parameter_type = models.CharField(
2401+
max_length=30,
2402+
choices=KeyParameterType.choices,
2403+
verbose_name=_("Key Parameter Type"),
2404+
help_text=_("The type of key parameter, such as quantity or price."),
2405+
)
2406+
name = TranslatedField(common_models.NameField(max_length=200))
2407+
description = TranslatedField(common_models.DescriptionField())

apps/baseline/serializers.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
FoodPurchase,
2020
Hazard,
2121
Hunting,
22+
KeyParameter,
2223
LivelihoodActivity,
2324
LivelihoodProductCategory,
2425
LivelihoodStrategy,
@@ -1466,3 +1467,34 @@ def get_strategy_label(self, obj):
14661467

14671468
def get_wealth_group_label(self, obj):
14681469
return str(obj.wealth_group)
1470+
1471+
1472+
class KeyParameterSerializer(serializers.ModelSerializer):
1473+
class Meta:
1474+
model = KeyParameter
1475+
fields = [
1476+
"id",
1477+
"livelihood_zone_baseline",
1478+
"livelihood_zone_baseline_label",
1479+
"strategy_type",
1480+
"strategy_type_label",
1481+
"key_parameter_type",
1482+
"key_parameter_type_label",
1483+
"name",
1484+
"description",
1485+
]
1486+
1487+
strategy_type_label = serializers.SerializerMethodField()
1488+
1489+
def get_strategy_type_label(self, obj):
1490+
return obj.get_strategy_type_display()
1491+
1492+
key_parameter_type_label = serializers.SerializerMethodField()
1493+
1494+
def get_key_parameter_type_label(self, obj):
1495+
return obj.get_key_parameter_type_display()
1496+
1497+
livelihood_zone_baseline_label = serializers.SerializerMethodField()
1498+
1499+
def get_livelihood_zone_baseline_label(self, obj):
1500+
return str(obj.livelihood_zone_baseline)

apps/baseline/tests/factories.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
FoodPurchase,
2222
Hazard,
2323
Hunting,
24+
KeyParameter,
2425
LivelihoodActivity,
2526
LivelihoodProductCategory,
2627
LivelihoodStrategy,
@@ -799,3 +800,44 @@ class Meta:
799800
)
800801
strategy = factory.Iterator(["reduce", "increase"])
801802
by_value = fuzzy.FuzzyInteger(0, 100)
803+
804+
805+
class KeyParameterFactory(factory.django.DjangoModelFactory):
806+
class Meta:
807+
model = KeyParameter
808+
django_get_or_create = [
809+
"livelihood_zone_baseline",
810+
"strategy_type",
811+
"key_parameter_type",
812+
"name_en",
813+
]
814+
815+
strategy_type = factory.Iterator(
816+
[
817+
"MilkProduction",
818+
"ButterProduction",
819+
"MeatProduction",
820+
"LivestockSale",
821+
"CropProduction",
822+
"FoodPurchase",
823+
"PaymentInKind",
824+
"ReliefGiftOther",
825+
"Fishing",
826+
"Hunting",
827+
"WildFoodGathering",
828+
"OtherCashIncome",
829+
"OtherPurchase",
830+
]
831+
)
832+
livelihood_zone_baseline = factory.SubFactory(LivelihoodZoneBaselineFactory)
833+
key_parameter_type = factory.Iterator(["quantity", "price"])
834+
name_en = factory.Sequence(lambda n: f"Key parameter {n} en")
835+
name_fr = factory.Sequence(lambda n: f"Key parameter {n} fr")
836+
name_es = factory.Sequence(lambda n: f"Key parameter {n} es")
837+
name_pt = factory.Sequence(lambda n: f"Key parameter {n} pt")
838+
name_ar = factory.Sequence(lambda n: f"Key parameter {n} ar")
839+
description_en = factory.LazyAttribute(lambda o: f"{o.name_en} description")
840+
description_fr = factory.LazyAttribute(lambda o: f"{o.name_fr} description")
841+
description_es = factory.LazyAttribute(lambda o: f"{o.name_es} description")
842+
description_pt = factory.LazyAttribute(lambda o: f"{o.name_pt} description")
843+
description_ar = factory.LazyAttribute(lambda o: f"{o.name_ar} description")

apps/baseline/tests/test_factories.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
FoodPurchase,
1717
Hazard,
1818
Hunting,
19+
KeyParameter,
1920
LivelihoodActivity,
2021
LivelihoodProductCategory,
2122
LivelihoodStrategy,
@@ -55,6 +56,7 @@
5556
FoodPurchaseFactory,
5657
HazardFactory,
5758
HuntingFactory,
59+
KeyParameterFactory,
5860
LivelihoodActivityFactory,
5961
LivelihoodProductCategoryFactory,
6062
LivelihoodStrategyFactory,
@@ -264,6 +266,11 @@ def test_copingstrategy_factory(self):
264266
CopingStrategyFactory()
265267
self.assertEqual(CopingStrategy.objects.count(), self.num_records)
266268

269+
def test_key_parameter_factory(self):
270+
for _ in range(self.num_records):
271+
KeyParameterFactory()
272+
self.assertEqual(KeyParameter.objects.count(), self.num_records)
273+
267274
def test_all_factories(self):
268275
for _ in range(2):
269276
SourceOrganizationFactory()
@@ -302,3 +309,4 @@ def test_all_factories(self):
302309
EventFactory()
303310
ExpandabilityFactorFactory()
304311
CopingStrategyFactory()
312+
KeyParameterFactory()

0 commit comments

Comments
 (0)