Skip to content

Commit

Permalink
Merge pull request #305 from Vizzuality/chore/api/output-entities-def…
Browse files Browse the repository at this point in the history
…inition

output entities migration ready + fake data for outputs
  • Loading branch information
aagm authored Jul 12, 2021
2 parents 9c6b039 + eb58277 commit d4b0611
Show file tree
Hide file tree
Showing 33 changed files with 914 additions and 321 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class marxanOutputEntities1624890503611 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
-- Rename output summary table to be more verbose - maps output_sum
-- Also connectivity_in, edge, out and fraction should be under metadata
ALTER TABLE output_results
RENAME TO output_scenarios_summaries;
ALTER TABLE output_scenarios_summaries
RENAME COLUMN scenarios_id TO scenario_id;
ALTER TABLE output_scenarios_summaries
ADD COLUMN best bool,
ADD COLUMN distinct_five bool;
`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
-- Rename output summary table to is past
ALTER TABLE output_scenarios_summaries
RENAME TO output_results;
ALTER TABLE output_results
RENAME COLUMN scenario_id TO scenarios_id,
DROP COLUMN best,
DROP COLUMN distinct_five;
`);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiExtraModels, ApiProperty, getSchemaPath } from '@nestjs/swagger';
import { ScenariosOutputResultsGeoEntity } from '@marxan/scenarios-planning-unit';
import { ScenariosOutputResultsApiEntity } from '@marxan/scenarios-planning-unit';

