Skip to content

Commit 06832e1

Browse files
committed
PER data-fetcher functionality to backend side, v1.0
1 parent 14be734 commit 06832e1

File tree

2 files changed

+111
-0
lines changed

2 files changed

+111
-0
lines changed

main/urls.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,12 @@
117117
router.register(r"per-overview", per_views.PerOverviewViewSet, basename="new_per")
118118
router.register(r"per-assessment", per_views.FormAssessmentViewSet, basename="per-assessent")
119119
router.register(r"public-per-assessment", per_views.PublicFormAssessmentViewSet, basename="public-per-assessent")
120+
router.register(r"public-per-assessment-2", per_views.PublicFormAssessmentViewSet2, basename="public-per-assessent-2")
120121
router.register(r"per-prioritization", per_views.FormPrioritizationViewSet, basename="per-priorirization")
121122
router.register(r"public-per-prioritization", per_views.PublicFormPrioritizationViewSet, basename="public-per-priorirization")
123+
router.register(
124+
r"public-per-prioritization-2", per_views.PublicFormPrioritizationViewSet2, basename="public-per-priorirization-2"
125+
)
122126
router.register(r"per-work-plan", per_views.NewPerWorkPlanViewSet)
123127
router.register(r"per-formanswer", per_views.FormAnswerViewset, basename="per-formanswer")
124128
router.register(r"per-formarea", per_views.FormAreaViewset, basename="per-formarea")
@@ -129,6 +133,7 @@
129133
router.register(r"per-file", per_views.PerFileViewSet, basename="per-file")
130134
router.register(r"per-process-status", per_views.PerProcessStatusViewSet, basename="per-process-status")
131135
router.register(r"public-per-process-status", per_views.PublicPerProcessStatusViewSet, basename="public-per-process-status")
136+
router.register(r"public-per-process-status-2", per_views.PublicPerProcessStatusViewSet2, basename="public-per-process-status-2")
132137
router.register(r"perdocs", per_views.PERDocsViewset)
133138
router.register(r"per-country", per_views.PerCountryViewSet, basename="per-country")
134139
router.register(r"public-per-stats", per_views.CountryPublicPerStatsViewset, basename="public_country_per_stats")

per/drf_views.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,30 @@
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í", "oui", "da", "ja", "sim", "aye", "yep", "igen", "hai", "evet", "是", "はい", "예", "نعم"}
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

104128
class 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+
560614
class 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+
599670
class 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+
616722
class PerFileViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet):
617723
permission_classes = [permissions.IsAuthenticated, DenyGuestUserPermission]
618724
serializer_class = PerFileSerializer

0 commit comments

Comments
 (0)