Skip to content

Commit b55eedb

Browse files
Crate api for eap create, update, get
- Modified models - Added serializers - Added views - Added test cases, factories
1 parent 7d4b299 commit b55eedb

File tree

9 files changed

+546
-36
lines changed

9 files changed

+546
-36
lines changed

eap/admin.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
from django.contrib import admin
22

33
# Register your models here.
4+
from .models import EAP
5+
6+
7+
@admin.register(EAP)
8+
class EAPAdmin(admin.ModelAdmin):
9+
pass

eap/factories.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import datetime
2+
import factory
3+
from factory import fuzzy
4+
from datetime import timezone
5+
6+
from django.core.files.base import ContentFile
7+
8+
from api.factories import (
9+
disaster_type,
10+
country,
11+
district,
12+
)
13+
14+
from .models import (
15+
EAP,
16+
EAPDocument,
17+
)
18+
19+
20+
class EAPDocumentFactory(factory.django.DjangoModelFactory):
21+
class Meta:
22+
model = EAPDocument
23+
24+
file = factory.LazyAttribute(
25+
lambda _: ContentFile(
26+
factory.django.ImageField()._make_data(
27+
{'width': 1024, 'height': 768}
28+
), 'flash_update.jpg'
29+
)
30+
)
31+
32+
33+
class EAPFactory(factory.django.DjangoModelFactory):
34+
class Meta:
35+
model = EAP
36+
37+
district = factory.SubFactory(district.DistrictFactory)
38+
country = factory.SubFactory(country.CountryFactory)
39+
disaster_type = factory.SubFactory(disaster_type.DisasterTypeFactory)
40+
document = factory.SubFactory(EAPDocumentFactory)
41+
eap_number = fuzzy.FuzzyText(length=20)
42+
approval_date = fuzzy.FuzzyDateTime(datetime.datetime(2008, 1, 1, tzinfo=timezone.utc))
43+
status = fuzzy.FuzzyChoice(EAP.Status)
44+
operational_timeframe = fuzzy.FuzzyInteger(0, 2)
45+
lead_time = fuzzy.FuzzyInteger(0, 2)
46+
eap_timeframe = fuzzy.FuzzyInteger(0, 2)
47+
num_of_people = fuzzy.FuzzyInteger(0, 5)
48+
total_budget = fuzzy.FuzzyInteger(0, 5)
49+
readiness_budget = fuzzy.FuzzyInteger(0, 5)
50+
pre_positioning_budget = fuzzy.FuzzyInteger(0, 5)
51+
early_action_budget = fuzzy.FuzzyInteger(0, 5)
52+
trigger_statement = fuzzy.FuzzyText(length=20)
53+
overview = fuzzy.FuzzyText(length=50)
54+
originator_name = fuzzy.FuzzyText(length=50)
55+
originator_title = fuzzy.FuzzyText(length=50)
56+
originator_email = fuzzy.FuzzyText(length=50)
57+
originator_phone = fuzzy.FuzzyInteger(0, 9)
58+
59+
nsc_name = fuzzy.FuzzyText(length=50)
60+
nsc_title = fuzzy.FuzzyText(length=50)
61+
nsc_email = fuzzy.FuzzyText(length=50)
62+
nsc_phone = fuzzy.FuzzyInteger(0, 9)
63+
64+
ifrc_focal_name = fuzzy.FuzzyText(length=50)
65+
ifrc_focal_title = fuzzy.FuzzyText(length=50)
66+
ifrc_focal_email = fuzzy.FuzzyText(length=50)
67+
ifrc_focal_phone = fuzzy.FuzzyInteger(0, 9)
68+
69+
@factory.post_generation
70+
def early_actions(self, create, extracted, **kwargs):
71+
if not create:
72+
return
73+
74+
if extracted:
75+
for early_action in extracted:
76+
self.early_actions.add(early_action)

eap/migrations/0001_initial.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Generated by Django 2.2.28 on 2022-07-08 05:56
1+
# Generated by Django 2.2.28 on 2022-07-13 05:26
22

33
import deployments.models
44
from django.conf import settings
@@ -13,8 +13,8 @@ class Migration(migrations.Migration):
1313
initial = True
1414

1515
dependencies = [
16-
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
1716
('api', '0156_appealfilter_comment'),
17+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
1818
]
1919

