Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[api] Add range of amounts (min, max) for feature per PU [MRXN23-444] #1551

Merged
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import { MigrationInterface, QueryRunner } from 'typeorm';

export class AddTokenIdColumnToLocks1645026803969
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { MigrationInterface, QueryRunner } from "typeorm"

export class AddMinMaxAmountColumnsToFeatures1697210673344 implements MigrationInterface {

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE features
ADD COLUMN amount_min float8,
ADD COLUMN amount_max float8;
`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE features
DROP COLUMN amount_min float8,
DROP COLUMN amount_max float8;
`);
}

}
14 changes: 14 additions & 0 deletions api/apps/api/src/modules/geo-features/geo-feature.api.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ export interface GeoFeatureProperty {
distinctValues: Array<string | number>;
}

export type FeatureAmountRange = {
min: number | null;
max: number | null;
};

@Entity('features')
export class GeoFeature extends BaseEntity {
@ApiProperty()
Expand Down Expand Up @@ -95,6 +100,12 @@ export class GeoFeature extends BaseEntity {
@Column('boolean', { name: 'is_custom' })
isCustom?: boolean;

@Column('float8', { name: 'amount_min' })
amountMin?: number;

@Column('float8', { name: 'amount_max' })
amountMax?: number;

@Column('boolean', { name: 'is_legacy' })
isLegacy!: boolean;

Expand All @@ -109,6 +120,9 @@ export class GeoFeature extends BaseEntity {

@ApiPropertyOptional()
scenarioUsageCount?: number;

@ApiPropertyOptional()
amountRange?: FeatureAmountRange;
}

export class JSONAPIGeoFeaturesData {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ import { mapAclDomainToHttpError } from '@marxan-api/utils/acl.utils';
)
export class GeoFeaturesController {
constructor(
public readonly service: GeoFeaturesService,
private readonly geoFeatureService: GeoFeaturesService,
public readonly geoFeaturesTagService: GeoFeatureTagsService,
private readonly proxyService: ProxyService,
Expand All @@ -75,8 +74,10 @@ export class GeoFeaturesController {
async findAll(
@ProcessFetchSpecification() fetchSpecification: FetchSpecification,
): Promise<GeoFeatureResult> {
const results = await this.service.findAllPaginated(fetchSpecification);
return this.service.serialize(results.data, results.metadata);
const results = await this.geoFeatureService.findAllPaginated(
fetchSpecification,
);
return this.geoFeatureService.serialize(results.data, results.metadata);
}

@ImplementsAcl()
Expand Down
22 changes: 21 additions & 1 deletion api/apps/api/src/modules/geo-features/geo-features.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import { ScenarioFeaturesService } from '@marxan-api/modules/scenarios-features'
import { GeoFeatureTag } from '@marxan-api/modules/geo-feature-tags/geo-feature-tag.api.entity';
import { GeoFeatureTagsService } from '@marxan-api/modules/geo-feature-tags/geo-feature-tags.service';
import { FeatureAmountUploadService } from '@marxan-api/modules/geo-features/import/features-amounts-upload.service';
import { isLeft } from 'fp-ts/Either';
import { isNil } from 'lodash';

const geoFeatureFilterKeyNames = [
Expand Down Expand Up @@ -138,6 +137,7 @@ export class GeoFeaturesService extends AppBaseService<
'isCustom',
'tag',
'scenarioUsageCount',
'amountRange',
],
keyForAttribute: 'camelCase',
};
Expand Down Expand Up @@ -834,4 +834,24 @@ export class GeoFeaturesService extends AppBaseService<
scenarioUsageCount: usage ? Number(usage.count) : 0,
} as GeoFeature;
}

async saveAmountRangeForFeatures(featureIds: string[]) {
for (const featureId of featureIds) {
const minAndMaxAmount = await this.geoEntityManager
.createQueryBuilder()
.select('MIN(amount)', 'min')
.addSelect('MAX(amount)', 'max')
.from('puvspr_calculations', 'puvspr')
.where('puvspr.feature_id = :featureId', { featureId })
hotzevzl marked this conversation as resolved.
Show resolved Hide resolved
.getOne();

await this.geoFeaturesRepository.update(
{ id: featureId },
{
amountMin: minAndMaxAmount?.min ?? null,
amountMax: minAndMaxAmount?.max ?? null,
},
);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
import {
BadRequestException,
forwardRef,
Inject,
Injectable,
Logger,
} from '@nestjs/common';
import { DbConnections } from '@marxan-api/ormconfig.connections';
import { DataSource, EntityManager, QueryRunner } from 'typeorm';
import { InjectDataSource, InjectEntityManager } from '@nestjs/typeorm';
import { featureAmountCsvParser } from '@marxan-api/modules/geo-features/import/csv.parser';
import { FeatureAmountCSVDto } from '@marxan-api/modules/geo-features/dto/feature-amount-csv.dto';
import { FeatureAmountUploadRegistry } from '@marxan-api/modules/geo-features/import/features-amounts-upload-registry.api.entity';
import {
GeoFeaturesService,
importedFeatureNameAlreadyExist,
unknownPuidsInFeatureAmountCsvUpload,
} from '@marxan-api/modules/geo-features/geo-features.service';
Expand All @@ -19,6 +26,7 @@ import { CHUNK_SIZE_FOR_BATCH_APIDB_OPERATIONS } from '@marxan-api/utils/chunk-s
import { UploadedFeatureAmount } from '@marxan-api/modules/geo-features/import/features-amounts-data.api.entity';
import { Project } from '@marxan-api/modules/projects/project.api.entity';
import { ProjectSourcesEnum } from '@marxan/projects';
import { ScenariosService } from '@marxan-api/modules/scenarios/scenarios.service';

@Injectable()
export class FeatureAmountUploadService {
Expand All @@ -33,6 +41,8 @@ export class FeatureAmountUploadService {
@InjectEntityManager(DbConnections.default)
private readonly apiEntityManager: EntityManager,
private readonly events: FeatureImportEventsService,
@Inject(forwardRef(() => GeoFeaturesService))
private readonly geoFeaturesService: GeoFeaturesService,
) {}

async uploadFeatureFromCsv(data: {
Expand Down Expand Up @@ -107,6 +117,12 @@ export class FeatureAmountUploadService {
apiQueryRunner.manager,
);

this.logger.log(`Saving min and max amounts for new features...`);
hotzevzl marked this conversation as resolved.
Show resolved Hide resolved

await this.geoFeaturesService.saveAmountRangeForFeatures(
newFeaturesFromCsvUpload.map((feature) => feature.id),
);
hotzevzl marked this conversation as resolved.
Show resolved Hide resolved

this.logger.log(`Csv file upload process finished successfully`);
// Committing transaction

Expand Down Expand Up @@ -211,7 +227,7 @@ export class FeatureAmountUploadService {
queryRunner: QueryRunner,
uploadId: string,
projectId: string,
) {
): Promise<GeoFeature[]> {
const newFeaturesToCreate = (
await queryRunner.manager
.createQueryBuilder()
Expand Down
31 changes: 22 additions & 9 deletions api/apps/api/src/modules/projects/projects.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,16 +178,29 @@ export class ProjectsService {
return project;
}

return right(
await this.geoCrud.findAllPaginated(fetchSpec, {
...appInfo,
params: {
...appInfo.params,
projectId: project.right.id,
bbox: project.right.bbox,
},
const result = await this.geoCrud.findAllPaginated(fetchSpec, {
...appInfo,
params: {
...appInfo.params,
projectId: project.right.id,
bbox: project.right.bbox,
},
});

const resultWithMappedAmountRange = {
data: result.data.map((feature) => {
return {
...feature,
amountRange: {
min: feature?.amountMin ?? null,
max: feature?.amountMax ?? null,
},
};
}),
);
metadata: result.metadata,
};

return right(resultWithMappedAmountRange);
}

async findAll(fetchSpec: FetchSpecification, info: ProjectsServiceRequest) {
Expand Down
6 changes: 5 additions & 1 deletion api/apps/api/test/geo-features.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import { bootstrapApplication } from './utils/api-application';
import { GivenUserIsLoggedIn } from './steps/given-user-is-logged-in';

import { createWorld } from './project/projects-world';
import { createWorld } from './projects/projects-world';
import { Repository } from 'typeorm';
import { ScenarioFeaturesData } from '@marxan/features';
import { v4 } from 'uuid';
Expand Down Expand Up @@ -65,6 +65,10 @@ describe('GeoFeaturesModule (e2e)', () => {
const geoFeaturesForProject: GeoFeature[] = response.body.data;
expect(geoFeaturesForProject.length).toBeGreaterThan(0);
expect(response.body.data[0].type).toBe(geoFeatureResource.name.plural);
expect(response.body.data[0].attributes.amountRange).toEqual({
min: null,
max: null,
});
});

test('should include correct scenarioUsageCounts for the given project', async () => {
Expand Down
2 changes: 2 additions & 0 deletions api/apps/api/test/upload-feature/upload-feature.fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ export const getFixtures = async () => {
featureClassName: name,
description,
alias: null,
amountMax: null,
amountMin: null,
propertyName: null,
intersection: null,
creationStatus: `done`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type ProjectCustomFeaturesSelectResult = {
list_property_keys: string[];
is_legacy: boolean;
tag: string;
amount_min: number | null;
amount_max: number | null;
};

type FeaturesDataSelectResult = {
Expand Down Expand Up @@ -81,6 +83,8 @@ export class ProjectCustomFeaturesPieceExporter
'f.creation_status',
'f.list_property_keys',
'f.is_legacy',
'f.amount_min',
'f.amount_max',
'pft.tag',
])
.from('features', 'f')
Expand Down