Skip to content

Commit 55d133b

Browse files
committed
feat(dref): add finalize api for Dref operational update and final report
1 parent 9e969d2 commit 55d133b

File tree

3 files changed

+270
-19
lines changed

3 files changed

+270
-19
lines changed

dref/serializers.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,7 @@ def update(self, instance, validated_data):
719719
return dref
720720

721721

722-
class DrefOperationalUpdateSerializer(NestedUpdateMixin, NestedCreateMixin, ModelSerializer):
722+
class DrefOperationalUpdateSerializer(NestedUpdateMixin, NestedCreateMixin, serializers.ModelSerializer):
723723
MAX_NUMBER_OF_IMAGES = 4
724724
national_society_actions = NationalSocietyActionSerializer(many=True, required=False)
725725
needs_identified = IdentifiedNeedSerializer(many=True, required=False)
@@ -804,6 +804,7 @@ def validate_images_file(self, images):
804804

805805
def create(self, validated_data):
806806
dref = validated_data["dref"]
807+
current_language = get_language()
807808
dref_operational_update = DrefOperationalUpdate.objects.filter(dref=dref).order_by("-operational_update_number").first()
808809
validated_data["created_by"] = self.context["request"].user
809810
if not dref_operational_update:
@@ -927,7 +928,8 @@ def create(self, validated_data):
927928
validated_data["is_man_made_event"] = dref.is_man_made_event
928929
validated_data["event_text"] = dref.event_text
929930
validated_data["did_national_society"] = dref.did_national_society
930-
931+
validated_data["original_language"] = current_language
932+
validated_data[TRANSLATOR_ORIGINAL_LANGUAGE_FIELD_NAME] = current_language
931933
operational_update = super().create(validated_data)
932934
# XXX: Copy files from DREF (Only nested serialized fields)
933935
nested_serialized_file_fields = [
@@ -1097,6 +1099,15 @@ def create(self, validated_data):
10971099
def update(self, instance, validated_data):
10981100
validated_data["modified_by"] = self.context["request"].user
10991101
modified_at = validated_data.pop("modified_at", None)
1102+
1103+
current_lang = get_language()
1104+
original_lang = instance.translation_module_original_language
1105+
if instance.status == Dref.Status.FINALIZED:
1106+
if current_lang != "en":
1107+
raise serializers.ValidationError(gettext("Finalized records can only be updated in English."))
1108+
elif current_lang != original_lang:
1109+
raise serializers.ValidationError(gettext("Only original language is supported: %s" % original_lang))
1110+
11001111
if modified_at is None:
11011112
raise serializers.ValidationError({"modified_at": "Modified At is required!"})
11021113

@@ -1106,7 +1117,7 @@ def update(self, instance, validated_data):
11061117
return super().update(instance, validated_data)
11071118

11081119

1109-
class DrefFinalReportSerializer(NestedUpdateMixin, NestedCreateMixin, ModelSerializer):
1120+
class DrefFinalReportSerializer(NestedUpdateMixin, NestedCreateMixin, serializers.ModelSerializer):
11101121
MAX_NUMBER_OF_PHOTOS = 4
11111122
SUB_TOTAL_COST = 75000
11121123
national_society_actions = NationalSocietyActionSerializer(many=True, required=False)
@@ -1251,13 +1262,16 @@ def create(self, validated_data):
12511262
# here check if there is operational update for corresponding dref
12521263
# if yes copy from the latest operational update
12531264
# else copy from dref
1265+
current_language = get_language()
12541266
dref = validated_data["dref"]
12551267
dref_operational_update = (
12561268
DrefOperationalUpdate.objects.filter(dref=dref, status=Dref.Status.APPROVED)
12571269
.order_by("-operational_update_number")
12581270
.first()
12591271
)
12601272
validated_data["created_by"] = self.context["request"].user
1273+
validated_data["original_language"] = current_language
1274+
validated_data[TRANSLATOR_ORIGINAL_LANGUAGE_FIELD_NAME] = current_language
12611275
# NOTE: Checks and common fields for the new dref final reports of new dref imminents
12621276
if dref.type_of_dref == Dref.DrefType.IMMINENT and dref.is_dref_imminent_v2:
12631277
validated_data["is_dref_imminent_v2"] = True
@@ -1540,6 +1554,13 @@ def create(self, validated_data):
15401554

15411555
def update(self, instance, validated_data):
15421556
modified_at = validated_data.pop("modified_at", None)
1557+
current_lang = get_language()
1558+
original_lang = instance.translation_module_original_language
1559+
if instance.status == Dref.Status.FINALIZED:
1560+
if current_lang != "en":
1561+
raise serializers.ValidationError(gettext("Finalized records can only be updated in English."))
1562+
elif current_lang != original_lang:
1563+
raise serializers.ValidationError(gettext("Only original language is supported: %s" % original_lang))
15431564
if modified_at is None:
15441565
raise serializers.ValidationError({"modified_at": "Modified At is required!"})
15451566
if modified_at and instance.modified_at and modified_at < instance.modified_at:

dref/test_views.py

Lines changed: 192 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from django.contrib.auth.models import Permission
88
from django.contrib.contenttypes.models import ContentType
99
from django.core import management
10-
from django.test import TestCase
1110
from rest_framework import status
1211

1312
from api.models import Country, DisasterType, District, Region, RegionName
@@ -28,7 +27,7 @@
2827
DrefOperationalUpdate,
2928
ProposedAction,
3029
)
31-
from dref.tasks import send_dref_email, translate_fields_to_english
30+
from dref.tasks import send_dref_email
3231
from main.test_case import APITestCase
3332