2020
operations = [
@@ -24,9 +24,9 @@ class Migration(migrations.Migration):
2424
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
2525
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='created at')),
2626
('modified_at', models.DateTimeField(auto_now=True, verbose_name='updated at')),
27-
('eap_number', models.CharField(max_length=50, verbose_name='EAP Number')),
27+
('eap_number', models.CharField(editable=False, max_length=50, verbose_name='EAP Number')),
2828
('approval_date', models.DateField(verbose_name='Date of EAP Approval')),
29-
('status', models.CharField(choices=[('approved', 'Approved'), ('in_process', 'In Process')], default=eap.models.EAP.Status('in_process'), max_length=255, verbose_name='EAP Status')),
29+
('status', models.CharField(choices=[('approved', 'Approved'), ('activated', 'Activated')], default=eap.models.EAP.Status('approved'), max_length=255, verbose_name='EAP Status')),
3030
('operational_timeframe', models.IntegerField(verbose_name='Operational Timeframe (Months)')),
3131
('lead_time', models.IntegerField(verbose_name='Lead Time')),
3232
('eap_timeframe', models.IntegerField(verbose_name='EAP Timeframe (Years)')),
@@ -37,7 +37,6 @@ class Migration(migrations.Migration):
3737
('early_action_budget', models.IntegerField(blank=True, null=True, verbose_name='Early Actions Budget (CHF)')),
3838
('trigger_statement', models.TextField(verbose_name='Trigger Statement (Threshold for Activation)')),
3939
('overview', models.TextField(verbose_name='EAP Overview')),
40-
('document', models.FileField(blank=True, null=True, upload_to='eap/documents/', verbose_name='EAP Documents')),
4140
('originator_name', models.CharField(blank=True, max_length=255, null=True, verbose_name='Originator Name')),
4241
('originator_title', models.CharField(blank=True, max_length=255, null=True, verbose_name='Originator Title')),
4342
('originator_email', models.CharField(blank=True, max_length=255, null=True, verbose_name='Originator Email')),
@@ -90,16 +89,16 @@ class Migration(migrations.Migration):
9089
},
9190
),
9291
migrations.CreateModel(
93-
name='EAPRefrence',
92+
name='EAPReference',
9493
fields=[
9594
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
9695
('source', models.CharField(blank=True, max_length=255, null=True, verbose_name='Name')),
9796
('url', models.URLField(blank=True, null=True, verbose_name='URL')),
9897
('eap', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='eap_reference', to='eap.EAP', verbose_name='EAP')),
9998
],
10099
options={
101-
'verbose_name': 'EAP Refrence',
102-
'verbose_name_plural': 'EAP Refrences',
100+
'verbose_name': 'EAP Reference',
101+
'verbose_name_plural': 'EAP References',
103102
},
104103
),
105104
migrations.CreateModel(
@@ -115,6 +114,23 @@ class Migration(migrations.Migration):
115114
'verbose_name_plural': 'EAP Partners',
116115
},
117116
),
117+
migrations.CreateModel(
118+
name='EAPDocument',
119+
fields=[
120+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
121+
('file', models.FileField(blank=True, null=True, upload_to='')),
122+
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='document_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created by')),
123+
],
124+
options={
125+
'verbose_name': 'Document',
126+
'verbose_name_plural': 'Documents',
127+
},
128+
),
129+
migrations.AddField(
130+
model_name='eap',
131+
name='document',
132+
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='eap_document', to='eap.EAPDocument', verbose_name='EAP Documents'),
133+
),
118134
migrations.AddField(
119135
model_name='eap',
120136
name='early_actions',

eap/migrations/0002_auto_20220708_0747.py

Lines changed: 0 additions & 21 deletions
This file was deleted.

eap/models.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,25 @@ def __str__(self):
5050
return f'{self.sector}'
5151

5252

53+
class EAPDocument(models.Model):
54+
file = models.FileField(null=True, blank=True)
55+
created_by = models.ForeignKey(
56+
settings.AUTH_USER_MODEL, verbose_name=_('Created by'), related_name='document_created_by',
57+
null=True, blank=True, on_delete=models.SET_NULL,
58+
)
59+
60+
class Meta:
61+
verbose_name = _('Document')
62+
verbose_name_plural = _('Documents')
63+
64+
def __str__(self):
65+
return str(self.id)
66+
67+
5368
class EAP(models.Model):
5469
class Status(TextChoices): # TODO some more status choices are to be expected by client.
5570
APPROVED = 'approved', _('Approved')
56-
IN_PROCESS = 'in_process', _('In Process')
71+
ACTIVATED = 'activated', _('Activated')
5772

5873
created_by = models.ForeignKey(
5974
settings.AUTH_USER_MODEL, verbose_name=_('Created by'), related_name='eap_created_by',
@@ -78,10 +93,10 @@ class Status(TextChoices): # TODO some more status choices are to be expected b
7893
DisasterType, on_delete=models.SET_NULL, verbose_name=_('Disaster Type'),
7994
related_name='eap_disaster_type', null=True
8095
)
81-
eap_number = models.CharField(max_length=50, verbose_name=_('EAP Number'))
96+
eap_number = models.CharField(max_length=50, editable=False, verbose_name=_('EAP Number'))
8297
approval_date = models.DateField(verbose_name=_('Date of EAP Approval'))
8398
status = models.CharField(
84-
max_length=255, choices=Status.choices, default=Status.IN_PROCESS,
99+
max_length=255, choices=Status.choices, default=Status.APPROVED,
85100
verbose_name=_('EAP Status')
86101
)
87102
operational_timeframe = models.IntegerField(verbose_name=_('Operational Timeframe (Months)'))
@@ -94,8 +109,9 @@ class Status(TextChoices): # TODO some more status choices are to be expected b
94109
early_action_budget = models.IntegerField(verbose_name=_('Early Actions Budget (CHF)'), null=True, blank=True)
95110
trigger_statement = models.TextField(verbose_name=_('Trigger Statement (Threshold for Activation)'))
96111
overview = models.TextField(verbose_name=_('EAP Overview'))
97-
document = models.FileField(
98-
verbose_name=_('EAP Documents'), upload_to='eap/documents/',
112+
document = models.ForeignKey(
113+
EAPDocument, on_delete=models.SET_NULL,
114+
verbose_name=_('EAP Documents'), related_name='eap_document',
99115
null=True, blank=True
100116
)
101117
early_actions = models.ManyToManyField(
@@ -165,3 +181,4 @@ class Meta:
165181

166182
def __str__(self):
167183
return f'{self.id}'
184+

eap/serializers.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
from django.utils.translation import ugettext
2+
3+
from rest_framework import serializers
4+
5+
from enumfields.drf.serializers import EnumSupportSerializerMixin
6+
7+
from api.serializers import (
8+
UserNameSerializer,
9+
DisasterTypeSerializer,
10+
CountrySerializer,
11+
MiniDistrictSerializer,
12+
)
13+
14+
from eap.models import (
15+
EAP,
16+
Action,
17+
EAPPartner,
18+
EAPReference,
19+
EarlyAction,
20+
EarlyActionIndicator,
21+
EAPDocument,
22+
)
23+
24+
from main.writable_nested_serializers import (
25+
NestedCreateMixin,
26+
NestedUpdateMixin,
27+
)
28+
29+
30+
class EAPReferenceSerializer(serializers.ModelSerializer):
31+
class Meta:
32+
model = EAPReference
33+
fields = '__all__'
34+
read_only_fields = ('eap',)
35+
36+
37+
class EAPPartnerSerializer(serializers.ModelSerializer):
38+
class Meta:
39+
model = EAPPartner
40+
fields = '__all__'
41+
read_only_fields = ('eap',)
42+
43+
44+
class EarlyActionIndicatorSerializer(serializers.ModelSerializer):
45+
class Meta:
46+
model = EarlyActionIndicator
47+
fields = ('__all__')
48+
49+
50+
class ActionSerializer(serializers.ModelSerializer):
51+
class Meta:
52+
model = Action
53+
fields = ('__all__')
54+
read_only_fields = ('early_action',)
55+
56+
57+
class EarlyActionSerializer(
58+
NestedUpdateMixin,
59+
NestedCreateMixin,
60+
serializers.ModelSerializer
61+
):
62+
indicators = EarlyActionIndicatorSerializer(many=True, required=False)
63+
actions = ActionSerializer(source='action', many=True, required=False)
64+
sector_display = serializers.CharField(source='get_sector_display', read_only=True)
65+
66+
class Meta:
67+
model = EarlyAction
68+
fields = ('__all__')
69+
70+
71+
class EAPActionSerializer(serializers.ModelSerializer):
72+
class Meta:
73+
model = Action
74+
fields = ('__all__')
75+
read_only_fields = ('early_action',)
76+
77+
78+
class EAPDocumentSerializer(serializers.ModelSerializer):
79+
created_by_details = UserNameSerializer(source='created_by', read_only=True)
80+
file = serializers.FileField(required=False)
81+
82+
class Meta:
83+
model = EAPDocument
84+
fields = '__all__'
85+
read_only_fields = ('created_by',)
86+
87+
def create(self, validated_data):
88+
validated_data['created_by'] = self.context['request'].user
89+
return super().create(validated_data)
90+
91+
92+
class EAPSerializer(
93+
EnumSupportSerializerMixin,
94+
NestedUpdateMixin,
95+
NestedCreateMixin,
96+
serializers.ModelSerializer
97+
):
98+
country_details = CountrySerializer(source='country', read_only=True)
99+
district_details = MiniDistrictSerializer(source='district', read_only=True)
100+
references = EAPReferenceSerializer(source='eap_reference', many=True, required=False)
101+
partners = EAPPartnerSerializer(source='eap_partner', many=True, required=False)
102+
early_actions = EarlyActionSerializer(many=True)
103+
created_by_details = UserNameSerializer(source='created_by', read_only=True)
104+
hazard_type_details = DisasterTypeSerializer(source='disaster_type', read_only=True)
105+
document_details = EAPDocumentSerializer(source='document', read_only=True, required=False)
106+
107+
class Meta:
108+
model = EAP
109+
fields = '__all__'
110+
111+
def validate(self, validated_data):
112+
district = validated_data['district']
113+
if district:
114+
if district.country != validated_data['country']:
115+
raise serializers.ValidationError({
116+
'district': ugettext('Different districts found for given country')
117+
})
118+
return validated_data
119+
120+
def create(self, validated_data):
121+
validated_data['created_by'] = self.context['request'].user
122+
eap = super().create(validated_data)
123+
return eap
124+
125+
def update(self, instance, validated_data):
126+
validated_data['modified_by'] = self.context['request'].user
127+
eap = super().update(instance, validated_data)
128+
return eap
129+

0 commit comments

Comments
 (0)