From 5170dc7971c6c7ff634c94ea9eaa48544f9b6e25 Mon Sep 17 00:00:00 2001 From: Maciej Ziarkowski Date: Tue, 3 Dec 2019 17:30:51 +0000 Subject: [PATCH] Improve land use processing backend --- app/package-lock.json | 6 ++ app/package.json | 1 + app/src/api/services/building.ts | 2 +- .../currentLandUseClassifications.ts | 73 +++++++++++++------ .../domainLogic/processBuildingUpdate.ts | 4 +- app/src/helpers.ts | 7 ++ 6 files changed, 67 insertions(+), 26 deletions(-) diff --git a/app/package-lock.json b/app/package-lock.json index 8913e572..4c1740ad 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1097,6 +1097,12 @@ "@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": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@types/mapbox__sphericalmercator/-/mapbox__sphericalmercator-1.1.3.tgz", diff --git a/app/package.json b/app/package.json index e1048cfd..030b155a 100644 --- a/app/package.json +++ b/app/package.json @@ -42,6 +42,7 @@ "@types/express": "^4.17.0", "@types/express-session": "^1.15.13", "@types/jest": "^24.0.17", + "@types/lodash": "^4.14.149", "@types/mapbox__sphericalmercator": "^1.1.3", "@types/node": "^8.10.52", "@types/nodemailer": "^6.2.1", diff --git a/app/src/api/services/building.ts b/app/src/api/services/building.ts index 3992a6ee..38bbc082 100644 --- a/app/src/api/services/building.ts +++ b/app/src/api/services/building.ts @@ -158,7 +158,7 @@ async function getBuildingUPRNsById(id: number) { async function saveBuilding(buildingId: number, building: any, userId: string) { // TODO add proper building type try { return await updateBuildingData(buildingId, userId, async () => { - const processedBuilding = await processBuildingUpdate(building); + const processedBuilding = await processBuildingUpdate(buildingId, building); // remove read-only fields from consideration delete processedBuilding.building_id; diff --git a/app/src/api/services/domainLogic/currentLandUseClassifications.ts b/app/src/api/services/domainLogic/currentLandUseClassifications.ts index 77fee603..30e10709 100644 --- a/app/src/api/services/domainLogic/currentLandUseClassifications.ts +++ b/app/src/api/services/domainLogic/currentLandUseClassifications.ts @@ -1,48 +1,73 @@ +import * as _ from 'lodash'; + import db from '../../../db'; -import { getBuildingById, getCurrentBuildingDataById } from '../building'; - -export async function processCurrentLandUseClassifications(building: any): Promise { +import { isNullishOrEmpty } from '../../../helpers'; +import { getCurrentBuildingDataById } from '../building'; +export async function processCurrentLandUseClassifications(buildingId: number, building: any): Promise { + 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 currentData = await getCurrentBuildingDataById(building.building_id); - if(updateFrom === 'class') { - building.current_landuse_group = await deriveGroupFromClass(building.current_landuse_class); - building.current_landuse_order = await deriveOrderFromGroup(building.current_landuse_group); + updateData.current_landuse_class = building.current_landuse_class; + updateData.current_landuse_group = await deriveGroupFromClass(updateData.current_landuse_class); + updateData.current_landuse_order = await deriveOrderFromGroup(updateData.current_landuse_group); } else if (updateFrom === 'group') { - if(currentData.current_landuse_class == undefined) { - building.current_landuse_order = await deriveOrderFromGroup(building.current_landuse_group); + if (isNullishOrEmpty(updateData.current_landuse_class)) { + updateData.current_landuse_group = building.current_landuse_group; + updateData.current_landuse_order = await deriveOrderFromGroup(building.current_landuse_group); } else { - delete building.current_landuse_group; - delete building.current_landuse_order; + throw new Error('Trying to update current_landuse_group field but a more detailed field (current_landuse_class) is already filled'); } } else if (updateFrom === 'order') { - if(currentData.current_landuse_class == undefined && currentData.current_landuse_group == undefined) { - // do nothing, building.current_landuse_order is already correctly set and can be used for the update + if (isNullishOrEmpty(updateData.current_landuse_class) && isNullishOrEmpty(updateData.current_landuse_group)) { + updateData.current_landuse_order = building.current_landuse_order; } 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. * @param building */ function getUpdateStartingStage(building) { - if(building.hasOwnProperty('current_landuse_class')) { + if(building.hasOwnProperty('current_landuse_class') && !isNullishOrEmpty(building.current_landuse_class)) { return 'class'; - } else if(building.hasOwnProperty('current_landuse_group')) { + } else if(building.hasOwnProperty('current_landuse_group') && !isNullishOrEmpty(building.current_landuse_group)) { return 'group'; - } else if(building.hasOwnProperty('current_landuse_order')) { + } else if(building.hasOwnProperty('current_landuse_order') && !isNullishOrEmpty(building.current_landuse_order)) { return 'order'; } else return 'none'; } -function deriveGroupFromClass(classes: string[]): Promise { - return db.many( +async function deriveGroupFromClass(classes: string[]): Promise { + if (classes.length === 0) return []; + + return (await db.many( ` SELECT DISTINCT parent.description FROM reference_tables.buildings_landuse_group AS parent @@ -51,11 +76,13 @@ function deriveGroupFromClass(classes: string[]): Promise { WHERE child.description IN ($1:csv) ORDER BY parent.description`, [classes] - ); + )).map(x => x.description); } async function deriveOrderFromGroup(groups: string[]): Promise { - const orders = await db.many( + if(groups.length === 0) return null; + + const orders = (await db.many( ` SELECT DISTINCT parent.description FROM reference_tables.buildings_landuse_order AS parent @@ -65,7 +92,7 @@ async function deriveOrderFromGroup(groups: string[]): Promise { ORDER BY parent.description `, [groups] - ); + )).map(x => x.description); if(orders.length === 1) { return orders[0]; diff --git a/app/src/api/services/domainLogic/processBuildingUpdate.ts b/app/src/api/services/domainLogic/processBuildingUpdate.ts index 0055b021..7b7d166f 100644 --- a/app/src/api/services/domainLogic/processBuildingUpdate.ts +++ b/app/src/api/services/domainLogic/processBuildingUpdate.ts @@ -2,9 +2,9 @@ import { hasAnyOwnProperty } from '../../../helpers'; import { processCurrentLandUseClassifications } from './currentLandUseClassifications'; -export async function processBuildingUpdate(building: T): Promise { +export async function processBuildingUpdate(buildingId: number, building: any): Promise { if(hasAnyOwnProperty(building, ['current_landuse_class', 'current_landuse_group', 'current_landuse_order'])) { - building = await processCurrentLandUseClassifications(building); + building = await processCurrentLandUseClassifications(buildingId, building); } return building; diff --git a/app/src/helpers.ts b/app/src/helpers.ts index e9ff3bc3..372001e1 100644 --- a/app/src/helpers.ts +++ b/app/src/helpers.ts @@ -27,3 +27,10 @@ export function parseJsonOrDefault(jsonString: string) { export function hasAnyOwnProperty(obj: {}, keys: string[]) { 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; +}