Skip to content

Commit

Permalink
chore(Features): Applies special rounding to non legacy features' amo…
Browse files Browse the repository at this point in the history
…unt range when retrieving features
  • Loading branch information
KevSanchez committed Dec 21, 2023
1 parent 42e452d commit 0996dbe
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 15 deletions.
35 changes: 24 additions & 11 deletions api/apps/api/src/modules/projects/projects.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,7 @@ import { GetScenarioFailure } from '@marxan-api/modules/blm/values/blm-repos';
import stream from 'stream';
import { AppConfig } from '@marxan-api/utils/config.utils';
import { WebshotBasicPdfConfig } from '@marxan/webshot/webshot.dto';
import {
ScenariosService,
SubmitProtectedAreaError,
} from '@marxan-api/modules/scenarios/scenarios.service';
import { ScenariosService } from '@marxan-api/modules/scenarios/scenarios.service';
import {
OutputProjectSummariesService,
outputProjectSummaryNotFound,
Expand All @@ -126,6 +123,7 @@ import {
import { ensureShapefileHasRequiredFiles } from '@marxan-api/utils/file-uploads.utils';
import { CostSurfaceService } from '@marxan-api/modules/cost-surface/cost-surface.service';
import { GeoFeature } from '../geo-features/geo-feature.api.entity';

export { validationFailed } from '../planning-areas';

export const projectNotFound = Symbol(`project not found`);
Expand Down Expand Up @@ -192,7 +190,10 @@ export class ProjectsService {
data: result.data.map((feature) => {
return {
...feature,
amountRange: this.transformMinMaxAmountsFromSquareMetresToSquareKmsForFeaturesFromShapefile(feature),
amountRange:
this.transformMinMaxAmountsFromSquareMetresToSquareKmsForFeaturesFromShapefile(
feature,
),
};
}),
metadata: result.metadata,
Expand All @@ -210,12 +211,24 @@ export class ProjectsService {
* report them in square km rather than in square metres (they are stored
* in square metres in the platform's backend).
*/
transformMinMaxAmountsFromSquareMetresToSquareKmsForFeaturesFromShapefile(feature: Partial<GeoFeature> | undefined): { min: number | null, max: number | null } {
const min = feature?.amountMin ? (feature.isLegacy ? feature.amountMin : feature.amountMin / 1_000_000) : null;
const max = feature?.amountMax ? (feature.isLegacy ? feature.amountMax : feature.amountMax / 1_000_000) : null;
return {
min, max
};
transformMinMaxAmountsFromSquareMetresToSquareKmsForFeaturesFromShapefile(
feature: Partial<GeoFeature> | undefined,
): { min: number | null; max: number | null } {
let minResult = feature?.amountMin ? feature?.amountMin : null;
let maxResult = feature?.amountMax ? feature?.amountMax : null;
if (!feature?.isLegacy) {
if (feature?.amountMin) {
const minKm2 = feature.amountMin / 1_000_000;
minResult =
minKm2 < 1 ? parseFloat(minKm2.toFixed(4)) : Math.round(minKm2);
}
if (feature?.amountMax) {
const maxKm2 = feature.amountMax / 1_000_000;
maxResult =
maxKm2 < 1 ? parseFloat(maxKm2.toFixed(4)) : Math.round(maxKm2);
}
}
return { min: minResult, max: maxResult };
}

async findAll(fetchSpec: FetchSpecification, info: ProjectsServiceRequest) {
Expand Down
89 changes: 85 additions & 4 deletions api/apps/api/test/geo-features.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { range } from 'lodash';
import { GeoFeatureGeometry } from '@marxan/geofeatures';
import { Scenario } from '@marxan-api/modules/scenarios/scenario.api.entity';
import { FixtureType } from '@marxan/utils/tests/fixture-type';
import { GivenUserExists } from './steps/given-user-exists';
import * as fs from 'fs/promises';
import * as path from 'path';

Expand Down Expand Up @@ -71,6 +70,74 @@ describe('GeoFeaturesModule (e2e)', () => {
});
});

test('should include the min and max amount of the features in the proper units of measure', async () => {
const projectId = world.projectWithCountry; // doesn't really matter how the project is created for this test
const sqlName = 'generic_namidia_feature_data';
const featureId1 = await fixtures.GivenFeatureWithData(
'legacy',
sqlName,
projectId,
);
const featureId2 = await fixtures.GivenFeatureWithData(
'nonLegacyBelow1',
sqlName,
projectId,
);
const featureId3 = await fixtures.GivenFeatureWithData(
'nonLegacyOver1',
sqlName,
projectId,
);
await fixtures.GivenMinMaxAmountForFeature(
featureId1,
true,
22304094,
0.1234,
);
await fixtures.GivenMinMaxAmountForFeature(
featureId2,
false,
123456,
567890,
);
await fixtures.GivenMinMaxAmountForFeature(
featureId3,
false,
123456789,
567891234,
);
const response = await request(app.getHttpServer())
.get(`/api/v1/projects/${world.projectWithCountry}/features`)
.set('Authorization', `Bearer ${jwtToken}`)
.expect(HttpStatus.OK);

const geoFeaturesForProject: any[] = response.body.data
.filter((element: any) =>
[featureId1, featureId2, featureId3].includes(element.id),
)
.sort((element: any) => element.name);
expect(geoFeaturesForProject.length).toBe(3);
expect(geoFeaturesForProject[0].attributes.featureClassName).toBe('legacy');
expect(geoFeaturesForProject[0].attributes.amountRange).toEqual({
min: 22304094,
max: 0.1234,
});
expect(geoFeaturesForProject[1].attributes.featureClassName).toBe(
'nonLegacyBelow1',
);
expect(geoFeaturesForProject[1].attributes.amountRange).toEqual({
min: parseFloat((0.123456).toFixed(4)),
max: parseFloat((0.56789).toFixed(4)),
});
expect(geoFeaturesForProject[2].attributes.featureClassName).toBe(
'nonLegacyOver1',
);
expect(geoFeaturesForProject[2].attributes.amountRange).toEqual({
min: Math.round(123.456789),
max: Math.round(567.891234),
});
});

test('should include correct scenarioUsageCounts for the given project', async () => {
// This tests accounts for both cases of having platform wide features and custom features assigned to projects

Expand Down Expand Up @@ -215,6 +282,21 @@ export const getGeoFeatureFixtures = async (app: INestApplication) => {
);

return {
GivenMinMaxAmountForFeature: async (
featureId: string,
isLegacy: boolean,
min: number,
max: number,
) => {
const feature = await featureRepo.findOneOrFail({
where: { id: featureId },
});
feature.isLegacy = isLegacy;
feature.amountMin = min;
feature.amountMax = max;

await featureRepo.save(feature);
},
GivenFeatureWithData: async (
name: string,
sqlFilename: string,
Expand Down Expand Up @@ -244,9 +326,8 @@ export const getGeoFeatureFixtures = async (app: INestApplication) => {
);
const content = await fs.readFile(filePath, 'utf-8');

return geoEntityManager.query(
content.replace(/\$feature_id/g, featureId),
);
await geoEntityManager.query(content.replace(/\$feature_id/g, featureId));
return featureId;
},
GivenScenarioFeaturesData: async (
featureClassName: string,
Expand Down

0 comments on commit 0996dbe

Please sign in to comment.