Skip to content

Commit

Permalink
refactor: move get-admin-regions from sql to typeorm AB#7940
Browse files Browse the repository at this point in the history
  • Loading branch information
jannisvisser committed May 28, 2021
1 parent c7ab00a commit beb480d
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 103 deletions.
1 change: 0 additions & 1 deletion database/init.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
CREATE SCHEMA IF NOT EXISTS "IBF-API";
CREATE SCHEMA IF NOT EXISTS "IBF-app";
CREATE EXTENSION "uuid-ossp";
42 changes: 31 additions & 11 deletions interfaces/IBF-dashboard/src/app/services/aggregates.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,20 @@ export class AggregatesService {
indicator: Indicator,
) => {
if (indicator.aggregateIndicator) {
if (indicator.name in feature.properties) {
aggregate[indicator.name] = feature.properties[indicator.name];
} else if (indicator.name in feature.properties.indicators) {
aggregate[indicator.name] =
feature.properties.indicators[indicator.name];
const foundIndicator = feature.records.find(
(a) => a.indicator === indicator.name,
);
if (foundIndicator) {
aggregate[indicator.name] = foundIndicator.value;
} else {
aggregate[indicator.name] = 0;
}
}
};

private onEachAdminFeature = (feature) => {
private onEachPlaceCode = (feature) => {
const aggregate = {
placeCode: feature.properties.placeCode,
placeCode: feature.placeCode,
};

this.indicators.forEach(
Expand All @@ -105,19 +105,39 @@ export class AggregatesService {
return aggregate;
};

private onAdminRegions = (adminRegions) => {
this.aggregates = adminRegions.features.map(this.onEachAdminFeature);
private onAggregatesData = (records) => {
const groupsByPlaceCode = this.aggregateOnPlaceCode(records);
this.aggregates = groupsByPlaceCode.map(this.onEachPlaceCode);
};

private aggregateOnPlaceCode(array) {
const groupsByPlaceCode = [];
array.forEach((record) => {
if (
groupsByPlaceCode.map((i) => i.placeCode).includes(record.placeCode)
) {
groupsByPlaceCode
.find((i) => i.placeCode === record.placeCode)
.records.push(record);
} else {
groupsByPlaceCode.push({
placeCode: record.placeCode,
records: [record],
});
}
});
return groupsByPlaceCode;
}

loadAggregateInformation(): void {
if (this.country) {
this.apiService
.getAdminRegions(
.getAggregatesData(
this.country.countryCodeISO3,
this.timelineService.activeLeadTime,
this.adminLevelService.adminLevel,
)
.subscribe(this.onAdminRegions);
.subscribe(this.onAggregatesData);
}
}

Expand Down
15 changes: 14 additions & 1 deletion interfaces/IBF-dashboard/src/app/services/api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,26 @@ export class ApiService {
adminLevel: AdminLevel = AdminLevel.adm1,
): Observable<GeoJSON.FeatureCollection> {
return this.get(
`admin-areas/per-leadtime/${countryCodeISO3}/${adminLevel}/${
`admin-areas/${countryCodeISO3}/${adminLevel}/${
leadTime ? leadTime : ''
}`,
false,
);
}

getAggregatesData(
countryCodeISO3: string,
leadTime: string,
adminLevel: AdminLevel = AdminLevel.adm1,
): Observable<any> {
return this.get(
`admin-areas/aggregates/${countryCodeISO3}/${adminLevel}/${
leadTime ? leadTime : '{leadTime}'
}`,
false,
);
}

getTriggeredAreas(countryCodeISO3: string) {
return this.get(`event/triggered-areas/${countryCodeISO3}`, false);
}
Expand Down
36 changes: 21 additions & 15 deletions services/API-service/src/api/admin-area/admin-area.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { GeoJson } from '../../shared/geo.model';
import { RolesGuard } from '../../roles.guard';
import { AdminAreaService } from './admin-area.service';
import { AggregateDataRecord } from 'src/shared/data.model';

@ApiBearerAuth()
@UseGuards(RolesGuard)
Expand All @@ -20,37 +21,42 @@ export class AdminAreaController {
this.adminAreaService = adminAreaService;
}

// NOTE: this endpoint is to be used by the IBF-pipeline to read this data from DB (instead of current way > TO DO)
@ApiOperation({
summary: 'Get admin-areas by country',
summary: 'Get admin-areas by country raw',
})
@ApiParam({ name: 'countryCodeISO3', required: true, type: 'string' })
@Get(':countryCodeISO3')
public async getAdminAreas(@Param() params): Promise<any[]> {
return await this.adminAreaService.getAdminAreas(params.countryCodeISO3);
@Get('raw/:countryCodeISO3')
public async getAdminAreasRaw(@Param() params): Promise<any[]> {
return await this.adminAreaService.getAdminAreasRaw(params.countryCodeISO3);
}

@ApiOperation({ summary: 'Get admin-area by leadTime' })
@ApiOperation({
summary: 'Get admin-areas by country as geojson for dashboard',
})
@ApiParam({ name: 'countryCodeISO3', required: true, type: 'string' })
@ApiParam({ name: 'leadTime', required: false, type: 'string' })
@ApiParam({ name: 'adminLevel', required: true, type: 'number' })
@Get('per-leadtime/:countryCodeISO3/:adminLevel/:leadTime?')
public async getAdminAreaData(@Param() params): Promise<GeoJson> {
return await this.adminAreaService.getAdminAreasPerLeadTime(
@Get(':countryCodeISO3/:adminLevel/:leadTime?')
public async getAdminAreas(@Param() params): Promise<GeoJson> {
return await this.adminAreaService.getAdminAreas(
params.countryCodeISO3,
params.leadTime,
params.adminLevel,
);
}

@ApiOperation({
summary: 'Get Glofas station to admin-area mapping by country',
})
@ApiOperation({ summary: 'Get admin-area by leadTime' })
@ApiParam({ name: 'countryCodeISO3', required: true, type: 'string' })
@Get('station-mapping/:countryCodeISO3')
public async getStationMapping(@Param() params): Promise<any[]> {
return await this.adminAreaService.getStationAdminAreaMappingByCountry(
@ApiParam({ name: 'leadTime', required: false, type: 'string' })
@ApiParam({ name: 'adminLevel', required: true, type: 'number' })
@Get('aggregates/:countryCodeISO3/:adminLevel/:leadTime?')
public async getAggregatesData(
@Param() params,
): Promise<AggregateDataRecord[]> {
return await this.adminAreaService.getAggregatesData(
params.countryCodeISO3,
params.leadTime,
params.adminLevel,
);
}
}
135 changes: 107 additions & 28 deletions services/API-service/src/api/admin-area/admin-area.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { InjectRepository } from '@nestjs/typeorm';
import { GeoJson } from '../../shared/geo.model';
import { HelperService } from '../../shared/helper.service';
import { EntityManager, Repository } from 'typeorm';
import fs from 'fs';
import { LeadTime } from '../admin-area-dynamic-data/enum/lead-time.enum';
import { AdminAreaEntity } from './admin-area.entity';
import { CountryEntity } from '../country/country.entity';
import { EventService } from '../event/event.service';
import { AdminAreaRecord } from 'src/shared/data.model';
import { AggregateDataRecord } from 'src/shared/data.model';
import { AdminAreaDynamicDataEntity } from '../admin-area-dynamic-data/admin-area-dynamic-data.entity';
import { AdminAreaDataEntity } from '../admin-area-data/admin-area-data.entity';

@Injectable()
export class AdminAreaService {
Expand All @@ -32,60 +33,138 @@ export class AdminAreaService {
this.eventService = eventService;
}

public async getAdminAreas(countryCodeISO3): Promise<any[]> {
public async getAdminAreasRaw(countryCodeISO3): Promise<any[]> {
return await this.adminAreaRepository.find({
select: ['countryCodeISO3', 'name', 'placeCode', 'geom'],
select: ['countryCodeISO3', 'name', 'placeCode', 'geom', 'glofasStation'],
where: { countryCodeISO3: countryCodeISO3 },
});
}

public async getAdminAreasPerLeadTime(
private async getTriggeredPlaceCodes(
countryCodeISO3: string,
leadTime: string,
adminLevel: number,
): Promise<GeoJson> {
) {
console.log('getTriggeredPlaceCodes: ', countryCodeISO3);
if (!leadTime) {
leadTime = await this.getDefaultLeadTime(countryCodeISO3);
}
const trigger = (
await this.eventService.getTriggerPerLeadtime(countryCodeISO3)
)[leadTime];

let placeCodes;
let placeCodes = [];
if (parseInt(trigger) === 1) {
placeCodes = (
await this.eventService.getTriggeredAreas(countryCodeISO3)
).map((triggeredArea): string => "'" + triggeredArea.placeCode + "'");
).map((triggeredArea): string => triggeredArea.placeCode);
}
return placeCodes;
}

const baseQuery = fs
.readFileSync('./src/api/admin-area/sql/get-admin-regions.sql')
.toString();
const query = baseQuery.concat(
placeCodes && placeCodes.length > 0
? ' and geo."placeCode" in (' + placeCodes.toString() + ')'
: '',
public async getAggregatesData(
countryCodeISO3: string,
leadTime: string,
adminLevel: number,
): Promise<AggregateDataRecord[]> {
console.log('getAggregatesData: ', countryCodeISO3);
const placeCodes = await this.getTriggeredPlaceCodes(
countryCodeISO3,
leadTime,
);

const adminAreas: AdminAreaRecord[] = await this.manager.query(query, [
let staticIndicatorsScript = this.adminAreaRepository
.createQueryBuilder('area')
.select(['area."placeCode"'])
.leftJoin(AdminAreaDataEntity, 'data', 'area.placeCode = data.placeCode')
.addSelect(['data."indicator"', 'data."value"'])
.where('area."countryCodeISO3" = :countryCodeISO3', {
countryCodeISO3: countryCodeISO3,
})
.andWhere('area."adminLevel" = :adminLevel', { adminLevel: adminLevel });
if (placeCodes.length) {
staticIndicatorsScript = staticIndicatorsScript.andWhere(
'area."placeCode" IN (:...placeCodes)',
{ placeCodes: placeCodes },
);
}
const staticIndicators = await staticIndicatorsScript.getRawMany();

let dynamicIndicatorsScript = this.adminAreaRepository
.createQueryBuilder('area')
.select(['area."placeCode"'])
.leftJoin(
AdminAreaDynamicDataEntity,
'dynamic',
'area.placeCode = dynamic.placeCode',
)
.addSelect(['dynamic."indicator"', 'dynamic."value"'])
.where('area."countryCodeISO3" = :countryCodeISO3', {
countryCodeISO3: countryCodeISO3,
})
.andWhere('date = current_date')
.andWhere('dynamic."leadTime" = :leadTime', { leadTime: leadTime })
.andWhere('area."adminLevel" = :adminLevel', { adminLevel: adminLevel });
if (placeCodes.length) {
dynamicIndicatorsScript = dynamicIndicatorsScript.andWhere(
'area."placeCode" IN (:...placeCodes)',
{ placeCodes: placeCodes },
);
}
const dynamicIndicators = await dynamicIndicatorsScript.getRawMany();

return staticIndicators.concat(dynamicIndicators);
}

public async getAdminAreas(
countryCodeISO3: string,
leadTime: string,
adminLevel: number,
): Promise<GeoJson> {
console.log('getAdminAreas: ', countryCodeISO3);
const placeCodes = await this.getTriggeredPlaceCodes(
countryCodeISO3,
leadTime,
adminLevel,
]);
);

return this.helperService.toGeojson(adminAreas);
}
let adminAreasScript = this.adminAreaRepository
.createQueryBuilder('area')
.select([
'area."placeCode"',
'area."name"',
'ST_AsGeoJSON(area.geom)::json As geom',
'area."countryCodeISO3"',
])
.leftJoin(
AdminAreaDynamicDataEntity,
'dynamic',
'area.placeCode = dynamic.placeCode',
)
.addSelect([
'dynamic.value AS "population_affected"',
'dynamic."leadTime"',
'dynamic."date"',
])
.where('area."countryCodeISO3" = :countryCodeISO3', {
countryCodeISO3: countryCodeISO3,
})
.andWhere('dynamic."leadTime" = :leadTime', { leadTime: leadTime })
.andWhere('area."adminLevel" = :adminLevel', { adminLevel: adminLevel })
.andWhere('date = current_date');

public async getStationAdminAreaMappingByCountry(
countryCodeISO3,
): Promise<any[]> {
return await this.adminAreaRepository.find({
select: ['countryCodeISO3', 'name', 'placeCode', 'glofasStation'],
where: { countryCodeISO3: countryCodeISO3 },
});
if (placeCodes.length) {
adminAreasScript = adminAreasScript.andWhere(
'area."placeCode" IN (:...placeCodes)',
{ placeCodes: placeCodes },
);
}

const adminAreas = await adminAreasScript.getRawMany();

return this.helperService.toGeojson(adminAreas);
}

private async getDefaultLeadTime(countryCodeISO3: string): Promise<string> {
console.log('getDefaultLeadTime: ', countryCodeISO3);
const findOneOptions = {
countryCodeISO3: countryCodeISO3,
};
Expand Down
44 changes: 0 additions & 44 deletions services/API-service/src/api/admin-area/sql/get-admin-regions.sql

This file was deleted.

Loading

0 comments on commit beb480d

Please sign in to comment.