3433

@@ -1076,6 +1075,105 @@ def test_dref_op_update_locking(self):
10761075
response = self.client.patch(url, data=data)
10771076
self.assertEqual(response.status_code, 400)
10781077

1078+
def test_create_and_update_operational_update(self):
1079+
self.country = Country.objects.create(name="test ops country")
1080+
self.district = District.objects.create(name="test ops dis", country=self.country)
1081+
1082+
user1, user2 = UserFactory.create_batch(2)
1083+
dref = DrefFactory.create(
1084+
title="Test Title", created_by=user1, status=Dref.Status.APPROVED, translation_module_original_language="en"
1085+
)
1086+
dref2 = DrefFactory.create(
1087+
title="Test Title", created_by=user1, status=Dref.Status.DRAFT, translation_module_original_language="ar"
1088+
)
1089+
1090+
ops_update_data = {
1091+
"dref": dref.id,
1092+
"country": self.country.id,
1093+
"district": [self.district.id],
1094+
}
1095+
ops_update_data2 = {
1096+
"dref": dref2.id,
1097+
"country": self.country.id,
1098+
"district": [self.district.id],
1099+
}
1100+
1101+
self.authenticate(user1)
1102+
# Test create
1103+
1104+
self.authenticate(user2)
1105+
url = "/api/v2/dref-op-update/"
1106+
response = self.client.post(url, data=ops_update_data2)
1107+
self.assert_400(response)
1108+
1109+
url = "/api/v2/dref-op-update/"
1110+
response = self.client.post(url, data=ops_update_data, HTTP_ACCEPT_LANGUAGE="fr")
1111+
self.assert_201(response)
1112+
self.assertEqual(response.data["translation_module_original_language"], "fr")
1113+
ops_update_id = response.data["id"]
1114+
# Test Update
1115+
update_url = f"/api/v2/dref-op-update/{ops_update_id}/"
1116+
data_ar = {"title": "العنوان بالعربية", "modified_at": datetime.now()}
1117+
response = self.client.patch(update_url, data=data_ar, HTTP_ACCEPT_LANGUAGE="ar")
1118+
self.assert_400(response)
1119+
# Update in Spanish
1120+
data_es = {"title": "Título en español", "modified_at": datetime.now()}
1121+
response = self.client.patch(update_url, data=data_es, HTTP_ACCEPT_LANGUAGE="es")
1122+
self.assert_400(response)
1123+
1124+
# Update in English
1125+
data_en = {"title": "Updated title in English", "modified_at": datetime.now()}
1126+
response = self.client.patch(update_url, data=data_en, HTTP_ACCEPT_LANGUAGE="en")
1127+
self.assert_400(response)
1128+
1129+
# Update in French
1130+
data_fr = {"title": "Titre en français", "modified_at": datetime.now()}
1131+
response = self.client.patch(update_url, data=data_fr, HTTP_ACCEPT_LANGUAGE="fr")
1132+
self.assert_200(response)
1133+
self.assertEqual(response.data["title"], "Titre en français")
1134+
1135+
@mock.patch("dref.tasks.translate_fields_to_english.delay")
1136+
def test_dref_operational_update_finalize(self, mock_translate):
1137+
# Create users
1138+
user1, user2 = UserFactory.create_batch(2)
1139+
dref = DrefFactory.create(
1140+
title="Test Title",
1141+
created_by=user1,
1142+
status=Dref.Status.APPROVED,
1143+
translation_module_original_language="en",
1144+
)
1145+
dref.users.add(user1)
1146+
op_update = DrefOperationalUpdateFactory.create(
1147+
dref=dref,
1148+
status=Dref.Status.DRAFT,
1149+
operational_update_number=1,
1150+
modified_at=datetime.now(),
1151+
translation_module_original_language="ar",
1152+
)
1153+
1154+
url = f"/api/v2/dref-op-update/{op_update.id}/"
1155+
self.client.force_authenticate(user1)
1156+
1157+
# Update in Arabic (original language)
1158+
data_ar = {"title": "العنوان بالعربية", "modified_at": datetime.now()}
1159+
response = self.client.patch(url, data=data_ar, HTTP_ACCEPT_LANGUAGE="ar")
1160+
self.assert_200(response)
1161+
self.assertEqual(response.data["title"], "العنوان بالعربية")
1162+
1163+
# Update in English
1164+
data_en = {"title": "Updated title in English", "modified_at": datetime.now()}
1165+
response = self.client.patch(url, data=data_en, HTTP_ACCEPT_LANGUAGE="en")
1166+
self.assert_400(response)
1167+
1168+
# Finalize Operational Update
1169+
with self.capture_on_commit_callbacks(execute=True):
1170+
finalize_url = f"/api/v2/dref-op-update/{op_update.id}/finalize/"
1171+
response = self.client.post(finalize_url)
1172+
1173+
self.assert_200(response)
1174+
self.assertEqual(response.data["status"], Dref.Status.FINALIZING)
1175+
mock_translate.assert_called_once_with("dref.DrefOperationalUpdate", op_update.id)
1176+
10791177
def test_optimistic_lock_in_final_report(self):
10801178
user1 = UserFactory.create()
10811179
dref = DrefFactory.create(
@@ -2004,27 +2102,105 @@ def test_dref_imminent_v2_final_report(self):
20042102
},
20052103
)
20062104

