100100 UserPerCountrySerializer ,
101101)
102102
103+ # Helpers for transformed "-2" endpoints
104+ AREA_NAMES = {
105+ 1 : "Policy Strategy and Standards" ,
106+ 2 : "Analysis and planning" ,
107+ 3 : "Operational capacity" ,
108+ 4 : "Coordination" ,
109+ 5 : "Operations support" ,
110+ }
111+
112+ AFFIRMATIVE_WORDS = {"yes" , "si" , "sí" }
113+
114+
115+ def _contains_affirmative (text : str ) -> bool :
116+ if not text or not isinstance (text , str ):
117+ return False
118+ try :
119+ import unicodedata
120+
121+ normalized = unicodedata .normalize ("NFD" , text .lower ())
122+ normalized = "" .join (ch for ch in normalized if unicodedata .category (ch ) != "Mn" )
123+ except Exception :
124+ normalized = text .lower ()
125+ return any (word in normalized for word in AFFIRMATIVE_WORDS )
126+
103127
104128class PERDocsFilter (filters .FilterSet ):
105129 id = filters .NumberFilter (field_name = "id" , lookup_expr = "exact" )
@@ -118,6 +142,7 @@ class PERDocsViewset(viewsets.ReadOnlyModelViewSet):
118142 queryset = NiceDocument .objects .all ()
119143 authentication_classes = (TokenAuthentication ,)
120144 permission_classes = (IsAuthenticated ,)
145+
121146 get_request_user_regions = RegionRestrictedAdmin .get_request_user_regions
122147 get_filtered_queryset = RegionRestrictedAdmin .get_filtered_queryset
123148 filterset_class = PERDocsFilter
@@ -126,6 +151,7 @@ def get_queryset(self):
126151 queryset = NiceDocument .objects .all ()
127152 cond1 = Q ()
128153 cond2 = Q ()
154+
129155 cond3 = Q ()
130156 if "new" in self .request .query_params .keys ():
131157 last_duedate = settings .PER_LAST_DUEDATE
@@ -557,6 +583,34 @@ class PublicFormPrioritizationViewSet(viewsets.ReadOnlyModelViewSet):
557583 ordering_fields = "__all__"
558584
559585
586+ class PublicFormPrioritizationViewSet2 (PublicFormPrioritizationViewSet ):
587+ """Adds simplified prioritized components array (mirrors JS)."""
588+
589+ def list (self , request , * args , ** kwargs ):
590+ resp = super ().list (request , * args , ** kwargs )
591+ data = resp .data
592+ results = data .get ("results" ) if isinstance (data , dict ) else data
593+ if results :
594+ for item in results :
595+ pcs = []
596+ for resp_item in item .get ("prioritized_action_responses" , []) or []:
597+ cd = resp_item .get ("component_details" ) or {}
598+ pcs .append (
599+ {
600+ "componentId" : resp_item .get ("component" ),
601+ "componentTitle" : cd .get ("title" ),
602+ "areaTitle" : (
603+ cd .get ("area_title" ) or AREA_NAMES .get (cd .get ("area" ))
604+ if isinstance (cd .get ("area" ), int )
605+ else cd .get ("area_title" )
606+ ),
607+ "description" : cd .get ("description" ),
608+ }
609+ )
610+ item ["components" ] = pcs
611+ return Response (data )
612+
613+
560614class PerOptionsView (views .APIView ):
561615 permission_classes = [permissions .IsAuthenticated , DenyGuestUserPermission ]
562616 ordering_fields = "__all__"
@@ -596,6 +650,23 @@ def get_queryset(self):
596650 return Overview .objects .order_by ("country" , "-assessment_number" , "-date_of_assessment" )
597651
598652
653+ class PublicPerProcessStatusViewSet2 (PublicPerProcessStatusViewSet ):
654+ """Phase display normalization (mirrors JS)."""
655+
656+ def list (self , request , * args , ** kwargs ):
657+ resp = super ().list (request , * args , ** kwargs )
658+ data = resp .data
659+ results = data .get ("results" ) if isinstance (data , dict ) else data
660+ if results :
661+ for row in results :
662+ pd = row .get ("phase_display" )
663+ if pd == "Action And Accountability" :
664+ row ["phase_display" ] = "Action & accountability"
665+ elif pd == "WorkPlan" :
666+ row ["phase_display" ] = "Workplan"
667+ return Response (data )
668+
669+
599670class FormAssessmentViewSet (viewsets .ModelViewSet ):
600671 serializer_class = PerAssessmentSerializer
601672 permission_classes = [permissions .IsAuthenticated , PerGeneralPermission , DenyGuestUserPermission ]
@@ -613,6 +684,41 @@ def get_queryset(self):
613684 return PerAssessment .objects .select_related ("overview" )
614685
615686
687+ class PublicFormAssessmentViewSet2 (PublicFormAssessmentViewSet ):
688+ """Adds affirmative flags & dashboard components (mirrors JS)."""
689+
690+ def list (self , request , * args , ** kwargs ):
691+ resp = super ().list (request , * args , ** kwargs )
692+ data = resp .data
693+ results = data .get ("results" ) if isinstance (data , dict ) else data
694+ if results :
695+ for assessment in results :
696+ components = []
697+ for area in assessment .get ("area_responses" , []) or []:
698+ for comp in area .get ("component_responses" , []) or []:
699+ comp ["urban_considerations_simplified" ] = _contains_affirmative (comp .get ("urban_considerations" ))
700+ comp ["epi_considerations_simplified" ] = _contains_affirmative (comp .get ("epi_considerations" ))
701+ comp ["migration_considerations_simplified" ] = _contains_affirmative (comp .get ("migration_considerations" ))
702+ comp ["climate_environmental_considerations_simplified" ] = _contains_affirmative (
703+ comp .get ("climate_environmental_considerations" )
704+ )
705+ cd = comp .get ("component_details" ) or {}
706+ rd = comp .get ("rating_details" ) or {}
707+ components .append (
708+ {
709+ "component_id" : comp .get ("component" ),
710+ "component_name" : cd .get ("title" ) or "" ,
711+ "component_num" : cd .get ("component_num" ),
712+ "area_id" : cd .get ("area" ),
713+ "area_name" : AREA_NAMES .get (cd .get ("area" )) if isinstance (cd .get ("area" ), int ) else None ,
714+ "rating_value" : rd .get ("value" ) or 0 ,
715+ "rating_title" : rd .get ("title" ) or "" ,
716+ }
717+ )
718+ assessment ["components" ] = components
719+ return Response (data )
720+
721+
616722class PerFileViewSet (mixins .ListModelMixin , mixins .CreateModelMixin , viewsets .GenericViewSet ):
617723 permission_classes = [permissions .IsAuthenticated , DenyGuestUserPermission ]
618724 serializer_class = PerFileSerializer
0 commit comments