Improve land use processing backend

This commit is contained in:
Maciej Ziarkowski 2019-12-03 17:30:51 +00:00
parent c3b5c51da7
commit 5170dc7971
6 changed files with 67 additions and 26 deletions

6
app/package-lock.json generated
View File

@ -1097,6 +1097,12 @@
"@types/geojson": "*" "@types/geojson": "*"
} }
}, },
"@types/lodash": {
"version": "4.14.149",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz",
"integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==",
"dev": true
},
"@types/mapbox__sphericalmercator": { "@types/mapbox__sphericalmercator": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/@types/mapbox__sphericalmercator/-/mapbox__sphericalmercator-1.1.3.tgz", "resolved": "https://registry.npmjs.org/@types/mapbox__sphericalmercator/-/mapbox__sphericalmercator-1.1.3.tgz",

View File

@ -42,6 +42,7 @@
"@types/express": "^4.17.0", "@types/express": "^4.17.0",
"@types/express-session": "^1.15.13", "@types/express-session": "^1.15.13",
"@types/jest": "^24.0.17", "@types/jest": "^24.0.17",
"@types/lodash": "^4.14.149",
"@types/mapbox__sphericalmercator": "^1.1.3", "@types/mapbox__sphericalmercator": "^1.1.3",
"@types/node": "^8.10.52", "@types/node": "^8.10.52",
"@types/nodemailer": "^6.2.1", "@types/nodemailer": "^6.2.1",

View File

@ -158,7 +158,7 @@ async function getBuildingUPRNsById(id: number) {
async function saveBuilding(buildingId: number, building: any, userId: string) { // TODO add proper building type async function saveBuilding(buildingId: number, building: any, userId: string) { // TODO add proper building type
try { try {
return await updateBuildingData(buildingId, userId, async () => { return await updateBuildingData(buildingId, userId, async () => {
const processedBuilding = await processBuildingUpdate(building); const processedBuilding = await processBuildingUpdate(buildingId, building);
// remove read-only fields from consideration // remove read-only fields from consideration
delete processedBuilding.building_id; delete processedBuilding.building_id;

View File

@ -1,48 +1,73 @@
import db from '../../../db'; import * as _ from 'lodash';
import { getBuildingById, getCurrentBuildingDataById } from '../building';
export async function processCurrentLandUseClassifications(building: any): Promise<any> { import db from '../../../db';
import { isNullishOrEmpty } from '../../../helpers';
import { getCurrentBuildingDataById } from '../building';
export async function processCurrentLandUseClassifications(buildingId: number, building: any): Promise<any> {
let updateData = _.pick(await getCurrentBuildingDataById(buildingId), [
'current_landuse_class',
'current_landuse_group',
'current_landuse_order'
]);
updateData = Object.assign({}, updateData, getClearValues(building));
const updateFrom = getUpdateStartingStage(building); const updateFrom = getUpdateStartingStage(building);
const currentData = await getCurrentBuildingDataById(building.building_id);
if(updateFrom === 'class') { if(updateFrom === 'class') {
building.current_landuse_group = await deriveGroupFromClass(building.current_landuse_class); updateData.current_landuse_class = building.current_landuse_class;
building.current_landuse_order = await deriveOrderFromGroup(building.current_landuse_group); updateData.current_landuse_group = await deriveGroupFromClass(updateData.current_landuse_class);
updateData.current_landuse_order = await deriveOrderFromGroup(updateData.current_landuse_group);
} else if (updateFrom === 'group') { } else if (updateFrom === 'group') {
if(currentData.current_landuse_class == undefined) { if (isNullishOrEmpty(updateData.current_landuse_class)) {
building.current_landuse_order = await deriveOrderFromGroup(building.current_landuse_group); updateData.current_landuse_group = building.current_landuse_group;
updateData.current_landuse_order = await deriveOrderFromGroup(building.current_landuse_group);
} else { } else {
delete building.current_landuse_group; throw new Error('Trying to update current_landuse_group field but a more detailed field (current_landuse_class) is already filled');
delete building.current_landuse_order;
} }
} else if (updateFrom === 'order') { } else if (updateFrom === 'order') {
if(currentData.current_landuse_class == undefined && currentData.current_landuse_group == undefined) { if (isNullishOrEmpty(updateData.current_landuse_class) && isNullishOrEmpty(updateData.current_landuse_group)) {
// do nothing, building.current_landuse_order is already correctly set and can be used for the update updateData.current_landuse_order = building.current_landuse_order;
} else { } else {
delete building.current_landuse_order; throw new Error('Trying to update current_landuse_order field but a more detailed field (current_landuse_class or current_landuse_group) is already filled');
} }
} }
return building; return Object.assign({}, building, updateData);
} }
function getClearValues(building) {
const clearValues: any = {};
if(building.hasOwnProperty('current_landuse_class') && isNullishOrEmpty(building.current_landuse_class)) {
clearValues.current_landuse_class = [];
}
if(building.hasOwnProperty('current_landuse_group') && isNullishOrEmpty(building.current_landuse_group)) {
clearValues.current_landuse_group = [];
}
if(building.hasOwnProperty('current_landuse_order') && isNullishOrEmpty(building.current_landuse_order)) {
clearValues.current_landuse_order = null;
}
return clearValues;
}
/** /**
* Choose which level of the land use classification hierarchy the update should start from. * Choose which level of the land use classification hierarchy the update should start from.
* @param building * @param building
*/ */
function getUpdateStartingStage(building) { function getUpdateStartingStage(building) {
if(building.hasOwnProperty('current_landuse_class')) { if(building.hasOwnProperty('current_landuse_class') && !isNullishOrEmpty(building.current_landuse_class)) {
return 'class'; return 'class';
} else if(building.hasOwnProperty('current_landuse_group')) { } else if(building.hasOwnProperty('current_landuse_group') && !isNullishOrEmpty(building.current_landuse_group)) {
return 'group'; return 'group';
} else if(building.hasOwnProperty('current_landuse_order')) { } else if(building.hasOwnProperty('current_landuse_order') && !isNullishOrEmpty(building.current_landuse_order)) {
return 'order'; return 'order';
} else return 'none'; } else return 'none';
} }
function deriveGroupFromClass(classes: string[]): Promise<string[]> { async function deriveGroupFromClass(classes: string[]): Promise<string[]> {
return db.many( if (classes.length === 0) return [];
return (await db.many(
` `
SELECT DISTINCT parent.description SELECT DISTINCT parent.description
FROM reference_tables.buildings_landuse_group AS parent FROM reference_tables.buildings_landuse_group AS parent
@ -51,11 +76,13 @@ function deriveGroupFromClass(classes: string[]): Promise<string[]> {
WHERE child.description IN ($1:csv) WHERE child.description IN ($1:csv)
ORDER BY parent.description`, ORDER BY parent.description`,
[classes] [classes]
); )).map(x => x.description);
} }
async function deriveOrderFromGroup(groups: string[]): Promise<string> { async function deriveOrderFromGroup(groups: string[]): Promise<string> {
const orders = await db.many( if(groups.length === 0) return null;
const orders = (await db.many(
` `
SELECT DISTINCT parent.description SELECT DISTINCT parent.description
FROM reference_tables.buildings_landuse_order AS parent FROM reference_tables.buildings_landuse_order AS parent
@ -65,7 +92,7 @@ async function deriveOrderFromGroup(groups: string[]): Promise<string> {
ORDER BY parent.description ORDER BY parent.description
`, `,
[groups] [groups]
); )).map(x => x.description);
if(orders.length === 1) { if(orders.length === 1) {
return orders[0]; return orders[0];

View File

@ -2,9 +2,9 @@ import { hasAnyOwnProperty } from '../../../helpers';
import { processCurrentLandUseClassifications } from './currentLandUseClassifications'; import { processCurrentLandUseClassifications } from './currentLandUseClassifications';
export async function processBuildingUpdate<T>(building: T): Promise<T> { export async function processBuildingUpdate(buildingId: number, building: any): Promise<any> {
if(hasAnyOwnProperty(building, ['current_landuse_class', 'current_landuse_group', 'current_landuse_order'])) { if(hasAnyOwnProperty(building, ['current_landuse_class', 'current_landuse_group', 'current_landuse_order'])) {
building = await processCurrentLandUseClassifications(building); building = await processCurrentLandUseClassifications(buildingId, building);
} }
return building; return building;

View File

@ -27,3 +27,10 @@ export function parseJsonOrDefault(jsonString: string) {
export function hasAnyOwnProperty(obj: {}, keys: string[]) { export function hasAnyOwnProperty(obj: {}, keys: string[]) {
return keys.some(k => obj.hasOwnProperty(k)); return keys.some(k => obj.hasOwnProperty(k));
} }
export function isNullishOrEmpty(obj: any) {
return obj == undefined || isEmptyArray(obj);
}
export function isEmptyArray(obj: any) {
return Array.isArray(obj) && obj.length === 0;
}