2105+
def test_create_and_update_final_report(self):
2106+
user1, user2 = UserFactory.create_batch(2)
2107+
dref = DrefFactory.create(
2108+
title="Test Title",
2109+
created_by=self.user,
2110+
status=Dref.Status.APPROVED,
2111+
type_of_dref=Dref.DrefType.ASSESSMENT,
2112+
)
2113+
dref2 = DrefFactory.create(
2114+
title="Test Title",
2115+
created_by=self.user,
2116+
status=Dref.Status.DRAFT,
2117+
type_of_dref=Dref.DrefType.IMMINENT,
2118+
)
2119+
dref.users.add(user1)
2120+
url = "/api/v2/dref-final-report/"
2121+
data = {
2122+
"dref": dref2.id,
2123+
}
2124+
2125+
self.authenticate(self.user)
2126+
response = self.client.post(url, data=data, HTTP_ACCEPT_LANGUAGE="es")
2127+
self.assert_400(response)
2128+
2129+
url = "/api/v2/dref-final-report/"
2130+
data = {
2131+
"dref": dref.id,
2132+
}
2133+
response = self.client.post(url, data=data, HTTP_ACCEPT_LANGUAGE="es")
2134+
self.assert_201(response)
2135+
self.assertEqual(response.data["translation_module_original_language"], "es")
2136+
final_report_id = response.data["id"]
2137+
# Test Update
2138+
update_url = f"/api/v2/dref-final-report/{final_report_id}/"
2139+
data_ar = {"title": "العنوان بالعربية", "modified_at": datetime.now()}
2140+
response = self.client.patch(update_url, data=data_ar, HTTP_ACCEPT_LANGUAGE="ar")
2141+
self.assert_400(response)
2142+
2143+
# Update in English
2144+
data_en = {"title": "Updated title in English", "modified_at": datetime.now()}
2145+
response = self.client.patch(update_url, data=data_en, HTTP_ACCEPT_LANGUAGE="en")
2146+
self.assert_400(response)
2147+
2148+
# Update in French
2149+
data_fr = {"title": "Titre en français", "modified_at": datetime.now()}
2150+
response = self.client.patch(update_url, data=data_fr, HTTP_ACCEPT_LANGUAGE="fr")
2151+
self.assert_400(response)
20072152

