-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(api): scenarios: cost-sufrace marxan data
* feat(api): scenarios: cost-sufrace marxan data * chore(api): scenario-cost-surface * refactor(api): use relative import inside module
- Loading branch information
Showing
17 changed files
with
286 additions
and
14 deletions.
There are no files selected for viewing
25 changes: 25 additions & 0 deletions
25
api/apps/api/src/modules/scenarios/cost-surface-readmodel/cost-surface-view.module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { CostSurfaceViewService } from './cost-surface-view.service'; | ||
import { TypeOrmModule } from '@nestjs/typeorm'; | ||
import { PlanningUnitsGeom } from '@marxan-jobs/planning-unit-geometry'; | ||
import { | ||
ScenariosPlanningUnitGeoEntity, | ||
ScenariosPuCostDataGeo, | ||
} from '@marxan/scenarios-planning-unit'; | ||
import { DbConnections } from '@marxan-api/ormconfig.connections'; | ||
|
||
@Module({ | ||
imports: [ | ||
TypeOrmModule.forFeature( | ||
[ | ||
PlanningUnitsGeom, | ||
ScenariosPlanningUnitGeoEntity, | ||
ScenariosPuCostDataGeo, | ||
], | ||
DbConnections.geoprocessingDB, | ||
), | ||
], | ||
providers: [CostSurfaceViewService], | ||
exports: [CostSurfaceViewService], | ||
}) | ||
export class CostSurfaceViewModule {} |
57 changes: 57 additions & 0 deletions
57
api/apps/api/src/modules/scenarios/cost-surface-readmodel/cost-surface-view.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import * as stream from 'stream'; | ||
import { InjectRepository } from '@nestjs/typeorm'; | ||
import { Repository } from 'typeorm'; | ||
import { DbConnections } from '@marxan-api/ormconfig.connections'; | ||
import { ScenariosPlanningUnitGeoEntity } from '@marxan/scenarios-planning-unit'; | ||
|
||
@Injectable() | ||
export class CostSurfaceViewService { | ||
readonly #separator = '\t'; | ||
|
||
constructor( | ||
@InjectRepository( | ||
ScenariosPlanningUnitGeoEntity, | ||
DbConnections.geoprocessingDB, | ||
) | ||
private readonly spuDataRepo: Repository<ScenariosPlanningUnitGeoEntity>, | ||
) {} | ||
|
||
async read( | ||
scenarioId: string, | ||
responseStream: stream.Writable, | ||
): Promise<void> { | ||
responseStream.write([`id`, `cost`, `status`].join(this.#separator)); | ||
|
||
const query = await this.spuDataRepo | ||
.createQueryBuilder('spu') | ||
.select(['spu.puid', 'spu.lockin_status', 'spucd.cost']) | ||
.leftJoin( | ||
`scenarios_pu_cost_data`, | ||
`spucd`, | ||
`spucd.scenarios_pu_data_id = spu.id`, | ||
) | ||
.where(`spu.scenario_id = :scenarioId`, { scenarioId }); | ||
|
||
const queryStream = await query.stream(); | ||
// "pipe" does not seem to trigger | ||
queryStream.on( | ||
'data', | ||
(data: { | ||
puid: number; | ||
lockin_status: number | null; | ||
spucd_cost: number | null; | ||
}) => { | ||
const tsvRow = [data.puid, data.spucd_cost, data.lockin_status].join( | ||
this.#separator, | ||
); | ||
responseStream.write(`\n`); | ||
responseStream.write(tsvRow); | ||
}, | ||
); | ||
|
||
queryStream.on('end', () => { | ||
responseStream.end(); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
api/apps/api/test/scenario-cost-surface/scenario-cost-surface.e2e-spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { PromiseType } from 'utility-types'; | ||
import { createWorld } from './world'; | ||
|
||
let world: PromiseType<ReturnType<typeof createWorld>>; | ||
|
||
beforeAll(async () => { | ||
world = await createWorld(); | ||
}); | ||
|
||
describe(`When scenario has PUs with cost and lock status`, () => { | ||
beforeAll(async () => { | ||
await world.GivenScenarioWithPuAndLocks(); | ||
}); | ||
|
||
it(`returns relevant data`, async () => { | ||
const result = await world.WhenGettingMarxanData(); | ||
const [headers, ...costAndStatus] = result.split('\n'); | ||
|
||
expect(headers).toEqual('id\tcost\tstatus'); | ||
expect(costAndStatus).toMatchInlineSnapshot(` | ||
Array [ | ||
"0 200 ", | ||
"1 400 1", | ||
"2 600 2", | ||
"3 800 2", | ||
] | ||
`); | ||
}); | ||
}); | ||
|
||
afterAll(async () => { | ||
await world?.cleanup(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import { bootstrapApplication } from '../utils/api-application'; | ||
import { GivenUserIsLoggedIn } from '../steps/given-user-is-logged-in'; | ||
import * as request from 'supertest'; | ||
import { In, Repository } from 'typeorm'; | ||
import { getRepositoryToken } from '@nestjs/typeorm'; | ||
import { | ||
LockStatus, | ||
ScenariosPlanningUnitGeoEntity, | ||
ScenariosPuCostDataGeo, | ||
} from '@marxan/scenarios-planning-unit'; | ||
import { Polygon } from 'geojson'; | ||
import { DbConnections } from '@marxan-api/ormconfig.connections'; | ||
import { | ||
PlanningUnitsGeom, | ||
ShapeType, | ||
} from '@marxan-jobs/planning-unit-geometry'; | ||
import { GivenProjectExists } from '../steps/given-project'; | ||
import { ScenariosTestUtils } from '../utils/scenarios.test.utils'; | ||
import { ScenarioType } from '@marxan-api/modules/scenarios/scenario.api.entity'; | ||
import { v4 } from 'uuid'; | ||
|
||
export const createWorld = async () => { | ||
const app = await bootstrapApplication(); | ||
const jwt = await GivenUserIsLoggedIn(app); | ||
const { cleanup: projectCleanup, projectId } = await GivenProjectExists( | ||
app, | ||
jwt, | ||
); | ||
const scenarioId = ( | ||
await ScenariosTestUtils.createScenario(app, jwt, { | ||
name: `scenario-name`, | ||
type: ScenarioType.marxan, | ||
projectId, | ||
}) | ||
).data.id; | ||
const geometries: string[] = []; | ||
const scenariosPuData: string[] = []; | ||
|
||
const puGeometryRepo: Repository<PlanningUnitsGeom> = app.get( | ||
getRepositoryToken(PlanningUnitsGeom, DbConnections.geoprocessingDB), | ||
); | ||
const scenarioPuDataRepo: Repository<ScenariosPlanningUnitGeoEntity> = app.get( | ||
getRepositoryToken( | ||
ScenariosPlanningUnitGeoEntity, | ||
DbConnections.geoprocessingDB, | ||
), | ||
); | ||
const scenarioPuDataCostRepo: Repository<ScenariosPuCostDataGeo> = app.get( | ||
getRepositoryToken(ScenariosPuCostDataGeo, DbConnections.geoprocessingDB), | ||
); | ||
|
||
return { | ||
cleanup: async () => { | ||
await ScenariosTestUtils.deleteScenario(app, jwt, scenarioId); | ||
await projectCleanup(); | ||
await scenarioPuDataCostRepo.delete({ | ||
scenariosPuDataId: In(scenariosPuData), | ||
}); | ||
await puGeometryRepo.delete({ | ||
id: In(geometries), | ||
}); | ||
await scenarioPuDataRepo.delete({ | ||
scenarioId, | ||
}); | ||
await app.close(); | ||
}, | ||
GivenScenarioWithPuAndLocks: async () => { | ||
const polygons: Polygon[] = [1, 2, 3, 4].map((i) => ({ | ||
type: 'Polygon', | ||
coordinates: [ | ||
[ | ||
[0, i], | ||
[i, i], | ||
[i, 0], | ||
[0, 0], | ||
[0, i], | ||
], | ||
], | ||
})); | ||
const geoRows = ( | ||
await puGeometryRepo.insert( | ||
polygons.map((poly) => ({ | ||
theGeom: () => | ||
`st_multi(ST_GeomFromGeoJSON('${JSON.stringify(poly)}'))`, | ||
type: ShapeType.Square, | ||
})), | ||
) | ||
).identifiers; | ||
|
||
geometries.push(...geoRows.map((geo) => geo.id)); | ||
const scenarioPuData = await scenarioPuDataRepo.save( | ||
geometries.map((id, index) => | ||
scenarioPuDataRepo.create({ | ||
puGeometryId: id, | ||
scenarioId, | ||
planningUnitMarxanId: index, | ||
lockStatus: | ||
index === 0 | ||
? LockStatus.Unstated | ||
: index === 1 | ||
? LockStatus.LockedIn | ||
: LockStatus.LockedOut, | ||
}), | ||
), | ||
); | ||
scenariosPuData.push(...scenarioPuData.map((spud) => spud.id)); | ||
await scenarioPuDataCostRepo.save( | ||
scenarioPuData.map((spud, index) => ({ | ||
cost: (index + 1) * 200, | ||
scenariosPuDataId: spud.id, | ||
scenariosPlanningUnit: spud, | ||
planningUnitId: v4(), | ||
})), | ||
); | ||
}, | ||
WhenGettingMarxanData: async () => | ||
( | ||
await request(app.getHttpServer()) | ||
.get(`/api/v1/scenarios/${scenarioId}/marxan/dat/pu.dat`) | ||
.set('Authorization', `Bearer ${jwt}`) | ||
).text, | ||
}; | ||
}; |
2 changes: 1 addition & 1 deletion
2
api/apps/geoprocessing/src/modules/planning-units/planning-units.module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.