diff --git a/src/apps/organization/query_serializers.py b/src/apps/organization/query_serializers.py index 77f3628..faf53a4 100644 --- a/src/apps/organization/query_serializers.py +++ b/src/apps/organization/query_serializers.py @@ -1,6 +1,7 @@ """Query serializers for organization analytics endpoints.""" from apps.organization.models import CorporationScope +from apps.organization.scope_utils import SCOPE_ALIASES from rest_framework import serializers @@ -17,8 +18,8 @@ class AliasChoiceField(serializers.ChoiceField): class DashboardQuerySerializer(serializers.Serializer): - corporation_scope = serializers.ChoiceField( - choices=CorporationScope.choices, required=False + corporation_scope = AliasChoiceField( + choices=CorporationScope.choices, aliases=SCOPE_ALIASES, required=False ) diff --git a/src/apps/organization/scope_utils.py b/src/apps/organization/scope_utils.py index 1c96e33..0a6e811 100644 --- a/src/apps/organization/scope_utils.py +++ b/src/apps/organization/scope_utils.py @@ -12,6 +12,10 @@ SCOPE_KEYWORDS: dict[str, tuple[str, ...]] = { "opk": ("ОПК",), } +SCOPE_ALIASES: dict[str, str] = { + "roskosmos": "roscosmos", +} + SCOPE_LABELS: dict[str, str] = { "rosatom": "Госкорпорация «Росатом»", "roscosmos": "Госкорпорация «Роскосмос»", @@ -64,6 +68,11 @@ def scope_labels(scope_codes: Iterable[str]) -> list[str]: return [SCOPE_LABELS[code] for code in scope_codes if code in SCOPE_LABELS] +def normalize_scope_code(scope_code: str) -> str: + normalized = str(scope_code).strip().lower() + return SCOPE_ALIASES.get(normalized, normalized) + + def get_corporation_scope_dictionary() -> list[dict[str, str | int]]: """Возвращает справочник корпусов для API-словаря.""" items: list[dict[str, str | int]] = [] @@ -121,7 +130,11 @@ def build_scope_query(scope_codes: Iterable[str]) -> Q: def filter_queryset_by_scopes( queryset: QuerySet, scope_codes: Iterable[str] ) -> QuerySet: - scope_codes = [code for code in scope_codes if code in SCOPE_KEYWORDS] + scope_codes = [ + normalized_code + for code in scope_codes + if (normalized_code := normalize_scope_code(code)) in SCOPE_KEYWORDS + ] if not scope_codes: return queryset.none() return queryset.filter(build_scope_query(scope_codes)).distinct() diff --git a/tests/apps/organization/test_analytics_api.py b/tests/apps/organization/test_analytics_api.py index 607b7b2..c0a7e46 100644 --- a/tests/apps/organization/test_analytics_api.py +++ b/tests/apps/organization/test_analytics_api.py @@ -480,6 +480,14 @@ class OrganizationAnalyticsApiTest(APITestCase): ) self.assertIn("growth_percent", response.data["headcount_growth_by_cluster"][0]) + def test_dashboard_endpoint_accepts_roskosmos_alias(self): + response = self.client.get( + "/api/v1/analytics/dashboard/?corporation_scope=roskosmos" + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["corporation_scope"], "roscosmos") + def test_analytics_query_validation(self): response = self.client.get( f"/api/v1/organizations/{self.organization.id}/analytics/economics/" diff --git a/tests/apps/organization/test_api.py b/tests/apps/organization/test_api.py index dbb817a..9a7765a 100644 --- a/tests/apps/organization/test_api.py +++ b/tests/apps/organization/test_api.py @@ -193,6 +193,18 @@ class OrganizationApiTest(APITestCase): self.assertEqual(len(response.data["results"]), 1) self.assertEqual(response.data["results"][0]["id"], str(rosatom_org.id)) + for scope_code in ("roscosmos", "roskosmos"): + response = self.client.get( + f"/api/v1/organizations/?corporation_scope={scope_code}" + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data["results"]), 1) + self.assertEqual(response.data["results"][0]["id"], str(roscosmos_org.id)) + self.assertEqual( + response.data["results"][0]["corporation_scope"], "roscosmos" + ) + def test_registry_category_filters_support_snake_and_camel_case(self): opk_org = OrganizationFactory.create(name="АО ОПК") goz_org = OrganizationFactory.create(name="АО ГОЗ", in_275_fz_registry=True)