2008-
class TranslateFieldsToEnglishTaskTest(TestCase):
2153+
# Update in Spanish (original language)
2154+
data_es = {"title": "Título en español", "modified_at": datetime.now()}
2155+
response = self.client.patch(update_url, data=data_es, HTTP_ACCEPT_LANGUAGE="es")
2156+
self.assert_200(response)
2157+
self.assertEqual(response.data["translation_module_original_language"], "es")
2158+
self.assertEqual(response.data["title"], "Título en español")
20092159

2010-
def test_translate_fields_to_english_task(self):
2160+
@mock.patch("dref.tasks.translate_fields_to_english.delay")
2161+
def test_dref_final_report_finalize(self, mock_translate):
2162+
region = Region.objects.create(name=RegionName.AFRICA)
2163+
country = Country.objects.create(name="Test country12", region=region)
2164+
# Create users
2165+
user1, user2 = UserFactory.create_batch(2)
20112166
dref = DrefFactory.create(
2012-
title="Titre en français",
2013-
type_of_dref=Dref.DrefType.IMMINENT,
2167+
title="Test Title",
2168+
created_by=user1,
2169+
status=Dref.Status.APPROVED,
2170+
translation_module_original_language="en",
2171+
)
2172+
dref.users.add(user1)
2173+
final_report = DrefFinalReportFactory(
2174+
title="Título en español",
2175+
dref=dref,
2176+
country=country,
2177+
type_of_dref=Dref.DrefType.RESPONSE,
20142178
status=Dref.Status.DRAFT,
2015-
translation_module_original_language="fr",
2179+
translation_module_original_language="es",
20162180
)
2181+
self.client.force_authenticate(user1)
2182+
url = f"/api/v2/dref-final-report/{final_report.id}/"
20172183

2018-
with mock.patch("dref.tasks.ModelTranslator.translate_model_fields_to_english") as mock_translate:
2019-
mock_translate.return_value = None
2184+
# Update in Arabic
2185+
data_ar = {"title": "العنوان بالعربية", "modified_at": datetime.now()}
2186+
response = self.client.patch(url, data=data_ar, HTTP_ACCEPT_LANGUAGE="ar")
2187+
self.assert_400(response)
20202188

2021-
# Call the task
2022-
translate_fields_to_english("dref.Dref", dref.pk)
2023-
# Reload object from DB
2024-
dref.refresh_from_db()
2025-
mock_translate.assert_called_once()
2026-
self.assertEqual(dref.status, Dref.Status.FINALIZED)
2027-
self.assertEqual(dref.translation_module_original_language, "en")
2189+
# Update in Spanish (original language)
2190+
data_es = {"title": "Título en español", "modified_at": datetime.now()}
2191+
response = self.client.patch(url, data=data_es, HTTP_ACCEPT_LANGUAGE="es")
2192+
self.assert_200(response)
2193+
self.assertEqual(response.data["title"], "Título en español")
2194+
self.assertEqual(response.data["translation_module_original_language"], "es")
2195+
2196+
# Finalize final-report
2197+
with self.capture_on_commit_callbacks(execute=True):
2198+
finalize_url = f"/api/v2/dref-final-report/{final_report.id}/finalize/"
2199+
response = self.client.post(finalize_url)
2200+
2201+
self.assert_200(response)
2202+
self.assertEqual(response.data["status"], Dref.Status.FINALIZING)
2203+
mock_translate.assert_called_once_with("dref.DrefFinalReport", final_report.id)
20282204

