11import typing
22from datetime import timedelta
33
4- from celery import group
4+ from celery import chain , group
55from django .conf import settings
66from django .contrib .auth .models import User
77from django .db import transaction
@@ -357,7 +357,7 @@ class OperationActivitySerializer(
357357 )
358358 timeframe_display = serializers .CharField (source = "get_timeframe_display" , read_only = True )
359359 time_value = serializers .ListField (
360- child = serializers .IntegerField (),
360+ child = serializers .IntegerField (required = True ),
361361 required = True ,
362362 )
363363
@@ -371,6 +371,9 @@ def validate(self, validated_data: dict[str, typing.Any]) -> dict[str, typing.An
371371 timeframe = validated_data ["timeframe" ]
372372 time_value = validated_data ["time_value" ]
373373
374+ if time_value is None or len (time_value ) == 0 :
375+ raise serializers .ValidationError ({"time_value" : gettext ("time_value is required and cannot be empty." )})
376+
374377 allowed_values = ALLOWED_MAP_TIMEFRAMES_VALUE .get (timeframe , [])
375378 invalid_values = [value for value in time_value if value not in allowed_values ]
376379
@@ -880,7 +883,7 @@ def create(self, validated_data: dict[str, typing.Any]):
880883class EAPStatusSerializer (BaseEAPSerializer ):
881884 status_display = serializers .CharField (source = "get_status_display" , read_only = True )
882885 # NOTE: Only required when changing status to NS Addressing Comments
883- review_checklist_file = serializers .FileField (required = False )
886+ review_checklist_file = serializers .FileField (required = False , write_only = True )
884887
885888 class Meta :
886889 model = EAPRegistration
@@ -924,23 +927,9 @@ def _validate_status(self, validated_data: dict[str, typing.Any]) -> dict[str, t
924927 if self .instance .get_eap_type_enum == EAPType .SIMPLIFIED_EAP :
925928 self .instance .latest_simplified_eap .is_locked = True
926929 self .instance .latest_simplified_eap .save (update_fields = ["is_locked" ])
927- # NOTE: Generating export PDF asynchronously
928- transaction .on_commit (
929- lambda : generate_export_eap_pdf .delay (
930- eap_registration_id = self .instance .id ,
931- version = self .instance .latest_simplified_eap .version ,
932- )
933- )
934930 else :
935931 self .instance .latest_full_eap .is_locked = True
936932 self .instance .latest_full_eap .save (update_fields = ["is_locked" ])
937- # NOTE: Generate export PDF asynchronously
938- transaction .on_commit (
939- lambda : generate_export_eap_pdf .delay (
940- eap_registration_id = self .instance .id ,
941- version = self .instance .latest_full_eap .version ,
942- )
943- )
944933
945934 # NOTE: IFRC Admins should be able to transition from TECHNICALLY_VALIDATED
946935 # to NS_ADDRESSING_COMMENTS to allow NS users to update their EAP changes after validated budget has been set.
@@ -1029,21 +1018,6 @@ def _validate_status(self, validated_data: dict[str, typing.Any]) -> dict[str, t
10291018 # Lock the latest eap
10301019 self .instance .latest_simplified_eap .is_locked = True
10311020 self .instance .latest_simplified_eap .save (update_fields = ["is_locked" ])
1032-
1033- # Generating PDFs asynchronously
1034- transaction .on_commit (
1035- lambda : group (
1036- generate_export_eap_pdf .s (
1037- eap_registration_id = self .instance .id ,
1038- version = self .instance .latest_simplified_eap .version ,
1039- ),
1040- generate_export_diff_pdf .s (
1041- eap_registration_id = self .instance .id ,
1042- version = self .instance .latest_simplified_eap .version ,
1043- ),
1044- ).apply_async ()
1045- )
1046-
10471021 else :
10481022 if self .instance .latest_full_eap .is_locked :
10491023 raise serializers .ValidationError (
@@ -1065,20 +1039,6 @@ def _validate_status(self, validated_data: dict[str, typing.Any]) -> dict[str, t
10651039 self .instance .latest_full_eap .is_locked = True
10661040 self .instance .latest_full_eap .save (update_fields = ["is_locked" ])
10671041
1068- # Generating PDFs asynchronously
1069- transaction .on_commit (
1070- lambda : group (
1071- generate_export_eap_pdf .s (
1072- eap_registration_id = self .instance .id ,
1073- version = self .instance .latest_full_eap .version ,
1074- ),
1075- generate_export_diff_pdf .s (
1076- eap_registration_id = self .instance .id ,
1077- version = self .instance .latest_full_eap .version ,
1078- ),
1079- ).apply_async ()
1080- )
1081-
10821042 elif (current_status , new_status ) == (
10831043 EAPRegistration .Status .TECHNICALLY_VALIDATED ,
10841044 EAPRegistration .Status .PENDING_PFA ,
@@ -1102,10 +1062,6 @@ def _validate_status(self, validated_data: dict[str, typing.Any]) -> dict[str, t
11021062 ]
11031063 )
11041064
1105- # Generate summary eap for full eap
1106- if self .instance .get_eap_type_enum == EAPType .FULL_EAP :
1107- transaction .on_commit (lambda : generate_eap_summary_pdf .delay (self .instance .id ))
1108-
11091065 elif (current_status , new_status ) == (
11101066 EAPRegistration .Status .PENDING_PFA ,
11111067 EAPRegistration .Status .APPROVED ,
@@ -1152,7 +1108,18 @@ def update(self, instance: EAPRegistration, validated_data: dict[str, typing.Any
11521108 EAPRegistration .Status .UNDER_DEVELOPMENT ,
11531109 EAPRegistration .Status .UNDER_REVIEW ,
11541110 ):
1155- transaction .on_commit (lambda : send_new_eap_submission_email .delay (eap_registration_id ))
1111+ # NOTE: Generating export pdf and sending email to IFRC at the first submission to under review.
1112+ latest_eap = (
1113+ instance .latest_simplified_eap
1114+ if instance .get_eap_type_enum == EAPType .SIMPLIFIED_EAP
1115+ else instance .latest_full_eap
1116+ )
1117+ transaction .on_commit (
1118+ lambda : chain (
1119+ generate_export_eap_pdf .s (eap_registration_id , latest_eap .version ),
1120+ send_new_eap_submission_email .si (eap_registration_id ),
1121+ ).apply_async ()
1122+ )
11561123
11571124 elif (old_status , new_status ) in [
11581125 (EAPRegistration .Status .UNDER_REVIEW , EAPRegistration .Status .NS_ADDRESSING_COMMENTS ),
@@ -1205,7 +1172,18 @@ def update(self, instance: EAPRegistration, validated_data: dict[str, typing.Any
12051172 EAPRegistration .Status .NS_ADDRESSING_COMMENTS ,
12061173 EAPRegistration .Status .UNDER_REVIEW ,
12071174 ):
1208- transaction .on_commit (lambda : send_eap_resubmission_email .delay (eap_registration_id ))
1175+ # NOTE: Generating diff pdf and sending email to IFRC after NS resubmission.
1176+ latest_eap = (
1177+ instance .latest_simplified_eap
1178+ if instance .get_eap_type_enum == EAPType .SIMPLIFIED_EAP
1179+ else instance .latest_full_eap
1180+ )
1181+ transaction .on_commit (
1182+ lambda : chain (
1183+ generate_export_diff_pdf .s (eap_registration_id , latest_eap .version ),
1184+ send_eap_resubmission_email .si (eap_registration_id ),
1185+ ).apply_async ()
1186+ )
12091187
12101188 elif (old_status , new_status ) == (
12111189 EAPRegistration .Status .UNDER_REVIEW ,
@@ -1217,7 +1195,23 @@ def update(self, instance: EAPRegistration, validated_data: dict[str, typing.Any
12171195 EAPRegistration .Status .TECHNICALLY_VALIDATED ,
12181196 EAPRegistration .Status .PENDING_PFA ,
12191197 ):
1220- transaction .on_commit (lambda : send_pending_pfa_email .delay (eap_registration_id ))
1198+ # NOTE: Generating diff pdf and summary pdf (for full eap) and sending email to PFA after technical validation.
1199+ is_full_eap = instance .get_eap_type_enum == EAPType .FULL_EAP
1200+ version = instance .latest_simplified_eap .version if not is_full_eap else instance .latest_full_eap .version
1201+
1202+ tasks = [
1203+ generate_export_diff_pdf .s (eap_registration_id , version ),
1204+ ]
1205+
1206+ if is_full_eap :
1207+ tasks .append (generate_eap_summary_pdf .s (eap_registration_id ))
1208+
1209+ transaction .on_commit (
1210+ lambda : chain (
1211+ group (tasks ),
1212+ send_pending_pfa_email .si (eap_registration_id ),
1213+ ).apply_async ()
1214+ )
12211215
12221216 elif (old_status , new_status ) == (
12231217 EAPRegistration .Status .PENDING_PFA ,
0 commit comments