Skip to content

Commit

Permalink
feat: API: Use count of org unit types only if necessary
Browse files Browse the repository at this point in the history
  • Loading branch information
beygorghor authored Dec 16, 2024
2 parents 10b9ac7 + 64a85cf commit 73fe566
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { getRequest } from '../../../../libs/Api';
import { useSnackQuery } from '../../../../libs/apiHooks';
import { makeUrlWithParams } from '../../../../libs/utils';
import {
PaginatedOrgUnitTypes,
OrgUnitTypesParams,
PaginatedOrgUnitTypes,
} from '../../types/orgunitTypes';

const queryParamsMap = new Map([['projectIds', 'project_ids']]);
const apiParamsKeys = ['order', 'page', 'limit', 'search'];
const apiParamsKeys = ['order', 'page', 'limit', 'search', 'with_units_count'];

const getParams = (params: OrgUnitTypesParams) => {
const { pageSize, ...urlParams } = params;
Expand Down
19 changes: 11 additions & 8 deletions hat/assets/js/apps/Iaso/domains/orgUnits/orgUnitTypes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import React, { FunctionComponent } from 'react';
import { Box, Grid } from '@mui/material';
import { makeStyles } from '@mui/styles';
import {
commonStyles,
AddButton,
useSafeIntl,
Column,
commonStyles,
useSafeIntl,
} from 'bluesquare-components';
import React, { FunctionComponent } from 'react';

import TopBar from '../../../components/nav/TopBarComponent';
import { OrgUnitsTypesDialog } from './components/OrgUnitsTypesDialog';
import { TableWithDeepLink } from '../../../components/tables/TableWithDeepLink';
import { OrgUnitsTypesDialog } from './components/OrgUnitsTypesDialog';

import { baseUrls } from '../../../constants/urls';
import { OrgUnitTypesParams } from '../types/orgunitTypes';
import MESSAGES from './messages';

import { useGetColumns } from './config/tableColumns';
import { useGetOrgUnitTypes } from './hooks/useGetOrgUnitTypes';
import { useParamsObject } from '../../../routing/hooks/useParamsObject';
import { Filters } from './components/Filters';
import { useGetColumns } from './config/tableColumns';
import { useGetOrgUnitTypes } from './hooks/useGetOrgUnitTypes';

const baseUrl = baseUrls.orgUnitTypes;

Expand All @@ -28,11 +28,14 @@ const useStyles = makeStyles(theme => ({
}));

const OrgUnitTypes: FunctionComponent = () => {
const params = useParamsObject(baseUrl) as OrgUnitTypesParams;
const params = useParamsObject(baseUrl) as unknown as OrgUnitTypesParams;
const classes: Record<string, string> = useStyles();
const { formatMessage } = useSafeIntl();
const columns: Column[] = useGetColumns();
const { data, isFetching } = useGetOrgUnitTypes(params);
const { data, isFetching } = useGetOrgUnitTypes({
...params,
with_units_count: true,
});
return (
<>
<TopBar
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ export interface PaginatedOrgUnitTypes extends Pagination {
export type OrgUnitTypesParams = UrlParams & {
accountId?: string;
search?: string;
with_units_count?: boolean;
};
25 changes: 23 additions & 2 deletions iaso/api/org_unit_types/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
from django.db.models import Q
from rest_framework import serializers

from iaso.models import Form, OrgUnitType, OrgUnit, Project
from ..common import TimestampField, DynamicFieldsModelSerializer
from iaso.models import Form, OrgUnit, OrgUnitType, Project

from ..common import DynamicFieldsModelSerializer, TimestampField
from ..forms import FormSerializer
from ..projects.serializers import ProjectSerializer

Expand Down Expand Up @@ -79,6 +80,10 @@ class Meta:

# Fixme make this directly in db !
def get_units_count(self, obj: OrgUnitType):
# Skip computation if the parameter is not present
if not self.context["request"].query_params.get("with_units_count"):
return None

orgUnits = OrgUnit.objects.filter_for_user_and_app_id(
self.context["request"].user, self.context["request"].query_params.get("app_id")
).filter(Q(validated=True) & Q(org_unit_type__id=obj.id))
Expand Down Expand Up @@ -121,6 +126,12 @@ def validate(self, data: typing.Mapping):
raise serializers.ValidationError({"project_ids": "Invalid project ids"})
return data

def to_representation(self, instance):
# Remove units_count from fields if not requested
if not self.context["request"].query_params.get("with_units_count"):
self.fields.pop("units_count", None)
return super().to_representation(instance)


class OrgUnitTypeSerializerV2(DynamicFieldsModelSerializer):
"""This one is a bit cryptic: sub_unit_types is only needed for "root" org unit types
Expand Down Expand Up @@ -179,6 +190,10 @@ class Meta:

# Fixme make this directly in db !
def get_units_count(self, obj: OrgUnitType):
# Skip computation if the parameter is not present
if not self.context["request"].query_params.get("with_units_count"):
return None

orgUnits = OrgUnit.objects.filter_for_user_and_app_id(
self.context["request"].user, self.context["request"].query_params.get("app_id")
).filter(Q(validation_status=OrgUnit.VALIDATION_VALID) & Q(org_unit_type__id=obj.id))
Expand Down Expand Up @@ -244,6 +259,12 @@ def validate(self, data: typing.Mapping):
validate_reference_forms(data)
return data

def to_representation(self, instance):
# Remove units_count from fields if not requested
if not self.context["request"].query_params.get("with_units_count"):
self.fields.pop("units_count", None)
return super().to_representation(instance)


class OrgUnitTypesDropdownSerializer(serializers.ModelSerializer):
class Meta:
Expand Down
23 changes: 22 additions & 1 deletion iaso/tests/api/test_org_unit_types_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def test_org_unit_types_list_count_valid_orgunits(self):
self.data_source_1.projects.set([self.ead, self.esd])

self.client.force_authenticate(self.jane)
response = self.client.get("/api/v2/orgunittypes/?order=id")
response = self.client.get("/api/v2/orgunittypes/?order=id&with_units_count=true")
self.assertJSONResponse(response, 200)

response_data = response.json()["orgUnitTypes"]
Expand All @@ -154,6 +154,27 @@ def test_org_unit_types_list_count_valid_orgunits(self):
for other_types in response_data[2:]:
self.assertEqual(other_types["units_count"], 0)

def test_org_unit_types_list_without_units_count(self):
"""GET /orgunittypes/ without with_units_count should not include units_count in response"""

# Create some org units to ensure there would be counts if requested
m.OrgUnit.objects.create(
name="OU 1 ok",
org_unit_type=self.org_unit_type_1,
validation_status=m.OrgUnit.VALIDATION_VALID,
version=self.version_1,
)

self.client.force_authenticate(self.jane)
response = self.client.get("/api/v2/orgunittypes/?order=id")
self.assertJSONResponse(response, 200)

response_data = response.json()["orgUnitTypes"]

# Verify units_count is not present in any of the returned org unit types
for org_unit_type in response_data:
self.assertNotIn("units_count", org_unit_type)

def test_org_unit_types_retrieve_without_auth_or_app_id(self):
"""GET /orgunittypes/<org_unit_type_id>/ without auth or app id should result in a 200 empty response"""

Expand Down

0 comments on commit 73fe566

Please sign in to comment.