From eb2782945b15c41aa04900ab0f48555d86d8ba9a Mon Sep 17 00:00:00 2001 From: rbsolis Date: Thu, 20 Jun 2019 11:03:05 +0200 Subject: [PATCH 01/11] Added new option to show dates at the same format as showed in the front. This is used ad csv files, but can be used isolated as well. --- models/devicesmodel.js | 7 ++++- protools/devicesformatter.js | 56 ++++++++++++++++++++++++------------ routes/devices.js | 5 +++- 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/models/devicesmodel.js b/models/devicesmodel.js index 533a074..1179e1f 100644 --- a/models/devicesmodel.js +++ b/models/devicesmodel.js @@ -427,7 +427,12 @@ DevicesModel.prototype.getDevicesRawData = function(opts, cb) { return Promise.all(promises); }).bind(this)) .then(function(results) { - return new DevicesFormatter().rawData(results); + if (opts.format_tz && opts.format) { + return new DevicesFormatter().parsedData(results); + } else { + return new DevicesFormatter().rawData(results); + } + }) .catch(function(err) { return Promise.reject(err); diff --git a/protools/devicesformatter.js b/protools/devicesformatter.js index 2442598..7049e88 100644 --- a/protools/devicesformatter.js +++ b/protools/devicesformatter.js @@ -1,26 +1,27 @@ -// Copyright 2017 Telefónica Digital España S.L. -// -// This file is part of UrboCore API. -// -// UrboCore API is free software: you can redistribute it and/or -// modify it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// UrboCore API is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero -// General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with UrboCore API. If not, see http://www.gnu.org/licenses/. -// -// For those usages not covered by this license please contact with +// Copyright 2017 Telefónica Digital España S.L. +// +// This file is part of UrboCore API. +// +// UrboCore API is free software: you can redistribute it and/or +// modify it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// UrboCore API is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +// General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with UrboCore API. If not, see http://www.gnu.org/licenses/. +// +// For those usages not covered by this license please contact with // iot_support at tid dot es 'use strict'; var _ = require('underscore'); +var moment = require('moment-timezone'); var utils = require('../utils'); var BaseFormatter = require('./baseformatter'); var log = utils.log(); @@ -47,6 +48,25 @@ class DevicesFormatter extends BaseFormatter { return Promise.resolve(rslts); } + parsedData(results) { + + + var rslts = []; + results.forEach(function(rslt) { + rslts.push(rslt.rows); + }); + if (rslts.length === 1) { + rslts = rslts[0]; + } + else { + rslts = _.union(_.flatten(rslts)); + } + rslts.forEach(function(rsl) { + rsl.time = moment.tz(rsl.time, 'Europe/Madrid').format('DD/MM/YYYY HH:mm'); + }); + return Promise.resolve(rslts); + } + } module.exports = DevicesFormatter; diff --git a/routes/devices.js b/routes/devices.js index 973c932..4ba8ef1 100644 --- a/routes/devices.js +++ b/routes/devices.js @@ -97,6 +97,7 @@ router.get('/:id_entity/:id_device/lastdata', log.error('Device info: Error when selecting data'); next(err); } else { + print(r); res.json(r); } }); @@ -123,6 +124,7 @@ router.post('/:id_entity/:id_device/raw', let start = req.body.time.start; let finish = req.body.time.finish; let format = req.body.format; + let format_tz = req.body.format_tz; var opts = { scope: req.scope, @@ -132,7 +134,8 @@ router.post('/:id_entity/:id_device/raw', finish: finish, id_vars: req.body.vars, filters: req.body.filters||{}, - format: format + format: format, + format_tz: format_tz || '' }; var model = new DevicesModel(); From 5461cea55e44962ff69e68e260fb1fc1fcbdb0cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Thu, 20 Jun 2019 12:05:17 +0200 Subject: [PATCH 02/11] Entity search extended added --- models/entitiesmodel.js | 71 ++++++++++++++++++++++++++++++----------- protools/conditions.js | 40 ++++++++++++----------- routes/entities.js | 62 ++++++++++++++++++++++++----------- 3 files changed, 119 insertions(+), 54 deletions(-) diff --git a/models/entitiesmodel.js b/models/entitiesmodel.js index 149f621..e1b1eeb 100644 --- a/models/entitiesmodel.js +++ b/models/entitiesmodel.js @@ -1,21 +1,21 @@ -// Copyright 2017 Telefónica Digital España S.L. -// -// This file is part of UrboCore API. -// -// UrboCore API is free software: you can redistribute it and/or -// modify it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// UrboCore API is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero -// General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with UrboCore API. If not, see http://www.gnu.org/licenses/. -// -// For those usages not covered by this license please contact with +// Copyright 2017 Telefónica Digital España S.L. +// +// This file is part of UrboCore API. +// +// UrboCore API is free software: you can redistribute it and/or +// modify it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// UrboCore API is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +// General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with UrboCore API. If not, see http://www.gnu.org/licenses/. +// +// For those usages not covered by this license please contact with // iot_support at tid dot es 'use strict'; @@ -30,6 +30,7 @@ var utils = require('../utils'); var modelutils = require('./modelutils.js'); var log = utils.log(); var auth = require('../auth.js'); +var QueryBuilder = require('../protools/querybuilder'); function EntitiesModel(cfg) { PGSQLModel.call(this,cfg); @@ -233,6 +234,40 @@ EntitiesModel.prototype.searchElements = function(opts,cb) { }).bind(this)); } +EntitiesModel.prototype.searchElementsExtended = function(scope, entities) { + const metadata = new MetadataInstanceModel(); + return metadata + .getEntsForSearch(scope, Object.keys(entities)) + .then(function(d) { + + const promises = d.rows.map(((entityData) => { + + const entitySelect = entities[entityData.id_entity].select; + + const qb = new QueryBuilder(entities[entityData.id_entity]); + const queryFilter = `${qb.bbox()} ${qb.filter()}`; + + const prefix = entities[entityData.id_entity].prefix; + return this.cachedQuery(` + SELECT DISTINCT ON (id_entity) ${entitySelect.join(', ')} + FROM ${entityData.dbschema}.${entityData.entity_table_name}${prefix} + WHERE TRUE ${queryFilter} + `); + }).bind(this)); + + return Promise.all(promises) + .then(dataResult => { + // group result by id_entity + return Promise.resolve(_.reduce(dataResult, (result, r, i) => { + return Object.assign(result, {[d.rows[i].id_entity]: r.rows}); + }, {})); + }); + }.bind(this)) + .catch(function(err) { + return Promise.reject(err); + }); +} + EntitiesModel.prototype._getNonRegisteredEntities = function(term,limit) { var qemp = ['(SELECT \'placement\' as type,park_name as name,id as element_id,BOX2D(the_geom) as pbbox', 'FROM parques WHERE park_name ILIKE unaccent(\'%'+term+'%\')', diff --git a/protools/conditions.js b/protools/conditions.js index 4d92460..12019ab 100644 --- a/protools/conditions.js +++ b/protools/conditions.js @@ -1,21 +1,21 @@ -// Copyright 2017 Telefónica Digital España S.L. -// -// This file is part of UrboCore API. -// -// UrboCore API is free software: you can redistribute it and/or -// modify it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// UrboCore API is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero -// General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with UrboCore API. If not, see http://www.gnu.org/licenses/. -// -// For those usages not covered by this license please contact with +// Copyright 2017 Telefónica Digital España S.L. +// +// This file is part of UrboCore API. +// +// UrboCore API is free software: you can redistribute it and/or +// modify it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// UrboCore API is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +// General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with UrboCore API. If not, see http://www.gnu.org/licenses/. +// +// For those usages not covered by this license please contact with // iot_support at tid dot es 'use strict'; @@ -92,6 +92,8 @@ Condition.prototype.getOp = function() { return 'LIKE'; } else if (this.filter === 'regex') { return '~'; + } else if (this.filter === 'icontains') { + return 'ILIKE'; } } @@ -111,6 +113,8 @@ Condition.prototype.toSQL = function() { return ' false '; } else return ''; + } else if (this.filter === 'icontains') { + return ' ' + this.getFullColumn() + ' ' + this.getOp() + ' unaccent(\'%' + this.getValue() + '%\') '; } return ' ' + this.getFullColumn() + ' ' + this.getOp() + ' \'' + this.getValue() + '\' '; } diff --git a/routes/entities.js b/routes/entities.js index ed985c9..3ae94a8 100644 --- a/routes/entities.js +++ b/routes/entities.js @@ -1,21 +1,21 @@ -// Copyright 2017 Telefónica Digital España S.L. -// -// This file is part of UrboCore API. -// -// UrboCore API is free software: you can redistribute it and/or -// modify it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// UrboCore API is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero -// General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with UrboCore API. If not, see http://www.gnu.org/licenses/. -// -// For those usages not covered by this license please contact with +// Copyright 2017 Telefónica Digital España S.L. +// +// This file is part of UrboCore API. +// +// UrboCore API is free software: you can redistribute it and/or +// modify it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// UrboCore API is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +// General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with UrboCore API. If not, see http://www.gnu.org/licenses/. +// +// For those usages not covered by this license please contact with // iot_support at tid dot es 'use strict'; @@ -106,4 +106,30 @@ router.get('/search',auth.logged,function(req, res, next) { }); }); +router.post('/search/extended',auth.logged,function(req, res, next) { + let entities = req.body.entities; + + if (entities == null || Object.keys(entities).length === 0) { + return next(utils.error('Bad parameters: Entities required', 400)); + } + + Object.keys(entities).forEach((entity) => { + const entitySelect = entities[entity].select; + if (entitySelect == null || entitySelect.length === 0) { + return next(utils.error('Bad parameters: Entity select required', 400)); + } + const entityFilters = entities[entity].filters; + entities[entity].filters = entityFilters || {'condition': {}}, + entities[entity].bbox = entityFilters ? entityFilters.bbox : undefined; + }); + + const model = new EntitiesModel(); + model.searchElementsExtended(req.scope, entities) + .then(r => res.json(r)) + .catch(err => { + log.error('Search elements: Error when selecting data'); + next(err); + }); +}); + module.exports = router; From 9f4044f5540d7af7180cf2e06b64118af1f7df13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Thu, 20 Jun 2019 12:25:55 +0200 Subject: [PATCH 03/11] Docs added --- docs/reference/entities.md | 73 ++++++++++++++++++++++++++++++++++++++ models/entitiesmodel.js | 4 +-- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/docs/reference/entities.md b/docs/reference/entities.md index 26d5c78..a37b84f 100644 --- a/docs/reference/entities.md +++ b/docs/reference/entities.md @@ -48,6 +48,79 @@ Response: }] ``` +### POST /:scope/entities/search/extended + +Extends the functionalities of `/entities/search` + +Params: +- entities (mandatory): entities to search. It's an object using the entities names as keys and the following attributes as values: + - select (mandatory): fields to retrieve. Array + - suffix (optional): prefix to append to the entity associated table_name. String + - filters (mandatory): the same functionalities used in other endpoints. Object + +Request sample +```json +{ + "entities": { + "fire_detection.fireforestobserved": { + "select": [ + "id_entity", + "hasfirealert" + ], + "prefix": "_lastdata", + "filters": { + "condition": { + "OR": { + "id_entity__icontains": "Node9", + "hasfirealert__eq": 1 + } + } + } + }, + "fire_detection.weatherobserved": { + "select": [ + "id_entity", + "battery" + ], + "prefix": "_lastdata", + "filters": { + "condition": { + "AND": { + "id_entity__icontains": "3A" + } + } + } + } + } +} +``` + +Response: +```json +{ + "fire_detection.fireforestobserved": [ + { + "id_entity": "fireForestObserved:Node9", + "hasfirealert": 0 + }, + { + "id_entity": "fireForestObserved:Simul1", + "hasfirealert": 1 + }, + { + "id_entity": "fireForestObserved:Simul2", + "hasfirealert": 1 + } + ], + "fire_detection.weatherobserved": [ + { + "id_entity": "weatherObserved:Node3A", + "battery": 78.647 + } + ] +} +``` + ### GET /:scope/entities/map/counters It returns the number of elements by entities. If bbox param is specified the 'filter' value is the number of elements inside the viewport. diff --git a/models/entitiesmodel.js b/models/entitiesmodel.js index e1b1eeb..dd5ef06 100644 --- a/models/entitiesmodel.js +++ b/models/entitiesmodel.js @@ -247,10 +247,10 @@ EntitiesModel.prototype.searchElementsExtended = function(scope, entities) { const qb = new QueryBuilder(entities[entityData.id_entity]); const queryFilter = `${qb.bbox()} ${qb.filter()}`; - const prefix = entities[entityData.id_entity].prefix; + const suffix = entities[entityData.id_entity].suffix || ''; return this.cachedQuery(` SELECT DISTINCT ON (id_entity) ${entitySelect.join(', ')} - FROM ${entityData.dbschema}.${entityData.entity_table_name}${prefix} + FROM ${entityData.dbschema}.${entityData.entity_table_name}${suffix} WHERE TRUE ${queryFilter} `); }).bind(this)); From 4b7f35442e239149470ae72243b3c6e83582eb52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Thu, 20 Jun 2019 12:27:40 +0200 Subject: [PATCH 04/11] Fix typo --- docs/reference/entities.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference/entities.md b/docs/reference/entities.md index a37b84f..3fb4c60 100644 --- a/docs/reference/entities.md +++ b/docs/reference/entities.md @@ -55,7 +55,7 @@ Extends the functionalities of `/entities/search` Params: - entities (mandatory): entities to search. It's an object using the entities names as keys and the following attributes as values: - select (mandatory): fields to retrieve. Array - - suffix (optional): prefix to append to the entity associated table_name. String + - suffix (optional): suffix to append to the entity associated table_name. String - filters (mandatory): the same functionalities used in other endpoints. Object Request sample @@ -67,7 +67,7 @@ Request sample "id_entity", "hasfirealert" ], - "prefix": "_lastdata", + "suffix": "_lastdata", "filters": { "condition": { "OR": { @@ -82,7 +82,7 @@ Request sample "id_entity", "battery" ], - "prefix": "_lastdata", + "suffix": "_lastdata", "filters": { "condition": { "AND": { From 44350c17c6542810101cfed013efd8fe095901af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Thu, 20 Jun 2019 12:31:24 +0200 Subject: [PATCH 05/11] Lint fix --- routes/entities.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/entities.js b/routes/entities.js index 3ae94a8..af69e3d 100644 --- a/routes/entities.js +++ b/routes/entities.js @@ -129,7 +129,7 @@ router.post('/search/extended',auth.logged,function(req, res, next) { .catch(err => { log.error('Search elements: Error when selecting data'); next(err); - }); + }); }); module.exports = router; From 07a45bfd85088a70e03158572a3bcc1fd01ab683 Mon Sep 17 00:00:00 2001 From: rbsolis Date: Thu, 20 Jun 2019 15:03:13 +0200 Subject: [PATCH 06/11] Changed timezone var name to data_tz. Added ref docs info. --- docs/reference/devices.md | 6 +++++- models/devicesmodel.js | 4 ++-- protools/devicesformatter.js | 4 ++-- routes/devices.js | 4 ++-- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/reference/devices.md b/docs/reference/devices.md index 4cb804a..f89b633 100644 --- a/docs/reference/devices.md +++ b/docs/reference/devices.md @@ -159,6 +159,8 @@ It returns a time serie of a variable (or a set of variables). - vars (mandatory): array of all variables ids. - start (mandatory): date start. Format is 'YYYY-MM-DD HH:MM'. Date time is UTC. - finish (mandatory): date finish. Format is 'YYYY-MM-DD HH:MM'. Date time is UTC. +- format (optional): If its value is `csv` it will return a CSV file. +- data_tz (optional, in conjunction with format): Will parse dates info into local user time zone. - bbox (optional) : [lx,ly,ux,uy] @@ -172,6 +174,8 @@ Payload: }, "filters": { "bbox": [-5.11170,37.24000,-5.10818,37.24303] - } + }, + "format": "csv", + "data_tz": "Europe/Madrid" } ``` diff --git a/models/devicesmodel.js b/models/devicesmodel.js index 1179e1f..1f73a2e 100644 --- a/models/devicesmodel.js +++ b/models/devicesmodel.js @@ -427,8 +427,8 @@ DevicesModel.prototype.getDevicesRawData = function(opts, cb) { return Promise.all(promises); }).bind(this)) .then(function(results) { - if (opts.format_tz && opts.format) { - return new DevicesFormatter().parsedData(results); + if (opts.data_tz && opts.format) { + return new DevicesFormatter().parsedData(results, opts.data_tz); } else { return new DevicesFormatter().rawData(results); } diff --git a/protools/devicesformatter.js b/protools/devicesformatter.js index 7049e88..3e86625 100644 --- a/protools/devicesformatter.js +++ b/protools/devicesformatter.js @@ -48,7 +48,7 @@ class DevicesFormatter extends BaseFormatter { return Promise.resolve(rslts); } - parsedData(results) { + parsedData(results, tz) { var rslts = []; @@ -62,7 +62,7 @@ class DevicesFormatter extends BaseFormatter { rslts = _.union(_.flatten(rslts)); } rslts.forEach(function(rsl) { - rsl.time = moment.tz(rsl.time, 'Europe/Madrid').format('DD/MM/YYYY HH:mm'); + rsl.time = moment.tz(rsl.time, tz).format('DD/MM/YYYY HH:mm'); }); return Promise.resolve(rslts); } diff --git a/routes/devices.js b/routes/devices.js index 4ba8ef1..886c612 100644 --- a/routes/devices.js +++ b/routes/devices.js @@ -124,7 +124,7 @@ router.post('/:id_entity/:id_device/raw', let start = req.body.time.start; let finish = req.body.time.finish; let format = req.body.format; - let format_tz = req.body.format_tz; + let data_tz = req.body.data_tz; var opts = { scope: req.scope, @@ -135,7 +135,7 @@ router.post('/:id_entity/:id_device/raw', id_vars: req.body.vars, filters: req.body.filters||{}, format: format, - format_tz: format_tz || '' + data_tz: data_tz || '' }; var model = new DevicesModel(); From ad53b0318774c4bf878878f5b75a81146d65ef74 Mon Sep 17 00:00:00 2001 From: rbsolis Date: Thu, 20 Jun 2019 15:21:04 +0200 Subject: [PATCH 07/11] Erased print --- routes/devices.js | 1 - 1 file changed, 1 deletion(-) diff --git a/routes/devices.js b/routes/devices.js index 886c612..f20029f 100644 --- a/routes/devices.js +++ b/routes/devices.js @@ -97,7 +97,6 @@ router.get('/:id_entity/:id_device/lastdata', log.error('Device info: Error when selecting data'); next(err); } else { - print(r); res.json(r); } }); From ac3c752302d810c3e49ee283454a50e3aae7fe40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Mon, 24 Jun 2019 11:04:21 +0200 Subject: [PATCH 08/11] Geometry/id_entity response by default --- models/entitiesmodel.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/models/entitiesmodel.js b/models/entitiesmodel.js index dd5ef06..58aed77 100644 --- a/models/entitiesmodel.js +++ b/models/entitiesmodel.js @@ -242,14 +242,14 @@ EntitiesModel.prototype.searchElementsExtended = function(scope, entities) { const promises = d.rows.map(((entityData) => { - const entitySelect = entities[entityData.id_entity].select; + const entitySelect = ['', ..._.without(entities[entityData.id_entity].select, ['id_entity', 'position'])]; const qb = new QueryBuilder(entities[entityData.id_entity]); const queryFilter = `${qb.bbox()} ${qb.filter()}`; const suffix = entities[entityData.id_entity].suffix || ''; return this.cachedQuery(` - SELECT DISTINCT ON (id_entity) ${entitySelect.join(', ')} + SELECT DISTINCT ON (id_entity) id_entity, ST_AsGeoJSON(position) as geometry ${entitySelect.join(', ')} FROM ${entityData.dbschema}.${entityData.entity_table_name}${suffix} WHERE TRUE ${queryFilter} `); @@ -259,7 +259,15 @@ EntitiesModel.prototype.searchElementsExtended = function(scope, entities) { .then(dataResult => { // group result by id_entity return Promise.resolve(_.reduce(dataResult, (result, r, i) => { - return Object.assign(result, {[d.rows[i].id_entity]: r.rows}); + const rows = r.rows.map((v)=>{ + console.log() + return Object.assign(v, { + geometry: v.geometry ? JSON.parse(v.geometry).coordinates : null + }); + }); + return Object.assign(result, { + [d.rows[i].id_entity]: r.rows + }); }, {})); }); }.bind(this)) From e825782bc48c1c748936d0bf7ad39cf3b1a4e326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Mon, 24 Jun 2019 11:10:12 +0200 Subject: [PATCH 09/11] Console.log removed --- models/entitiesmodel.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/models/entitiesmodel.js b/models/entitiesmodel.js index 58aed77..a391d9e 100644 --- a/models/entitiesmodel.js +++ b/models/entitiesmodel.js @@ -260,13 +260,12 @@ EntitiesModel.prototype.searchElementsExtended = function(scope, entities) { // group result by id_entity return Promise.resolve(_.reduce(dataResult, (result, r, i) => { const rows = r.rows.map((v)=>{ - console.log() return Object.assign(v, { geometry: v.geometry ? JSON.parse(v.geometry).coordinates : null }); }); return Object.assign(result, { - [d.rows[i].id_entity]: r.rows + [d.rows[i].id_entity]: rows }); }, {})); }); From 77f4a492595516367dc3fafa72cab774a2f740f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Mon, 24 Jun 2019 12:12:51 +0200 Subject: [PATCH 10/11] NoData attribute added to timeserie --- docs/reference/variables.md | 2 ++ models/variablesmodel.js | 24 ++++++++++++++++++++++-- routes/variables.js | 3 ++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/docs/reference/variables.md b/docs/reference/variables.md index 428b369..e088ba4 100644 --- a/docs/reference/variables.md +++ b/docs/reference/variables.md @@ -135,6 +135,7 @@ It returns a time serie of a variable (or a set of variables). - step (optional): time resolution. Values: 1h, 2h, 4h, 1d (default: 1d) - bbox (optional) : [lx,ly,ux,uy] - findTimes : true | false. Default to false. If set to true and max or min aggregator are used, it returns the list of "TimeInstant" where the max or min appears. +- noData (optional): true | false. Default to false. If true, it returns a registry for every time (aggregated by the step), even if there is no data (in that case, it's going to return null). Payload: ```json @@ -147,6 +148,7 @@ Payload: "step": "1d" }, "findTimes" : true, + "noData": false, "filters": { "bbox": [-5.11170,37.24000,-5.10818,37.24303] } diff --git a/models/variablesmodel.js b/models/variablesmodel.js index 868f9ba..8f059e5 100644 --- a/models/variablesmodel.js +++ b/models/variablesmodel.js @@ -183,7 +183,26 @@ VariablesModel.prototype.getVariablesTimeSerie = function(opts) { )`; } - var sql = ` + if (opts.noData) { + var sql = ` + WITH filtered AS ( + SELECT + DISTINCT id_entity + FROM ${schema}.${entityTables[i]}_lastdata + WHERE true + ${qb.bbox()} + ${qb.filter()} + ) + SELECT + _timeserie AS start, + (_timeserie + '${step}')::timestamp AS finish, + ${aggs[i]}(foo."${varNames[i]}") AS "${varIds[i]}_${aggs[i]}"${ cGroupAlias } + FROM generate_series('${opts.start}'::timestamp, '${opts.finish}'::timestamp, '${ step }') AS _timeserie + LEFT JOIN ${ from } foo + ON foo."TimeInstant" >= _timeserie AND foo."TimeInstant" < _timeserie + '${ step }' and id_entity IN (SELECT id_entity FROM filtered) + GROUP BY _timeserie${ cGroupAlias } ORDER BY _timeserie`; + } else { + var sql = ` WITH filtered AS ( SELECT DISTINCT id_entity @@ -201,6 +220,7 @@ VariablesModel.prototype.getVariablesTimeSerie = function(opts) { ON foo."TimeInstant" >= _timeserie AND foo."TimeInstant" < _timeserie + '${ step }' WHERE id_entity IN (SELECT id_entity FROM filtered) GROUP BY _timeserie${ cGroupAlias } ORDER BY _timeserie`; + } if (opts.findTimes && (aggs[i] === 'MIN' || aggs[i] === 'MAX')) { var preSQL = ` @@ -221,7 +241,7 @@ VariablesModel.prototype.getVariablesTimeSerie = function(opts) { sql = `${preSQL} ${sql} ${postSQL}`; } - + console.log(sql); return this.cachedQuery(sql); }).bind(this)()); diff --git a/routes/variables.js b/routes/variables.js index f8344a4..ea25aa9 100644 --- a/routes/variables.js +++ b/routes/variables.js @@ -144,7 +144,8 @@ router.post('/timeserie', id_vars: req.body.vars, filters: req.body.filters || {}, findTimes: req.body.findTimes || false, - csv: req.body.csv || false + csv: req.body.csv || false, + noData: req.body.noData || false }; if (opts.filters.group) { From db6b9b2d2a45ef49e9a84e29128cd70f44aea8a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Mon, 24 Jun 2019 12:14:35 +0200 Subject: [PATCH 11/11] console.log removed --- models/variablesmodel.js | 1 - 1 file changed, 1 deletion(-) diff --git a/models/variablesmodel.js b/models/variablesmodel.js index 8f059e5..3c03cf9 100644 --- a/models/variablesmodel.js +++ b/models/variablesmodel.js @@ -241,7 +241,6 @@ VariablesModel.prototype.getVariablesTimeSerie = function(opts) { sql = `${preSQL} ${sql} ${postSQL}`; } - console.log(sql); return this.cachedQuery(sql); }).bind(this)());