diff options
-rw-r--r-- | src/Diller.js | 15 | ||||
-rw-r--r-- | src/DillerDao.js | 28 | ||||
-rw-r--r-- | src/web/DillerWeb.js | 33 | ||||
-rw-r--r-- | web/static/app/DillerRpc.js | 15 | ||||
-rw-r--r-- | web/static/app/app.js | 41 | ||||
-rw-r--r-- | web/static/app/templates/device.html | 13 | ||||
-rw-r--r-- | web/static/app/templates/edit-attribute.modal.html (renamed from web/static/app/templates/device-edit-attribute.modal.html) | 2 | ||||
-rw-r--r-- | web/static/app/templates/front-page.html | 10 | ||||
-rw-r--r-- | web/static/app/templates/property.html | 38 |
9 files changed, 156 insertions, 39 deletions
diff --git a/src/Diller.js b/src/Diller.js index 46dc8e2..dc4c5e7 100644 --- a/src/Diller.js +++ b/src/Diller.js @@ -21,13 +21,13 @@ function Diller(config, pg, dao) { function newName(dao, device, property, timestamp, name) { log.info('New name for property ', device.key + '/' + property.key + '.name = ' + name); - return dao.updatePropertyName(property.id, name); + return dao.updateProperty(property.id, {name: name}); } function newDescription(dao, device, property, timestamp, description) { log.info('New description for property ', device.key + '/' + property.key + '.description = ' + description); - return dao.updatePropertyDescription(property.id, description); + return dao.updateProperty(property.id, {description: description}); } function updateAggregates(propertyId, timestamp) { @@ -54,6 +54,14 @@ function Diller(config, pg, dao) { return dao.updateDevice(deviceId, attributes); } + function updatePropertyAttributes(propertyId, attributes) { + var x = _.clone(attributes); + x.propertyId = propertyId; + log.info('Updating property attributes', x); + + return dao.updateProperty(propertyId, attributes); + } + //noinspection JSUnusedLocalSymbols function onMessage(topic, message, payload) { var timestamp = new Date(); @@ -121,7 +129,8 @@ function Diller(config, pg, dao) { return { onMessage: onMessage, - updateDeviceAttributes: updateDeviceAttributes + updateDeviceAttributes: updateDeviceAttributes, + updatePropertyAttributes: updatePropertyAttributes } } diff --git a/src/DillerDao.js b/src/DillerDao.js index e7f6b54..63ad3c1 100644 --- a/src/DillerDao.js +++ b/src/DillerDao.js @@ -69,12 +69,27 @@ function DillerDao(tx) { return tx.one('INSERT INTO device_property(id, device, key, created_timestamp) VALUES(DEFAULT, $1, $2, CURRENT_TIMESTAMP) RETURNING ' + propertyColumns, [deviceId, key]); } - function updatePropertyName(id, name) { - return tx.none('UPDATE device_property SET name=$1 WHERE id=$2', [name, id]); - } + function updateProperty(id, attributes) { + var values = [id]; + var i = 2; + var fields = _(attributes).chain() + .map(function (value, attribute) { + if (attribute == 'name' || attribute == 'description') { + value = (value || '').trim(); + values.push(value.length > 0 ? value : null); + return attribute + ' = $' + i++; + } + }) + .collect() + .join(', ') + .value(); + + if (fields.length == 0) { + return Promise.resolve({}); + } - function updatePropertyDescription(id, description) { - return tx.none('UPDATE device_property SET description=$1 WHERE id=$2', description, id); + var sql = 'UPDATE device_property SET ' + fields + ' WHERE id = $1 RETURNING *'; + return tx.one(sql, values); } // ------------------------------------------------------------------------------------------------------------------- @@ -130,8 +145,7 @@ function DillerDao(tx) { devicePropertyByDeviceIdAndKey: devicePropertyByDeviceIdAndKey, devicePropertiesByDeviceId: devicePropertiesByDeviceId, insertDeviceProperty: insertDeviceProperty, - updatePropertyName: updatePropertyName, - updatePropertyDescription: updatePropertyDescription, + updateProperty: updateProperty, valuesByPropertyId: valuesByPropertyId, insertValue: insertValue, diff --git a/src/web/DillerWeb.js b/src/web/DillerWeb.js index 3f1d107..03daca7 100644 --- a/src/web/DillerWeb.js +++ b/src/web/DillerWeb.js @@ -98,6 +98,37 @@ function DillerWeb(tx, config) { }); } + function patchProperty(req, res) { + tx(function (pg, dao, diller) { + var propertyId = req.params.propertyId; + + var body = req.body; + + var p; + if (!body.attribute) { + res.status(400).json({message: 'Required keys: "attribute" and "value".'}); + } else if (body.attribute == 'name' || body.attribute == 'description') { + var attributes = {}; + attributes[body.attribute] = body.value; + + p = diller.updatePropertyAttributes(propertyId, attributes); + } else { + p = Promise.reject('Unsupported attribute: ' + body.attribute); + } + + return p.then(function (property) { + return pg.batch([ + dao.deviceById(property.device), + dao.devicePropertiesByDeviceId(property.device)] + ); + }); + }).then(function (data) { + var device = data[0]; + device.properties = data[1]; + res.json({device: device}); + }, genericErrorHandler(res)); + } + function getIndex(req, res) { var baseUrl = (req.headers['trygvis-prefix'] || '').replace(/\/$/, ''); res.render('index', {baseUrl: baseUrl + '/'}); @@ -143,7 +174,9 @@ function DillerWeb(tx, config) { addApi('getDevices', 'get', '/device', getDevices); addApi('getDevice', 'get', '/device/:deviceId', getDevice); addApi('patchDevice', 'patch', '/device/:deviceId', patchDevice); + addApi('getValues', 'get', '/property/:propertyId/values', getValues); + addApi('patchProperty', 'patch', '/property/:propertyId/values', patchProperty); var templates = express.Router(); diff --git a/web/static/app/DillerRpc.js b/web/static/app/DillerRpc.js index b1939f4..f865dc5 100644 --- a/web/static/app/DillerRpc.js +++ b/web/static/app/DillerRpc.js @@ -33,11 +33,21 @@ function DillerRpc($http, DillerConfig) { return $http(req); } + function patchProperty(propertyId, payload) { + var req = {}; + req.method = 'patch'; + req.url = baseUrl + '/api/property/:propertyId/values'; + req.url = req.url.replace(/:propertyId/, propertyId); + req.data = payload; + return $http(req); + } + return { getDevices: getDevices, getDevice: getDevice, patchDevice: patchDevice, - getValues: getValues + getValues: getValues, + patchProperty: patchProperty }; } @@ -54,3 +64,6 @@ DillerRpcResolve.patchDevice = function(DillerRpc, $route) { DillerRpcResolve.getValues = function(DillerRpc, $route) { return DillerRpc.getValues($route.current.params.propertyId); }; +DillerRpcResolve.patchProperty = function(DillerRpc, $route) { + return DillerRpc.patchProperty($route.current.params.propertyId, $route.current.params.payload); +}; diff --git a/web/static/app/app.js b/web/static/app/app.js index fa4c4cc..22c7f83 100644 --- a/web/static/app/app.js +++ b/web/static/app/app.js @@ -10,7 +10,7 @@ ctrl.device = device.data.device; - ctrl.propertyChunks = _.chunk(ctrl.device.properties, 3); + ctrl.propertyChunks = _(ctrl.device.properties).sortByAll(['name', 'key']).chunk(3).value(); ctrl.editDeviceAttribute = function (attributeName) { var outer = ctrl; @@ -18,7 +18,7 @@ controller: function ($uibModalInstance) { var ctrl = this; - ctrl.attributeName = attributeName; + ctrl.title = 'Edit device ' + attributeName; ctrl.label = attributeName.substr(0, 1).toUpperCase() + attributeName.substr(1); ctrl.value = outer.device[attributeName]; @@ -34,16 +34,19 @@ }, controllerAs: 'ctrl', bindToController: true, - templateUrl: 'app/templates/device-edit-attribute.modal.html' + templateUrl: 'app/templates/edit-attribute.modal.html' }); } } - function PropertyController($timeout, $route, DillerRpc, device, values) { + function PropertyController($timeout, $route, $uibModal, DillerRpc, device, values) { var ctrl = this; - ctrl.device = device.data.device; - ctrl.property = _.find(ctrl.device.properties, {id: $route.current.params.propertyId}); + function updateData(device) { + ctrl.device = device.data.device; + ctrl.property = _.find(ctrl.device.properties, {id: $route.current.params.propertyId}); + } + updateData(device); ctrl.values = values.data.values; var refreshPromise; @@ -59,6 +62,32 @@ $timeout.cancel(refreshPromise); }) }; + + ctrl.editPropertyAttribute = function (attributeName) { + var outer = ctrl; + $uibModal.open({ + controller: function ($uibModalInstance) { + var ctrl = this; + + ctrl.title = 'Edit property ' + attributeName; + ctrl.label = attributeName.substr(0, 1).toUpperCase() + attributeName.substr(1); + ctrl.value = outer.property[attributeName]; + + ctrl.update = function () { + DillerRpc.patchProperty(outer.property.id, {attribute: attributeName, value: ctrl.value}) + .then(function (res) { + updateData(res); + $uibModalInstance.close({}); + }, function (res) { + ctrl.error = res.data.message; + }); + }; + }, + controllerAs: 'ctrl', + bindToController: true, + templateUrl: 'app/templates/edit-attribute.modal.html' + }); + } } function TimestampFilter() { diff --git a/web/static/app/templates/device.html b/web/static/app/templates/device.html index e3c988d..09452a8 100644 --- a/web/static/app/templates/device.html +++ b/web/static/app/templates/device.html @@ -8,12 +8,15 @@ </a> </h1> - <p ng-if="ctrl.device.description"> + <p> <a ng-click="ctrl.editDeviceAttribute('description')" class="pull-right"> <i class="fa fa-edit"/> </a> - {{ctrl.device.description}} + + <a href ng-if="!ctrl.device.description" ng-click="ctrl.editDeviceAttribute('description')"> + Edit description + </a> </p> <!-- @@ -74,7 +77,7 @@ <dt class="col-sm-3">Created</dt> <dd class="col-sm-9"> - {{ctrl.device.created_timestamp | date}} + {{ctrl.device.created_timestamp | date:'medium'}} </dd> </dl> @@ -90,9 +93,9 @@ </div> <div class="row" ng-if="ctrl.device.properties.length > 0" - ng-repeat="chunk in ctrl.propertyChunks | orderBy:'key' track by $index"> + ng-repeat="chunk in ctrl.propertyChunks"> - <div class="col-sm-4" ng-repeat="p in chunk | orderBy:'key' track by $index"> + <div class="col-sm-4" ng-repeat="p in chunk"> <div class="card"> <!-- <div class="card-header"> diff --git a/web/static/app/templates/device-edit-attribute.modal.html b/web/static/app/templates/edit-attribute.modal.html index 2e57f2d..effa08f 100644 --- a/web/static/app/templates/device-edit-attribute.modal.html +++ b/web/static/app/templates/edit-attribute.modal.html @@ -3,7 +3,7 @@ <button type="button" class="close" ng-click="$dismiss()"> <span>×</span> </button> - <h4 class="modal-title">Edit device {{ctrl.attributeName}}</h4> + <h4 class="modal-title">{{ctrl.title}}</h4> </div> <div class="modal-body"> <fieldset class="form-group"> diff --git a/web/static/app/templates/front-page.html b/web/static/app/templates/front-page.html index 68026c3..e2ee38b 100644 --- a/web/static/app/templates/front-page.html +++ b/web/static/app/templates/front-page.html @@ -5,17 +5,19 @@ <table class="table"> <thead> <tr> + <th>Name</th> <th>Registered</th> - <th>Key</th> </tr> </thead> <tbody> - <tr ng-repeat="d in ctrl.devices | orderBy:'key'"> + <tr ng-repeat="d in ctrl.devices | orderBy:['name', 'key']"> <td> - {{d.created_timestamp | date:'medium'}} + <a href="#/device/{{d.id}}"> + {{(d.name || d.key)}} + </a> </td> <td> - <a href="#/device/{{d.id}}">{{d.key}}</a> + {{d.created_timestamp | date:'medium'}} </td> </tr> </tbody> diff --git a/web/static/app/templates/property.html b/web/static/app/templates/property.html index 8cff1f0..969734b 100644 --- a/web/static/app/templates/property.html +++ b/web/static/app/templates/property.html @@ -1,24 +1,38 @@ <div class="container"> <h1> - <a href="#/device/{{ctrl.device.id}}"> - {{(ctrl.device.name || ctrl.device.key)}} + <a href="#/device/{{ctrl.device.id}}">{{(ctrl.device.name || ctrl.device.key)}}</a>: + {{(ctrl.property.name || ctrl.property.key)}} + + <a ng-click="ctrl.editPropertyAttribute('name')" class="pull-right" style="font-size: 1rem;"> + <i class="fa fa-edit"/> </a> </h1> - <h2> - {{(ctrl.property.name || ctrl.property.key)}} - </h2> - - <p ng-show="ctrl.property.description"> + <p> + <a ng-click="ctrl.editPropertyAttribute('description')" class="pull-right"> + <i class="fa fa-edit"/> + </a> {{ctrl.property.description}} + + <a href ng-if="!ctrl.property.description" ng-click="ctrl.editPropertyAttribute('description')"> + Edit description + </a> </p> - <ul> - <li>Created: {{ctrl.property.created_timestamp | date}}</li> - <li>Name: {{ctrl.property.name}}</li> - <li>Description: {{ctrl.property.description}}</li> - </ul> + <dl> + <dt class="col-sm-3">Key</dt> + <dd class="col-sm-9"> + {{ctrl.property.key}} + + </dd> + + <dt class="col-sm-3">Created</dt> + <dd class="col-sm-9"> + {{ctrl.property.created_timestamp | date:'medium'}} + + </dd> + </dl> <h3> Latest Values |