Skip to content

Commit

Permalink
Update service - get protected areas list for project
Browse files Browse the repository at this point in the history
  • Loading branch information
yulia-bel committed Sep 20, 2023
1 parent 917d052 commit 75abd36
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { AppConfig } from '@marxan-api/utils/config.utils';
import { IUCNCategory } from '@marxan/iucn';
import { isDefined } from '@marxan/utils';
import { Scenario } from '../scenarios/scenario.api.entity';
import { groupBy, intersection } from 'lodash';
import _, {groupBy, intersection, isArray} from 'lodash';
import { ProjectSnapshot } from '@marxan/projects';
import { SelectionGetService } from '@marxan-api/modules/scenarios/protected-area/getter/selection-get.service';
import { Either, left, right } from 'fp-ts/Either';
Expand Down Expand Up @@ -192,6 +192,7 @@ export class ProtectedAreasCrudService extends AppBaseService<
'designation',
'scenarioUsageCount',
'isCustom',
'name',
],
keyForAttribute: 'camelCase',
};
Expand All @@ -209,12 +210,19 @@ export class ProtectedAreasCrudService extends AppBaseService<
project,
);

const uniqueCategoryGlobalProtectedAreasIds: string[] = []

for (const category of projectGlobalProtectedAreasData.categories) {
const wdpaOfCategorySample = await this.repository.findOneOrFail({where: {iucnCategory: category as IUCNCategory}})
uniqueCategoryGlobalProtectedAreasIds.push(wdpaOfCategorySample.id)
}

query.andWhere(
new Brackets((qb) => {
qb.where(`${this.alias}.projectId = :projectId`, {
projectId: project.id,
}).orWhere(`${this.alias}.id in (:...ids)`, {
ids: Object.values(projectGlobalProtectedAreasData.areas).flat(),
ids: uniqueCategoryGlobalProtectedAreasIds,
})
}),
)
Expand Down Expand Up @@ -360,34 +368,70 @@ export class ProtectedAreasCrudService extends AppBaseService<

info!.params.project = project;

const projectProtectedAreas = await this.findAllPaginated(
fetchSpecification,
/** if the fetchSpecification contains sort or filter with 'name' property, we need to remove it from fetch specification
* used for base service findAll method, because 'name' column does not exist in protected_areas table and error will occur
* Filtering and sorting by 'name' will be done manually later in this method
*/

const editedFetchSpecification = JSON.parse(JSON.stringify(fetchSpecification))

if(fetchSpecification?.filter?.name) {
delete editedFetchSpecification?.filter?.name;
}

if(fetchSpecification?.sort?.includes('name') || fetchSpecification?.sort?.includes('-name')) {
editedFetchSpecification?.sort?.splice(editedFetchSpecification?.sort?.indexOf('name'), 1);
editedFetchSpecification?.sort?.splice(editedFetchSpecification?.sort?.indexOf('-name'), 1);
}

const projectProtectedAreas = await this.findAll(
editedFetchSpecification,
info,
);

const result: any[] = [];
let result: any[] = [];

projectProtectedAreas.data.forEach((protectedArea) => {
projectProtectedAreas[0].forEach((protectedArea: any) => {
const scenarioUsageCount: number = protectedAreaUsedInProjectScenarios[
protectedArea!.id!
]
? protectedAreaUsedInProjectScenarios[protectedArea!.id!].length
: 0;
const name = protectedArea.fullName === null ? protectedArea.iucnCategory : protectedArea.fullName;
result.push({
...protectedArea,
scenarioUsageCount,
isCustom: protectedArea!.projectId !== null,
name
});
});

const serializer = new JSONAPISerializer.Serializer(
'protected_areas', {
...this.serializerConfig,
meta: projectProtectedAreas.metadata,

// Applying filtering and sorting manually for 'name' property

if(fetchSpecification?.filter?.name) {
const filterNames: string[] = fetchSpecification?.filter?.name as string[];
result = result.filter((pa) => {
return filterNames.includes(pa.name)
});
}

if (fetchSpecification?.sort?.includes('name') || fetchSpecification?.sort?.includes('-name')) {
if(fetchSpecification?.sort?.includes('name')) {
result.sort((a, b) => a.name.localeCompare(b.name));
} else {
result.sort((a, b) => b.name.localeCompare(a.name));
}

}

// Serialising final result

const serializer = new JSONAPISerializer.Serializer(
'protected_areas', this.serializerConfig,
);

return serializer.serialize(result, projectProtectedAreas.metadata);
return serializer.serialize(result);
}

public async updateProtectedAreaName(
Expand Down
20 changes: 20 additions & 0 deletions api/apps/api/test/scenario-protected-areas/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,17 @@ export const getFixtures = async () => {
.get(`/api/v1/projects/${projectId}/protected-areas?q=${search}`)
.set('Authorization', `Bearer ${ownerToken}`)
.then((response) => response.body),

WhenGettingProtectedAreasListForProjectWithFilter: async (projectId: string, filterColumn: string, filterValue: string) =>
request(app.getHttpServer())
.get(`/api/v1/projects/${projectId}/protected-areas?filter[${filterColumn}]=${filterValue}`)
.set('Authorization', `Bearer ${ownerToken}`)
.then((response) => response.body),
WhenGettingProtectedAreasListForProjectWithSort: async (projectId: string, sortValue: string) =>
request(app.getHttpServer())
.get(`/api/v1/projects/${projectId}/protected-areas?sort=${sortValue}`)
.set('Authorization', `Bearer ${ownerToken}`)
.then((response) => response.body),
ThenItContainsRelevantWdpa: async (response: any) => {
expect(response).toEqual([
{
Expand Down Expand Up @@ -148,6 +154,20 @@ export const getFixtures = async () => {
expect(response.data[0].attributes.scenarioUsageCount).toBe(1);
},

ThenItContainsSortedProtectedAreas: async (response: any) => {
response.data.sort();
expect(response.data).toHaveLength(2);
expect(response.data[0].attributes.isCustom).toBe(true);
expect(response.data[0].attributes.name).toBe(
'custom protected area',
);
expect(response.data[1].attributes.isCustom).toBe(false);
expect(response.data[1].attributes.name).toBe(
'III',
);

},

GivenCustomProtectedAreaWasAddedToProject: async () => {
const ids: { id: string }[] = await wdpa.query(
`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,19 @@ test(`getting list of protected areas with scenario usage count for a project an
);
await fixtures.ThenItContainsFilteredProtectedAreas(areas);
});

test(`getting list of protected areas with scenario usage count for a project and using sort`, async () => {
const projectId = fixtures.GivenProjectExists();
await fixtures.GivenGlobalProtectedAreaWasCreated(IUCNCategory.III);
const scenario: string = await fixtures.GivenScenarioInsideNAM41WasCreated();
const areaId = await fixtures.GivenCustomProtectedAreaWasAddedToProject();
await fixtures.GivenAreasWereSelectedAsOwner(
scenario,
IUCNCategory.III,
areaId,
);
const areas = await fixtures.WhenGettingProtectedAreasListForProjectWithSort(
projectId, 'name'
);
await fixtures.ThenItContainsSortedProtectedAreas(areas);
});
5 changes: 5 additions & 0 deletions api/libs/protected-areas/src/protected-areas.geo.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,9 @@ export class ProtectedArea extends TimeUserEntityMetadata {
* If protected area is global or custom for project.
*/
isCustom?: boolean;

/**
* Name used for project protected areas list (fullname for custom, iucn category for global).
*/
name?: string;
}

0 comments on commit 75abd36

Please sign in to comment.