class ScenarioSolutionsDataDto {
@ApiProperty()
Expand All @@ -10,9 +10,9 @@ class ScenarioSolutionsDataDto {

@ApiProperty({
isArray: true,
type: () => ScenariosOutputResultsGeoEntity,
type: () => ScenariosOutputResultsApiEntity,
})
attributes!: ScenariosOutputResultsGeoEntity[];
attributes!: ScenariosOutputResultsApiEntity[];
}

class ScenarioSolutionDataDto {
Expand All @@ -23,9 +23,9 @@ class ScenarioSolutionDataDto {
id!: string;

@ApiProperty({
type: () => ScenariosOutputResultsGeoEntity,
type: () => ScenariosOutputResultsApiEntity,
})
attributes!: ScenariosOutputResultsGeoEntity;
attributes!: ScenariosOutputResultsApiEntity;
}

@ApiExtraModels(ScenarioSolutionsDataDto, ScenarioSolutionDataDto)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { PaginationMeta } from '@marxan-api/utils/app-base.service';
import { ScenariosOutputResultsGeoEntity } from '@marxan/scenarios-planning-unit';
import { ScenariosOutputResultsApiEntity } from '@marxan/scenarios-planning-unit';
import { SolutionResultCrudService } from '../solutions-result/solution-result-crud.service';

@Injectable()
Expand All @@ -11,8 +11,8 @@ export class ScenarioSolutionSerializer {

async serialize(
entities:
| Partial<ScenariosOutputResultsGeoEntity>
| (Partial<ScenariosOutputResultsGeoEntity> | undefined)[],
| Partial<ScenariosOutputResultsApiEntity>
| (Partial<ScenariosOutputResultsApiEntity> | undefined)[],
paginationMeta?: PaginationMeta,
): Promise<any> {
return this.scenariosSolutionsCrudService.serialize(
Expand Down
42 changes: 20 additions & 22 deletions api/apps/api/src/modules/scenarios/scenarios.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
Get,
Header,
Param,
ParseBoolPipe,
ParseUUIDPipe,
Patch,
Post,
Expand Down Expand Up @@ -64,7 +63,7 @@ import { ProxyService } from '@marxan-api/modules/proxy/proxy.service';
import { ZipFilesSerializer } from './dto/zip-files.serializer';

const basePath = `${apiGlobalPrefixes.v1}/scenarios`;
const solutionsSubPath = `:id/marxan/run/:runId/solutions`;
const solutionsSubPath = `:id/marxan/solutions`;

const marxanRunTag = 'Marxan Run';
const marxanRunFiles = 'Marxan Run - Files';
Expand Down Expand Up @@ -307,26 +306,10 @@ export class ScenariosController {
@Get(solutionsSubPath)
async getScenarioRunSolutions(
@Param('id', ParseUUIDPipe) id: string,
@Param('runId', ParseUUIDPipe) runId: string,
@ProcessFetchSpecification() fetchSpecification: FetchSpecification,
@Query('best', ParseBoolPipe) selectOnlyBest?: boolean,
@Query('most-different', ParseBoolPipe) selectMostDifferent?: boolean,
): Promise<ScenarioFeatureResultDto> {
if (selectOnlyBest) {
return this.getScenarioRunBestSolutions(id, runId);
}

if (selectMostDifferent) {
return this.getScenarioRunMostDifferentSolutions(
id,
runId,
fetchSpecification,
);
}

const result = await this.service.findAllSolutionsPaginated(
id,
runId,
fetchSpecification,
);
return this.scenarioSolutionSerializer.serialize(
Expand Down Expand Up @@ -377,10 +360,12 @@ export class ScenariosController {
@Get(`${solutionsSubPath}/best`)
async getScenarioRunBestSolutions(
@Param('id', ParseUUIDPipe) id: string,
@Param('runId', ParseUUIDPipe) runId: string,
@ProcessFetchSpecification() fetchSpecification: FetchSpecification,
): Promise<ScenarioFeatureResultDto> {
const result = await this.service.getBestSolution(id, fetchSpecification);
return this.scenarioSolutionSerializer.serialize(
await this.service.getBestSolution(id, runId),
result.data,
result.metadata,
);
}

Expand All @@ -392,12 +377,10 @@ export class ScenariosController {
@Get(`${solutionsSubPath}/most-different`)
async getScenarioRunMostDifferentSolutions(
@Param('id', ParseUUIDPipe) id: string,
@Param('runId', ParseUUIDPipe) runId: string,
@ProcessFetchSpecification() fetchSpecification: FetchSpecification,
): Promise<ScenarioFeatureResultDto> {
const result = await this.service.getMostDifferentSolutions(
id,
runId,
fetchSpecification,
);
return this.scenarioSolutionSerializer.serialize(
Expand All @@ -406,6 +389,21 @@ export class ScenariosController {
);
}

@ApiOkResponse({
type: ScenarioSolutionResultDto,
})
@JSONAPIQueryParams()
@Get(`${solutionsSubPath}/:runId`)
async getScenarioRunId(
@Param('id', ParseUUIDPipe) id: string,
@Param('runId', ParseUUIDPipe) runId: string,
@ProcessFetchSpecification() fetchSpecification: FetchSpecification,
): Promise<ScenarioFeatureResultDto> {
return this.scenarioSolutionSerializer.serialize(
await this.service.getOneSolution(id, runId, fetchSpecification),
);
}

@ApiTags(marxanRunFiles)
@Header('Content-Type', 'text/csv')
@ApiOkResponse({
Expand Down
6 changes: 3 additions & 3 deletions api/apps/api/src/modules/scenarios/scenarios.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { ScenarioFeatureSerializer } from './dto/scenario-feature.serializer';
import { CostSurfaceTemplateModule } from './cost-surface-template';
import { SolutionResultCrudService } from './solutions-result/solution-result-crud.service';
import { DbConnections } from '@marxan-api/ormconfig.connections';
import { ScenariosOutputResultsGeoEntity } from '@marxan/scenarios-planning-unit';
import { ScenariosOutputResultsApiEntity, ScenariosPuOutputGeoEntity } from '@marxan/scenarios-planning-unit';
import { ScenarioSolutionSerializer } from './dto/scenario-solution.serializer';
import { CostSurfaceViewModule } from './cost-surface-readmodel/cost-surface-view.module';
import { PlanningUnitsProtectionLevelModule } from '@marxan-api/modules/planning-units-protection-level';
Expand All @@ -44,9 +44,9 @@ import { ZipFilesSerializer } from './dto/zip-files.serializer';
CqrsModule,
ProtectedAreasModule,
forwardRef(() => ProjectsModule),
TypeOrmModule.forFeature([Project, Scenario]),
TypeOrmModule.forFeature([Project, Scenario, ScenariosOutputResultsApiEntity]),
TypeOrmModule.forFeature(
[ScenariosOutputResultsGeoEntity],
[ScenariosPuOutputGeoEntity],
DbConnections.geoprocessingDB,
),
UsersModule,
Expand Down
15 changes: 11 additions & 4 deletions api/apps/api/src/modules/scenarios/scenarios.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ export class ScenariosService {

async findScenarioResults(
scenarioId: string,
runId: string,
fetchSpecification: FetchSpecification,
) {
await this.assertScenario(scenarioId);
Expand Down Expand Up @@ -174,25 +173,33 @@ export class ScenariosService {
await this.crudService.getById(scenarioId);
}

async getBestSolution(scenarioId: string, runId: string) {
async getOneSolution(scenarioId: string, runId: string, fetchSpecification: FetchSpecification) {
await this.assertScenario(scenarioId);
// TODO correct implementation
return this.solutionsCrudService.getById(runId);
}

async getBestSolution(
scenarioId: string,
fetchSpecification: FetchSpecification) {
await this.assertScenario(scenarioId);
// TODO correct implementation
fetchSpecification.filter = {...fetchSpecification.filter, best: true }
return this.solutionsCrudService.findAllPaginated(fetchSpecification);
}

async getMostDifferentSolutions(
scenarioId: string,
runId: string,
fetchSpecification: FetchSpecification,
) {
await this.assertScenario(scenarioId);
// TODO correct implementation
fetchSpecification.filter = {...fetchSpecification.filter, distinctFive: true }
return this.solutionsCrudService.findAllPaginated(fetchSpecification);
}

async findAllSolutionsPaginated(
scenarioId: string,
runId: string,
fetchSpecification: FetchSpecification,
) {
await this.assertScenario(scenarioId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,64 +9,63 @@ import {
import { AppInfoDTO } from '@marxan-api/dto/info.dto';
import { AppConfig } from '@marxan-api/utils/config.utils';
import { FetchSpecification } from 'nestjs-base-service';
import { ScenariosOutputResultsGeoEntity } from '@marxan/scenarios-planning-unit';
import { DbConnections } from '@marxan-api/ormconfig.connections';
import { ScenariosOutputResultsApiEntity } from '@marxan/scenarios-planning-unit';

@Injectable()
export class SolutionResultCrudService extends AppBaseService<
ScenariosOutputResultsGeoEntity,
ScenariosOutputResultsApiEntity,
never,
never,
AppInfoDTO
> {
constructor(
@InjectRepository(
ScenariosOutputResultsGeoEntity,
DbConnections.geoprocessingDB,
ScenariosOutputResultsApiEntity
)
protected readonly repository: Repository<ScenariosOutputResultsGeoEntity>,
protected readonly repository: Repository<ScenariosOutputResultsApiEntity>,
) {
super(repository, 'solution', 'solutions', {
logging: { muteAll: AppConfig.get<boolean>('logging.muteAll', false) },
});
}

get serializerConfig(): JSONAPISerializerConfig<ScenariosOutputResultsGeoEntity> {
get serializerConfig(): JSONAPISerializerConfig<ScenariosOutputResultsApiEntity> {
return {
attributes: [
'id',
'planningUnits',
'runId',
'scoreValue',
'costValue',
'missingValues',
'cost',
'score',
'run',
'planningUnits'
],
keyForAttribute: 'camelCase',
};
}

async extendFindAllResults(
entitiesAndCount: [ScenariosOutputResultsGeoEntity[], number],
entitiesAndCount: [ScenariosOutputResultsApiEntity[], number],
_fetchSpecification?: FetchSpecification,
_info?: AppInfoDTO,
): Promise<[ScenariosOutputResultsGeoEntity[], number]> {
const extendedEntities: Promise<ScenariosOutputResultsGeoEntity>[] = entitiesAndCount[0].map(
): Promise<[ScenariosOutputResultsApiEntity[], number]> {
const extendedEntities: Promise<ScenariosOutputResultsApiEntity>[] = entitiesAndCount[0].map(
(entity) => this.extendGetByIdResult(entity),
);
return [await Promise.all(extendedEntities), entitiesAndCount[1]];
}

async extendGetByIdResult(
entity: ScenariosOutputResultsGeoEntity,
entity: ScenariosOutputResultsApiEntity,
_fetchSpecification?: FetchSpecification,
_info?: AppInfoDTO,
): Promise<ScenariosOutputResultsGeoEntity> {
): Promise<ScenariosOutputResultsApiEntity> {
// TODO implement
entity.scoreValue = 999;
entity.costValue = 400;
entity.planningUnits = 17;
entity.missingValues = 13;
entity.cost = 400;
entity.score = 999;
entity.run = 1;
entity.runId = 1;

return entity;
}
}
32 changes: 27 additions & 5 deletions api/apps/api/test/fixtures/test-data.sql
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,32 @@ VALUES
((SELECT id FROM users WHERE lower(email) = '[email protected]'), (SELECT id FROM projects WHERE name = 'Example Project 2 Org 2'), 'project_user');

INSERT INTO scenarios
(name, project_id, type, wdpa_threshold, created_by)
(name, project_id, type, wdpa_threshold, number_of_runs, blm, created_by)
VALUES
('Example scenario 1 Project 1 Org 1', (select id from projects where name = 'Example Project 1 Org 1'), 'marxan', 30, (SELECT id FROM users WHERE email = '[email protected]') ),
('Example scenario 2 Project 1 Org 1', (select id from projects where name = 'Example Project 1 Org 1'), 'marxan', 50, (SELECT id FROM users WHERE email = '[email protected]') ),
('Example scenario 1 Project 2 Org 2', (select id from projects where name = 'Example Project 2 Org 2'), 'marxan', 30, (SELECT id FROM users WHERE email = '[email protected]') ),
('Example scenario 2 Project 2 Org 2', (select id from projects where name = 'Example Project 2 Org 2'), 'marxan', 50, (SELECT id FROM users WHERE email = '[email protected]') );
('Example scenario 1 Project 1 Org 1', (select id from projects where name = 'Example Project 1 Org 1'), 'marxan', 30, 100, 1, (SELECT id FROM users WHERE email = '[email protected]') ),
('Example scenario 2 Project 1 Org 1', (select id from projects where name = 'Example Project 1 Org 1'), 'marxan', 50, 100, 1, (SELECT id FROM users WHERE email = '[email protected]') ),
('Example scenario 1 Project 2 Org 2', (select id from projects where name = 'Example Project 2 Org 2'), 'marxan', 30, 100, 1, (SELECT id FROM users WHERE email = '[email protected]') ),
('Example scenario 2 Project 2 Org 2', (select id from projects where name = 'Example Project 2 Org 2'), 'marxan', 50, 100, 1, (SELECT id FROM users WHERE email = '[email protected]') );

-- Fake summary outputs
WITH RECURSIVE nums (n) AS (
SELECT 1
UNION ALL
SELECT n+1 FROM nums WHERE n+1 <= 10
)
INSERT INTO output_scenarios_summaries
(run_id, scenario_id, score, "cost", planning_units, connectivity, connectivity_total,
mpm, penalty, shortfall, missing_values, best, distinct_five)
SELECT n as run_id, oss.id as scenario_id,
round(random()*5359200) score,
round(random()*53592) as "cost",
53000 + round(random()*591) as planning_units,
round(random()*53592000) as connectivity,
53592000 as connectivity_total, 1 as mpm,
round(random()*2000) as penalty,
20449 as shortfall,
round(random()) as missing_values,
false as best,
false as distinct_five
FROM nums, scenarios oss
where oss.id=(select id from scenarios where name = 'Example scenario 1 Project 1 Org 1')
Loading

0 comments on commit d4b0611

Please sign in to comment.