Skip to content

Commit

Permalink
feat(api): allow to get scenarios solutions results
Browse files Browse the repository at this point in the history
  • Loading branch information
kgajowy committed Jun 15, 2021
1 parent 4ce5f47 commit 78a43b9
Show file tree
Hide file tree
Showing 8 changed files with 338 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {
ApiExtraModels,
ApiProperty,
getSchemaPath,
refs,
} from '@nestjs/swagger';
import { ScenariosOutputResultsGeoEntity } from '@marxan/scenarios-planning-unit';
import { oneOf } from 'purify-ts';

class ScenarioSolutionsDataDto {
@ApiProperty()
type: 'solutions' = 'solutions';

@ApiProperty()
id!: string;

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

class ScenarioSolutionDataDto {
@ApiProperty()
type: 'solution' = 'solution';

@ApiProperty()
id!: string;

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

@ApiExtraModels(ScenarioSolutionsDataDto, ScenarioSolutionDataDto)
export class ScenarioSolutionResultDto {
@ApiProperty({
oneOf: [
{ $ref: getSchemaPath(ScenarioSolutionsDataDto) },
{ $ref: getSchemaPath(ScenarioSolutionDataDto) },
],
})
data!: ScenarioSolutionsDataDto | ScenarioSolutionDataDto;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Injectable } from '@nestjs/common';
import { PaginationMeta } from '@marxan-api/utils/app-base.service';
import { ScenariosOutputResultsGeoEntity } from '@marxan/scenarios-planning-unit';
import { SolutionResultCrudService } from '../solutions-result/solution-result-crud.service';

@Injectable()
export class ScenarioSolutionSerializer {
constructor(
private readonly scenariosSolutionsCrudService: SolutionResultCrudService,
) {}

async serialize(
entities:
| Partial<ScenariosOutputResultsGeoEntity>
| (Partial<ScenariosOutputResultsGeoEntity> | undefined)[],
paginationMeta?: PaginationMeta,
): Promise<any> {
return this.scenariosSolutionsCrudService.serialize(
entities,
paginationMeta,
);
}
}
56 changes: 56 additions & 0 deletions api/apps/api/src/modules/scenarios/scenarios.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import {
Delete,
Get,
Param,
ParseBoolPipe,
ParseUUIDPipe,
Patch,
Post,
Query,
Req,
UploadedFile,
UseGuards,
Expand Down Expand Up @@ -47,6 +49,10 @@ import { FileInterceptor } from '@nestjs/platform-express';
import { ScenariosService } from './scenarios.service';
import { ScenarioSerializer } from './dto/scenario.serializer';
import { ScenarioFeatureSerializer } from './dto/scenario-feature.serializer';
import { ScenarioFeatureResultDto } from './dto/scenario-feature-result.dto';
import { ScenarioSolutionResultDto } from './dto/scenario-solution-result.dto';
import { ApiImplicitQuery } from '@nestjs/swagger/dist/decorators/api-implicit-query.decorator';
import { ScenarioSolutionSerializer } from './dto/scenario-solution.serializer';

@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
Expand All @@ -57,6 +63,7 @@ export class ScenariosController {
private readonly service: ScenariosService,
private readonly scenarioSerializer: ScenarioSerializer,
private readonly scenarioFeatureSerializer: ScenarioFeatureSerializer,
private readonly scenarioSolutionSerializer: ScenarioSolutionSerializer,
) {}

@ApiOperation({
Expand Down Expand Up @@ -170,4 +177,53 @@ export class ScenariosController {
await this.service.getFeatures(id),
);
}

@ApiOkResponse({
type: ScenarioSolutionResultDto,
})
@ApiImplicitQuery({
name: 'best',
required: false,
type: Boolean,
})
@ApiImplicitQuery({
name: 'most-different',
required: false,
type: Boolean,
})
@JSONAPIQueryParams()
@Get(`:id/marxan/run/:runId/solutions`)
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.scenarioSolutionSerializer.serialize(
await this.service.getBestSolution(id, runId),
);
}
if (selectMostDifferent) {
const result = await this.service.getMostDifferentSolutions(
id,
runId,
fetchSpecification,
);
return this.scenarioSolutionSerializer.serialize(
result.data,
result.metadata,
);
}
const result = await this.service.findAllSolutionsPaginated(
id,
runId,
fetchSpecification,
);
return this.scenarioSolutionSerializer.serialize(
result.data,
result.metadata,
);
}
}
10 changes: 10 additions & 0 deletions api/apps/api/src/modules/scenarios/scenarios.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,21 @@ import { ScenariosService } from './scenarios.service';
import { ScenarioSerializer } from './dto/scenario.serializer';
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 { ScenarioSolutionSerializer } from './dto/scenario-solution.serializer';

@Module({
imports: [
CqrsModule,
ProtectedAreasModule,
forwardRef(() => ProjectsModule),
TypeOrmModule.forFeature([Project, Scenario]),
TypeOrmModule.forFeature(
[ScenariosOutputResultsGeoEntity],
DbConnections.geoprocessingDB,
),
UsersModule,
ScenarioFeaturesModule,
AnalysisModule,
Expand All @@ -39,6 +47,8 @@ import { CostSurfaceTemplateModule } from './cost-surface-template';
WdpaAreaCalculationService,
ScenarioSerializer,
ScenarioFeatureSerializer,
SolutionResultCrudService,
ScenarioSolutionSerializer,
],
controllers: [ScenariosController],
exports: [ScenariosCrudService, ScenariosService],
Expand Down
37 changes: 37 additions & 0 deletions api/apps/api/src/modules/scenarios/scenarios.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ScenariosCrudService } from './scenarios-crud.service';
import { CreateScenarioDTO } from './dto/create.scenario.dto';
import { UpdateScenarioDTO } from './dto/update.scenario.dto';
import { UpdateScenarioPlanningUnitLockStatusDto } from './dto/update-scenario-planning-unit-lock-status.dto';
import { SolutionResultCrudService } from './solutions-result/solution-result-crud.service';

@Injectable()
export class ScenariosService {
Expand All @@ -26,6 +27,7 @@ export class ScenariosService {
private readonly updatePlanningUnits: AdjustPlanningUnits,
private readonly costSurface: CostSurfaceFacade,
private readonly httpService: HttpService,
private readonly solutionsCrudService: SolutionResultCrudService,
) {}

async findAllPaginated(fetchSpecification: FetchSpecification) {
Expand Down Expand Up @@ -103,7 +105,42 @@ export class ScenariosService {
return geoJson;
}

async findScenarioResults(
scenarioId: string,
runId: string,
fetchSpecification: FetchSpecification,
) {
await this.assertScenario(scenarioId);
return this.solutionsCrudService.findAll(fetchSpecification);
}

private async assertScenario(scenarioId: string) {
await this.crudService.getById(scenarioId);
}

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

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

async findAllSolutionsPaginated(
scenarioId: string,
runId: string,
fetchSpecification: FetchSpecification,
) {
await this.assertScenario(scenarioId);
// TODO correct implementation
return this.solutionsCrudService.findAllPaginated(fetchSpecification);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Repository } from 'typeorm';
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import {
AppBaseService,
JSONAPISerializerConfig,
} from '@marxan-api/utils/app-base.service';

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';

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

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

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

async extendGetByIdResult(
entity: ScenariosOutputResultsGeoEntity,
_fetchSpecification?: FetchSpecification,
_info?: AppInfoDTO,
): Promise<ScenariosOutputResultsGeoEntity> {
entity.planningUnits = 17;
entity.missingValues = 13;
entity.cost = 400;
entity.score = 999;
entity.run = 1;
return entity;
}

// TODO DTO (x2)
// TODO params switcher
// TODO serializer
}
1 change: 1 addition & 0 deletions api/libs/scenarios-planning-unit/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { LockStatus } from './lock-status.enum';
export { ScenariosPlanningUnitGeoEntity } from './scenarios-planning-unit.geo.entity';
export { ScenariosOutputResultsGeoEntity } from './scenarios-output-results.geo.entity';
export * from './domain';
Loading

0 comments on commit 78a43b9

Please sign in to comment.