20292205

20302206
User = get_user_model()

dref/views.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,34 @@ def get_approved(self, request, pk=None, version=None):
191191
serializer = DrefOperationalUpdateSerializer(operational_update, context={"request": request})
192192
return response.Response(serializer.data)
193193

194+
@action(
195+
detail=True,
196+
url_path="finalize",
197+
methods=["post"],
198+
serializer_class=DrefOperationalUpdateSerializer,
199+
permission_classes=[permissions.IsAuthenticated, DenyGuestUserPermission],
200+
)
201+
def finalize(self, request, pk=None, version=None):
202+
operational_update = self.get_object()
203+
if operational_update.status in [Dref.Status.FINALIZED, Dref.Status.APPROVED]:
204+
raise serializers.ValidationError(
205+
gettext("Cannot be finalized because it is already %s") % operational_update.get_status_display()
206+
)
207+
# NOTE: If the operational update original language is English, skip the translation task and update the status.
208+
if operational_update.translation_module_original_language == "en":
209+
operational_update.status = Dref.Status.FINALIZED
210+
operational_update.save(update_fields=["status"])
211+
serializer = DrefOperationalUpdateSerializer(operational_update, context={"request": request})
212+
return response.Response(serializer.data)
213+
214+
operational_update.status = Dref.Status.FINALIZING
215+
operational_update.save(update_fields=["status"])
216+
transaction.on_commit(
217+
lambda: translate_fields_to_english.delay(get_model_name(type(operational_update)), operational_update.pk)
218+
)
219+
serializer = DrefOperationalUpdateSerializer(operational_update, context={"request": request})
220+
return response.Response(serializer.data)
221+
194222

195223
class DrefFinalReportViewSet(RevisionMixin, viewsets.ModelViewSet):
196224
serializer_class = DrefFinalReportSerializer
@@ -229,6 +257,32 @@ def get_approved(self, request, pk=None, version=None):
229257
serializer = DrefFinalReportSerializer(field_report, context={"request": request})
230258
return response.Response(serializer.data)
231259

260+
@action(
261+
detail=True,
262+
url_path="finalize",
263+
methods=["post"],
264+
serializer_class=DrefFinalReportSerializer,
265+
permission_classes=[permissions.IsAuthenticated, DenyGuestUserPermission],
266+
)
267+
def finalize(self, request, pk=None, version=None):
268+
field_report = self.get_object()
269+
if field_report.status in [Dref.Status.FINALIZED, Dref.Status.APPROVED]:
270+
raise serializers.ValidationError(
271+
gettext("Cannot be finalized because it is already %s") % field_report.get_status_display()
272+
)
273+
# NOTE: If the final report original language is English, skip the translation task and update the status.
274+
if field_report.translation_module_original_language == "en":
275+
field_report.status = Dref.Status.FINALIZED
276+
field_report.save(update_fields=["status"])
277+
serializer = DrefFinalReportSerializer(field_report, context={"request": request})
278+
return response.Response(serializer.data)
279+
280+
field_report.status = Dref.Status.FINALIZING
281+
field_report.save(update_fields=["status"])
282+
transaction.on_commit(lambda: translate_fields_to_english.delay(get_model_name(type(field_report)), field_report.pk))
283+
serializer = DrefFinalReportSerializer(field_report, context={"request": request})
284+
return response.Response(serializer.data)
285+
232286

233287
class DrefFileViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet):
234288
permission_classes = [permissions.IsAuthenticated, DenyGuestUserPermission]

0 commit comments

Comments
 (0)