Commit 07cd8b9978b810cd2c0bffcaad453fcab9a09233

Authored by Vladyslav_Prykhodko
1 parent 62f43369

Fix dashboard device button, clear code and counted all statistical info

@@ -238,23 +238,23 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) @@ -238,23 +238,23 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
238 function success() { 238 function success() {
239 deferred.resolve(response.data); 239 deferred.resolve(response.data);
240 }, 240 },
241 - function fail(response) {  
242 - deferred.reject(response); 241 + function fail() {
  242 + deferred.reject();
243 } 243 }
244 ) 244 )
245 } else { 245 } else {
246 deferred.resolve(response.data); 246 deferred.resolve(response.data);
247 } 247 }
248 - }, function fail(response) {  
249 - deferred.reject(response); 248 + }, function fail() {
  249 + deferred.reject();
250 }); 250 });
251 } else if (deleteEntityAttributesPromise) { 251 } else if (deleteEntityAttributesPromise) {
252 deleteEntityAttributesPromise.then( 252 deleteEntityAttributesPromise.then(
253 function success() { 253 function success() {
254 deferred.resolve(); 254 deferred.resolve();
255 }, 255 },
256 - function fail(response) {  
257 - deferred.reject(response); 256 + function fail() {
  257 + deferred.reject();
258 } 258 }
259 ) 259 )
260 } else { 260 } else {
@@ -287,23 +287,23 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) @@ -287,23 +287,23 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
287 function success() { 287 function success() {
288 deferred.resolve(response.data); 288 deferred.resolve(response.data);
289 }, 289 },
290 - function fail(response) {  
291 - deferred.reject(response); 290 + function fail() {
  291 + deferred.reject();
292 } 292 }
293 ) 293 )
294 } else { 294 } else {
295 deferred.resolve(response.data); 295 deferred.resolve(response.data);
296 } 296 }
297 - }, function fail(response) {  
298 - deferred.reject(response); 297 + }, function fail() {
  298 + deferred.reject();
299 }); 299 });
300 } else if (deleteEntityTimeseriesPromise) { 300 } else if (deleteEntityTimeseriesPromise) {
301 deleteEntityTimeseriesPromise.then( 301 deleteEntityTimeseriesPromise.then(
302 function success() { 302 function success() {
303 deferred.resolve(); 303 deferred.resolve();
304 }, 304 },
305 - function fail(response) {  
306 - deferred.reject(response); 305 + function fail() {
  306 + deferred.reject();
307 } 307 }
308 ) 308 )
309 } else { 309 } else {
@@ -325,8 +325,8 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) @@ -325,8 +325,8 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
325 var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/' + attributeScope + '?keys=' + keys; 325 var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/' + attributeScope + '?keys=' + keys;
326 $http.delete(url, config).then(function success() { 326 $http.delete(url, config).then(function success() {
327 deferred.resolve(); 327 deferred.resolve();
328 - }, function fail(response) {  
329 - deferred.reject(response); 328 + }, function fail() {
  329 + deferred.reject();
330 }); 330 });
331 return deferred.promise; 331 return deferred.promise;
332 } 332 }
@@ -344,8 +344,8 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) @@ -344,8 +344,8 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
344 var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/delete' + '?keys=' + keys; 344 var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/delete' + '?keys=' + keys;
345 $http.delete(url, config).then(function success() { 345 $http.delete(url, config).then(function success() {
346 deferred.resolve(); 346 deferred.resolve();
347 - }, function fail(response) {  
348 - deferred.reject(response); 347 + }, function fail() {
  348 + deferred.reject();
349 }); 349 });
350 return deferred.promise; 350 return deferred.promise;
351 } 351 }
@@ -43,8 +43,7 @@ function DeviceService($http, $q, $window, userService, attributeService, custom @@ -43,8 +43,7 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
43 sendOneWayRpcCommand: sendOneWayRpcCommand, 43 sendOneWayRpcCommand: sendOneWayRpcCommand,
44 sendTwoWayRpcCommand: sendTwoWayRpcCommand, 44 sendTwoWayRpcCommand: sendTwoWayRpcCommand,
45 findByQuery: findByQuery, 45 findByQuery: findByQuery,
46 - getDeviceTypes: getDeviceTypes,  
47 - findByName: findByName 46 + getDeviceTypes: getDeviceTypes
48 } 47 }
49 48
50 return service; 49 return service;
@@ -167,8 +166,8 @@ function DeviceService($http, $q, $window, userService, attributeService, custom @@ -167,8 +166,8 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
167 var url = '/api/device'; 166 var url = '/api/device';
168 $http.post(url, device, config).then(function success(response) { 167 $http.post(url, device, config).then(function success(response) {
169 deferred.resolve(response.data); 168 deferred.resolve(response.data);
170 - }, function fail(response) {  
171 - deferred.reject(response); 169 + }, function fail() {
  170 + deferred.reject();
172 }); 171 });
173 return deferred.promise; 172 return deferred.promise;
174 } 173 }
@@ -178,18 +177,36 @@ function DeviceService($http, $q, $window, userService, attributeService, custom @@ -178,18 +177,36 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
178 let attributesType = Object.keys(types.attributesScope); 177 let attributesType = Object.keys(types.attributesScope);
179 let allPromise = []; 178 let allPromise = [];
180 let promise = ""; 179 let promise = "";
  180 + let statisticalInfo = {};
181 for (let i = 0; i < attributesType.length; i++) { 181 for (let i = 0; i < attributesType.length; i++) {
182 - if (deviceRelation.attributes[attributesType[i]] && deviceRelation.attributes[attributesType[i]].length !== 0) {  
183 - promise = attributeService.saveEntityAttributes(types.entityType.device, deviceId, types.attributesScope[attributesType[i]].value, deviceRelation.attributes[attributesType[i]], config); 182 + let attrribute = attributesType[i];
  183 + if (deviceRelation.attributes[attrribute] && deviceRelation.attributes[attrribute].length !== 0) {
  184 + promise = attributeService.saveEntityAttributes(types.entityType.device, deviceId, types.attributesScope[attrribute].value, deviceRelation.attributes[attrribute], config).then(function () {
  185 + statisticalInfo.create = {
  186 + [attrribute]: deviceRelation.attributes[attributesType[i]].length
  187 + };
  188 + }, function () {
  189 + statisticalInfo.error = {
  190 + [attrribute]: deviceRelation.attributes[attributesType[i]].length
  191 + };
  192 + });
184 allPromise.push(promise); 193 allPromise.push(promise);
185 } 194 }
186 } 195 }
187 if (deviceRelation.timeseries.length !== 0) { 196 if (deviceRelation.timeseries.length !== 0) {
188 - promise = attributeService.saveEntityTimeseries(types.entityType.device, deviceId, "time", deviceRelation.timeseries, config); 197 + promise = attributeService.saveEntityTimeseries(types.entityType.device, deviceId, "time", deviceRelation.timeseries, config).then(function () {
  198 + statisticalInfo.create = {
  199 + timeseries: deviceRelation.timeseries.length
  200 + };
  201 + }, function () {
  202 + statisticalInfo.error = {
  203 + timeseries: deviceRelation.timeseries.length
  204 + };
  205 + });
189 allPromise.push(promise); 206 allPromise.push(promise);
190 } 207 }
191 $q.all(allPromise).then(function success() { 208 $q.all(allPromise).then(function success() {
192 - deferred.resolve(); 209 + deferred.resolve(statisticalInfo);
193 }); 210 });
194 return deferred.promise; 211 return deferred.promise;
195 } 212 }
@@ -197,26 +214,28 @@ function DeviceService($http, $q, $window, userService, attributeService, custom @@ -197,26 +214,28 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
197 function saveDeviceParameters(deviceParameters, update, config) { 214 function saveDeviceParameters(deviceParameters, update, config) {
198 config = config || {}; 215 config = config || {};
199 const deferred = $q.defer(); 216 const deferred = $q.defer();
200 - let statisticalInfo = {  
201 - create: {},  
202 - update: {},  
203 - error: {}  
204 - }; 217 + let statisticalInfo = {};
205 let newDevice = { 218 let newDevice = {
206 name: deviceParameters.name, 219 name: deviceParameters.name,
207 type: deviceParameters.type 220 type: deviceParameters.type
208 }; 221 };
209 saveDevice(newDevice, config).then(function success(response) { 222 saveDevice(newDevice, config).then(function success(response) {
210 - statisticalInfo.create.device = 1;  
211 - saveDeviceRelarion(response.id.id, deviceParameters, config).then(function success() { 223 + statisticalInfo.create={
  224 + device: 1
  225 + };
  226 + saveDeviceRelarion(response.id.id, deviceParameters, config).then(function success(response) {
  227 + angular.merge(statisticalInfo, response, statisticalInfo);
212 deferred.resolve(statisticalInfo); 228 deferred.resolve(statisticalInfo);
213 }); 229 });
214 - }, function fail(response) {  
215 - console.log(response); // eslint-disable-line 230 + }, function fail() {
216 if (update) { 231 if (update) {
217 findByName(deviceParameters.name, config).then(function success(response) { 232 findByName(deviceParameters.name, config).then(function success(response) {
218 - statisticalInfo.update.device = 1;  
219 - saveDeviceRelarion(response.id.id, deviceParameters, config).then(function success() { 233 + statisticalInfo.update = {
  234 + device: 1
  235 + };
  236 + saveDeviceRelarion(response.id.id, deviceParameters, config).then(function success(response) {
  237 + delete Object.assign(response, {update: response.create}).create;
  238 + angular.merge(statisticalInfo, response);
220 deferred.resolve(statisticalInfo); 239 deferred.resolve(statisticalInfo);
221 }); 240 });
222 }, function fail() { 241 }, function fail() {
@@ -384,8 +403,8 @@ function DeviceService($http, $q, $window, userService, attributeService, custom @@ -384,8 +403,8 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
384 var url = '/api/tenant/devices?deviceName=' + deviceName; 403 var url = '/api/tenant/devices?deviceName=' + deviceName;
385 $http.get(url, config).then(function success(response) { 404 $http.get(url, config).then(function success(response) {
386 deferred.resolve(response.data); 405 deferred.resolve(response.data);
387 - }, function fail(response) {  
388 - deferred.reject(response); 406 + }, function fail() {
  407 + deferred.reject();
389 }); 408 });
390 return deferred.promise; 409 return deferred.promise;
391 } 410 }
@@ -352,6 +352,10 @@ export default angular.module('thingsboard.types', []) @@ -352,6 +352,10 @@ export default angular.module('thingsboard.types', [])
352 }, 352 },
353 entityGroup: { 353 entityGroup: {
354 columnType: { 354 columnType: {
  355 + clientAttribute: {
  356 + name: 'entity-group.column-type.client-attribute',
  357 + value: 'CLIENT_ATTRIBUTE'
  358 + },
355 sharedAttribute: { 359 sharedAttribute: {
356 name: 'entity-group.column-type.shared-attribute', 360 name: 'entity-group.column-type.shared-attribute',
357 value: 'SHARED_ATTRIBUTE' 361 value: 'SHARED_ATTRIBUTE'
@@ -68,7 +68,7 @@ export function DeviceController($rootScope, userService, deviceService, custome @@ -68,7 +68,7 @@ export function DeviceController($rootScope, userService, deviceService, custome
68 }, 68 },
69 { 69 {
70 onAction: function ($event) { 70 onAction: function ($event) {
71 - importExport.importDevices($event).then( 71 + importExport.importDevices($event, types.entityType.device).then(
72 function() { 72 function() {
73 vm.grid.refreshList(); 73 vm.grid.refreshList();
74 } 74 }
@@ -107,7 +107,6 @@ export function DeviceController($rootScope, userService, deviceService, custome @@ -107,7 +107,6 @@ export function DeviceController($rootScope, userService, deviceService, custome
107 107
108 addItemTemplateUrl: addDeviceTemplate, 108 addItemTemplateUrl: addDeviceTemplate,
109 109
110 - // addItemText: function() { return $translate.instant('device.add-device-text') },  
111 noItemsText: function() { return $translate.instant('device.no-devices-text') }, 110 noItemsText: function() { return $translate.instant('device.no-devices-text') },
112 itemDetailsText: function() { return $translate.instant('device.device-details') }, 111 itemDetailsText: function() { return $translate.instant('device.device-details') },
113 isDetailsReadOnly: isCustomerUser, 112 isDetailsReadOnly: isCustomerUser,
@@ -338,6 +337,7 @@ export function DeviceController($rootScope, userService, deviceService, custome @@ -338,6 +337,7 @@ export function DeviceController($rootScope, userService, deviceService, custome
338 icon: "add" 337 icon: "add"
339 }; 338 };
340 339
  340 + vm.deviceGridConfig.addItemActions = [];
341 341
342 } else if (vm.devicesScope === 'customer_user') { 342 } else if (vm.devicesScope === 'customer_user') {
343 deviceActionsList.push( 343 deviceActionsList.push(
@@ -352,6 +352,7 @@ export function DeviceController($rootScope, userService, deviceService, custome @@ -352,6 +352,7 @@ export function DeviceController($rootScope, userService, deviceService, custome
352 ); 352 );
353 353
354 vm.deviceGridConfig.addItemAction = {}; 354 vm.deviceGridConfig.addItemAction = {};
  355 + vm.deviceGridConfig.addItemActions = [];
355 } 356 }
356 } 357 }
357 358
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 import './import-dialog.scss'; 16 import './import-dialog.scss';
17 17
18 /*@ngInject*/ 18 /*@ngInject*/
19 -export default function ImportDialogCsvController($scope, $mdDialog, toast, importTitle, importFileLabel, importExport, types, $timeout) { 19 +export default function ImportDialogCsvController($scope, $mdDialog, toast, importTitle, importFileLabel, entityType, importExport, types, $timeout, $q) {
20 20
21 var vm = this; 21 var vm = this;
22 22
@@ -27,63 +27,80 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo @@ -27,63 +27,80 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo
27 27
28 vm.addDevices = addDevices; 28 vm.addDevices = addDevices;
29 vm.importParams = { 29 vm.importParams = {
  30 + delim: ',',
30 isUpdate: true, 31 isUpdate: true,
31 isHeader: true 32 isHeader: true
32 }; 33 };
33 34
  35 + vm.selectedStep = 0;
  36 + vm.stepProgress = 1;
  37 + vm.maxStep = 3;
  38 + vm.showBusyText = false;
  39 + vm.stepData = [
  40 + { step: 1, completed: false, optional: false, data: {} },
  41 + { step: 2, completed: false, optional: false, data: {} },
  42 + { step: 3, completed: false, optional: false, data: {} },
  43 + ];
  44 +
  45 + vm.enableNextStep = function nextStep() {
  46 + //do not exceed into max step
  47 + if (vm.selectedStep >= vm.maxStep) {
  48 + return;
  49 + }
  50 + //do not increment vm.stepProgress when submitting from previously completed step
  51 + if (vm.selectedStep === vm.stepProgress - 1) {
  52 + vm.stepProgress = vm.stepProgress + 1;
  53 + }
  54 + vm.selectedStep = vm.selectedStep + 1;
  55 + };
  56 +
  57 + vm.moveToPreviousStep = function moveToPreviousStep() {
  58 + if (vm.selectedStep > 0) {
  59 + vm.selectedStep = vm.selectedStep - 1;
  60 + }
  61 + };
  62 +
  63 + vm.submitCurrentStep = function submitCurrentStep(stepData, isSkip) {
  64 + var deferred = $q.defer();
  65 + vm.showBusyText = true;
  66 + if (!stepData.completed && !isSkip) {
  67 + //simulate $http
  68 + $timeout(function () {
  69 + vm.showBusyText = false;
  70 + deferred.resolve({ status: 200, statusText: 'success', data: {} });
  71 + //move to next step when success
  72 + stepData.completed = true;
  73 + vm.enableNextStep();
  74 + }, 1000)
  75 + } else {
  76 + vm.showBusyText = false;
  77 + vm.enableNextStep();
  78 + }
  79 + };
  80 +
34 vm.importTitle = importTitle; 81 vm.importTitle = importTitle;
35 vm.importFileLabel = importFileLabel; 82 vm.importFileLabel = importFileLabel;
  83 + vm.entityType = entityType;
36 84
37 vm.columnsParam = []; 85 vm.columnsParam = [];
38 vm.parseData = []; 86 vm.parseData = [];
39 87
40 - vm.entityType = types.entityType.device;  
41 - vm.columnTypes = {};  
42 - vm.entityField = {}; 88 + vm.delimiters = [{
  89 + key: ',',
  90 + value: ','
  91 + },{
  92 + key: ';',
  93 + value: ';'
  94 + },{
  95 + key: '|',
  96 + value: '|'
  97 + },{
  98 + key: '\t',
  99 + value: 'Tab'
  100 + }];
43 101
44 var parseData = {}; 102 var parseData = {};
45 103
46 - switch (vm.entityType) {  
47 - case types.entityType.device:  
48 - vm.columnTypes = types.entityGroup.columnType;  
49 - break;  
50 - }  
51 -  
52 - vm.entityField.name = types.entityGroup.entityField.name;  
53 -  
54 - switch (vm.entityType) {  
55 - case types.entityType.device:  
56 - vm.entityField.type = types.entityGroup.entityField.type;  
57 - // vm.entityField.assigned_customer = types.entityGroup.entityField.assigned_customer;  
58 - break;  
59 - }  
60 -  
61 - $scope.$watch('vm.columnsParam', function(newVal, prevVal){  
62 - if (newVal && !angular.equals(newVal, prevVal)) {  
63 - var isSelectName = false;  
64 - var isSelectType = false;  
65 - for (var i = 0; i < newVal.length; i++) {  
66 - if (newVal[i].type === types.entityGroup.columnType.entityField.value &&  
67 - newVal[i].key === types.entityGroup.entityField.name.value) {  
68 - isSelectName = true;  
69 - }  
70 - if (newVal[i].type === types.entityGroup.columnType.entityField.value &&  
71 - newVal[i].key === types.entityGroup.entityField.type.value) {  
72 - isSelectType = true;  
73 - }  
74 - }  
75 - $timeout(function () {  
76 - vm.entityField.name.disable = isSelectName;  
77 - vm.entityField.type.disable = isSelectType;  
78 - });  
79 - }  
80 - }, true);  
81 -  
82 -  
83 - function cancel() {  
84 - $mdDialog.cancel();  
85 - }  
86 -  
87 function fileAdded($file) { 104 function fileAdded($file) {
88 if ($file.getExtension() === 'csv') { 105 if ($file.getExtension() === 'csv') {
89 var reader = new FileReader(); 106 var reader = new FileReader();
@@ -94,9 +111,8 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo @@ -94,9 +111,8 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo
94 var importCSV = event.target.result; 111 var importCSV = event.target.result;
95 if (importCSV && importCSV.length > 0) { 112 if (importCSV && importCSV.length > 0) {
96 try { 113 try {
97 - vm.importData = importCSV; 114 + parseCSV(importCSV);
98 vm.fileName = $file.name; 115 vm.fileName = $file.name;
99 - parseCSVData(vm.importData);  
100 } catch (err) { 116 } catch (err) {
101 vm.fileName = null; 117 vm.fileName = null;
102 toast.showError(err.message); 118 toast.showError(err.message);
@@ -109,7 +125,7 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo @@ -109,7 +125,7 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo
109 } 125 }
110 } 126 }
111 127
112 - function parseCSVData(importData) { 128 + function parseCSV(importData) {
113 var columnParam = {}; 129 var columnParam = {};
114 var config = { 130 var config = {
115 delim: vm.importParams.delim, 131 delim: vm.importParams.delim,
@@ -202,10 +218,14 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo @@ -202,10 +218,14 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo
202 function clearFile() { 218 function clearFile() {
203 $scope.theForm.$setDirty(); 219 $scope.theForm.$setDirty();
204 vm.fileName = null; 220 vm.fileName = null;
205 - vm.importData = null; 221 + parseData = null;
206 vm.columnsParam = []; 222 vm.columnsParam = [];
207 } 223 }
208 224
  225 + function cancel() {
  226 + $mdDialog.cancel();
  227 + }
  228 +
209 function importFromJson() { 229 function importFromJson() {
210 $scope.theForm.$setPristine(); 230 $scope.theForm.$setPristine();
211 $mdDialog.hide(vm.importData); 231 $mdDialog.hide(vm.importData);
@@ -63,66 +63,26 @@ @@ -63,66 +63,26 @@
63 </div> 63 </div>
64 </fieldset> 64 </fieldset>
65 <div flex layout="row"> 65 <div flex layout="row">
66 - <md-input-container class="md-block">  
67 - <label translate>CSV delimiter parametr</label>  
68 - <input ng-model="vm.importParams.delim"> 66 + <md-input-container class="md-block" style="min-width: 120px">
  67 + <label translate>CSV delimiter</label>
  68 + <md-select ng-model="vm.importParams.delim">
  69 + <md-option ng-repeat="delimiter in vm.delimiters" ng-value="delimiter.key">
  70 + {{delimiter.value}}
  71 + </md-option>
  72 + </md-select>
69 </md-input-container> 73 </md-input-container>
70 <md-input-container class="md-block"> 74 <md-input-container class="md-block">
71 <md-checkbox ng-model="vm.importParams.isHeader" aria-label="Checkbox 1"> 75 <md-checkbox ng-model="vm.importParams.isHeader" aria-label="Checkbox 1">
72 - Use first line is header 76 + First line is header
73 </md-checkbox> 77 </md-checkbox>
74 </md-input-container> 78 </md-input-container>
75 <md-input-container class="md-block"> 79 <md-input-container class="md-block">
76 <md-checkbox ng-model="vm.importParams.isUpdate" aria-label="Checkbox 1"> 80 <md-checkbox ng-model="vm.importParams.isUpdate" aria-label="Checkbox 1">
77 - Update parameter device 81 + Update parameters
78 </md-checkbox> 82 </md-checkbox>
79 </md-input-container> 83 </md-input-container>
80 </div> 84 </div>
81 - <md-table-container flex class="tb-table-select">  
82 - <table md-table>  
83 - <thead md-head>  
84 - <tr md-row>  
85 - <th md-column>&nbsp</th>  
86 - <th md-column>Example value data</th>  
87 - <th md-column style="min-width: 140px">Column type</th>  
88 - <th md-column style="min-width: 140px">Value</th>  
89 - </tr>  
90 - </thead>  
91 - <tbody md-body>  
92 - <tr md-row ng-repeat="column in vm.columnsParam">  
93 - <td md-cell>{{$index + 1}}</td>  
94 - <td md-cell>{{column.sampleData}}</td>  
95 - <td md-cell>  
96 - <md-select ng-model="column.type" required name="columnType"  
97 - aria-label="{{ 'entity-group.column-type' | translate }}">  
98 - <md-option ng-repeat="type in vm.columnTypes" ng-value="type.value">  
99 - {{type.name | translate}}  
100 - </md-option>  
101 - </md-select>  
102 - </td>  
103 - <td md-cell>  
104 - <md-select ng-if="column.type == vm.columnTypes.entityField.value"  
105 - required name="columnKey" ng-model="column.key"  
106 - aria-label="{{ 'entity-group.column-value' | translate }}">  
107 - <md-option ng-repeat="field in vm.entityField" ng-value="field.value" ng-disabled="field.disable">  
108 - {{field.name | translate}}  
109 - </md-option>  
110 - </md-select>  
111 - <md-input-container md-no-float  
112 - ng-if="column.type != vm.columnTypes.entityField.value &&  
113 - column.type != vm.columnTypes.name.value &&  
114 - column.type != vm.columnTypes.type.value">  
115 - <input required name="columnKeyName"  
116 - placeholder="{{ 'entity-group.column-value' | translate }}"  
117 - ng-model="column.key"  
118 - aria-label="{{ 'entity-group.column-value' | translate }}">  
119 - </md-input-container>  
120 - </td>  
121 - </tr>  
122 - </tbody>  
123 - </table>  
124 - <md-divider></md-divider>  
125 - </md-table-container> 85 + <tb-table-columns-assignment the-form="theForm" columns="vm.columnsParam" entityType="vm.entityType"></tb-table-columns-assignment>
126 </div> 86 </div>
127 </md-dialog-content> 87 </md-dialog-content>
128 <md-dialog-actions layout="row"> 88 <md-dialog-actions layout="row">
@@ -578,9 +578,9 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, @@ -578,9 +578,9 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
578 return deferred.promise; 578 return deferred.promise;
579 } 579 }
580 580
581 - function importDevices($event) { 581 + function importDevices($event, entityType) {
582 var deferred = $q.defer(); 582 var deferred = $q.defer();
583 - openImportDialogCSV($event, 'device.import', 'device.device-file').then( 583 + openImportDialogCSV($event, entityType,'device.import', 'device.device-file').then(
584 function success() { 584 function success() {
585 // if (!validateImportedDashboard(dashboard)) { 585 // if (!validateImportedDashboard(dashboard)) {
586 // toast.showError($translate.instant('dashboard.invalid-dashboard-file-error')); 586 // toast.showError($translate.instant('dashboard.invalid-dashboard-file-error'));
@@ -783,10 +783,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, @@ -783,10 +783,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
783 return deferred.promise; 783 return deferred.promise;
784 } 784 }
785 785
786 - /**  
787 - * splitCSV function (c) 2009 Brian Huisman, see http://www.greywyvern.com/?post=258  
788 - * Works by spliting on seperators first, then patching together quoted values  
789 - */  
790 function splitCSV(str, sep) { 786 function splitCSV(str, sep) {
791 for (var foo = str.split(sep = sep || ","), x = foo.length - 1, tl; x >= 0; x--) { 787 for (var foo = str.split(sep = sep || ","), x = foo.length - 1, tl; x >= 0; x--) {
792 if (foo[x].replace(/"\s+$/, '"').charAt(foo[x].length - 1) == '"') { 788 if (foo[x].replace(/"\s+$/, '"').charAt(foo[x].length - 1) == '"') {
@@ -805,7 +801,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, @@ -805,7 +801,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
805 return !isNaN(parseFloat(str)) && isFinite(str); 801 return !isNaN(parseFloat(str)) && isFinite(str);
806 } 802 }
807 803
808 - function parseStringToFormatJS(str) { 804 + function convertStringToJSType(str) {
809 if (isNumeric(str.replace(',', '.'))) { 805 if (isNumeric(str.replace(',', '.'))) {
810 return parseFloat(str.replace(',', '.')); 806 return parseFloat(str.replace(',', '.'));
811 } 807 }
@@ -827,7 +823,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, @@ -827,7 +823,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
827 let csvlines = csvdata.split(/[\r\n]+/); 823 let csvlines = csvdata.split(/[\r\n]+/);
828 let csvheaders = splitCSV(csvlines[0], delim); 824 let csvheaders = splitCSV(csvlines[0], delim);
829 if (csvheaders.length < 2) { 825 if (csvheaders.length < 2) {
830 - toast.showError('A file should contain at least two columns'); 826 + toast.showError($translate.instant('entity.import-csv-number-columns-error'));
831 return -1; 827 return -1;
832 } 828 }
833 let csvrows = header ? csvlines.slice(1, csvlines.length) : csvlines; 829 let csvrows = header ? csvlines.slice(1, csvlines.length) : csvlines;
@@ -844,34 +840,47 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, @@ -844,34 +840,47 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
844 840
845 let rowitems = splitCSV(row, delim); 841 let rowitems = splitCSV(row, delim);
846 if (rowitems.length !== result.headers.length) { 842 if (rowitems.length !== result.headers.length) {
847 - toast.showError('Invalid file format. Row:' + (header ? result.rows.length + 2: result.rows.length + 1)); 843 + toast.showError($translate.instant('entity.import-csv-invalid-format-error', {line: (header ? result.rows.length + 2: result.rows.length + 1)}));
848 return -1; 844 return -1;
849 } 845 }
850 for (let i = 0; i < rowitems.length; i++) { 846 for (let i = 0; i < rowitems.length; i++) {
851 - rowitems[i] = parseStringToFormatJS(rowitems[i]); 847 + rowitems[i] = convertStringToJSType(rowitems[i]);
852 } 848 }
853 result.rows.push(rowitems); 849 result.rows.push(rowitems);
854 } 850 }
855 } 851 }
856 return result; 852 return result;
857 } 853 }
  854 +
  855 + function sumObject(obj1, obj2){
  856 + Object.keys(obj2).map(function(key) {
  857 + if (angular.isObject(obj2[key])) {
  858 + obj1[key] = obj1[key] || {};
  859 + angular.merge(obj1[key], sumObject(obj1[key], obj2[key]));
  860 + } else {
  861 + obj1[key] = (obj1[key] || 0) + obj2[key];
  862 + }
  863 + });
  864 + return obj1;
  865 + }
858 866
859 function createMultiEntity(arrayData, entityType, update, config) { 867 function createMultiEntity(arrayData, entityType, update, config) {
860 - var deferred = $q.defer();  
861 - var allPromise = []; 868 + let deferred = $q.defer();
  869 + let allPromise = [];
  870 + let statisticalInfo = {};
862 switch (entityType) { 871 switch (entityType) {
863 case types.entityType.device: 872 case types.entityType.device:
864 - for(var i = 0; i < arrayData.length; i++){  
865 - var promise = deviceService.saveDeviceParameters(arrayData[i], update, config); 873 + for(let i = 0; i < arrayData.length; i++){
  874 + const promise = deviceService.saveDeviceParameters(arrayData[i], update, config);
866 allPromise.push(promise); 875 allPromise.push(promise);
867 } 876 }
868 break; 877 break;
869 } 878 }
870 - $q.all(allPromise).then(function success() {  
871 - deferred.resolve();  
872 - $timeout(function () {  
873 - console.log("1"); // eslint-disable-line  
874 - }, 1000); 879 + $q.all(allPromise).then(function success(response) {
  880 + for (let i = 0; i < response.length; i++){
  881 + statisticalInfo = sumObject(statisticalInfo, response[i]);
  882 + }
  883 + deferred.resolve(statisticalInfo);
875 }); 884 });
876 return deferred.promise; 885 return deferred.promise;
877 } 886 }
@@ -950,7 +959,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, @@ -950,7 +959,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
950 return deferred.promise; 959 return deferred.promise;
951 } 960 }
952 961
953 - function openImportDialogCSV($event, importTitle, importFileLabel) { 962 + function openImportDialogCSV($event, entityType, importTitle, importFileLabel) {
954 var deferred = $q.defer(); 963 var deferred = $q.defer();
955 $mdDialog.show({ 964 $mdDialog.show({
956 controller: 'ImportDialogCSVController', 965 controller: 'ImportDialogCSVController',
@@ -958,7 +967,8 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, @@ -958,7 +967,8 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
958 templateUrl: importDialogCSVTemplate, 967 templateUrl: importDialogCSVTemplate,
959 locals: { 968 locals: {
960 importTitle: importTitle, 969 importTitle: importTitle,
961 - importFileLabel: importFileLabel 970 + importFileLabel: importFileLabel,
  971 + entityType: entityType
962 }, 972 },
963 parent: angular.element($document[0].body), 973 parent: angular.element($document[0].body),
964 multiple: true, 974 multiple: true,
@@ -16,10 +16,12 @@ @@ -16,10 +16,12 @@
16 import ImportExport from './import-export.service'; 16 import ImportExport from './import-export.service';
17 import ImportDialogController from './import-dialog.controller'; 17 import ImportDialogController from './import-dialog.controller';
18 import ImportDialogCSVController from './import-dialog-csv.controller'; 18 import ImportDialogCSVController from './import-dialog-csv.controller';
  19 +import TableColumnsAssignment from './table-columns-assignment.directive';
19 20
20 21
21 export default angular.module('thingsboard.importexport', []) 22 export default angular.module('thingsboard.importexport', [])
22 .factory('importExport', ImportExport) 23 .factory('importExport', ImportExport)
23 .controller('ImportDialogController', ImportDialogController) 24 .controller('ImportDialogController', ImportDialogController)
24 .controller('ImportDialogCSVController', ImportDialogCSVController) 25 .controller('ImportDialogCSVController', ImportDialogCSVController)
  26 + .directive('tbTableColumnsAssignment', TableColumnsAssignment)
25 .name; 27 .name;
  1 +/*
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +// import './timeinterval.scss';
  17 +
  18 +/* eslint-disable import/no-unresolved, import/default */
  19 +
  20 +import tableColumnsAssignment from './table-columns-assignment.tpl.html';
  21 +
  22 +/* eslint-enable import/no-unresolved, import/default */
  23 +
  24 +/*@ngInject*/
  25 +export default function TableColumnsAssignment() {
  26 + return {
  27 + restrict: "E",
  28 + scope: true,
  29 + bindToController: {
  30 + theForm: '=?',
  31 + columns: '=',
  32 + entityType: '=',
  33 + },
  34 + templateUrl: tableColumnsAssignment,
  35 + controller: TableColumnsAssignmentController,
  36 + controllerAs: 'vm'
  37 + };
  38 +}
  39 +
  40 +/*@ngInject*/
  41 +function TableColumnsAssignmentController($scope, types, $timeout) {
  42 + var vm = this;
  43 +
  44 + vm.columnTypes = {};
  45 + vm.entityField = {};
  46 +
  47 + switch (vm.entityType) {
  48 + case types.entityType.device:
  49 + vm.columnTypes.sharedAttribute = types.entityGroup.columnType.sharedAttribute;
  50 + vm.columnTypes.serverAttribute = types.entityGroup.columnType.serverAttribute;
  51 + vm.columnTypes.timeseries = types.entityGroup.columnType.timeseries;
  52 + vm.columnTypes.entityField = types.entityGroup.columnType.entityField;
  53 + break;
  54 + }
  55 +
  56 + vm.entityField.name = types.entityGroup.entityField.name;
  57 +
  58 + switch (vm.entityType) {
  59 + case types.entityType.device:
  60 + vm.entityField.type = types.entityGroup.entityField.type;
  61 + // vm.entityField.assigned_customer = types.entityGroup.entityField.assigned_customer;
  62 + break;
  63 + }
  64 +
  65 + $scope.$watch('vm.columns', function(newVal, prevVal){
  66 + if (newVal && !angular.equals(newVal, prevVal)) {
  67 + var isSelectName = false;
  68 + var isSelectType = false;
  69 + for (var i = 0; i < newVal.length; i++) {
  70 + if (newVal[i].type === types.entityGroup.columnType.entityField.value &&
  71 + newVal[i].key === types.entityGroup.entityField.name.value) {
  72 + isSelectName = true;
  73 + }
  74 + if (newVal[i].type === types.entityGroup.columnType.entityField.value &&
  75 + newVal[i].key === types.entityGroup.entityField.type.value) {
  76 + isSelectType = true;
  77 + }
  78 + }
  79 + $timeout(function () {
  80 + vm.entityField.name.disable = isSelectName;
  81 + vm.entityField.type.disable = isSelectType;
  82 + });
  83 + }
  84 + }, true);
  85 +
  86 + $scope.$watch('vm.columns', function(newVal, prevVal) {
  87 + if (vm.isEdit && !angular.equals(newVal, prevVal)) {
  88 + vm.theForm.$setDirty();
  89 + }
  90 + }, true);
  91 +}
  1 +<!--
  2 +
  3 + Copyright © 2016-2019 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<md-table-container flex class="tb-table-select">
  19 + <table md-table>
  20 + <thead md-head>
  21 + <tr md-row>
  22 + <th md-column>&nbsp</th>
  23 + <th md-column>Example value data</th>
  24 + <th md-column style="min-width: 140px">Column type</th>
  25 + <th md-column style="min-width: 140px">Value</th>
  26 + </tr>
  27 + </thead>
  28 + <tbody md-body>
  29 + <tr md-row ng-repeat="column in vm.columns track by $index">
  30 + <td md-cell>{{$index + 1}}</td>
  31 + <td md-cell>{{column.sampleData}}</td>
  32 + <td md-cell>
  33 + <md-select ng-model="column.type" required name="columnType"
  34 + aria-label="{{ 'entity-group.column-type' | translate }}">
  35 + <md-option ng-repeat="type in vm.columnTypes" ng-value="type.value">
  36 + {{type.name | translate}}
  37 + </md-option>
  38 + </md-select>
  39 + </td>
  40 + <td md-cell>
  41 + <md-select ng-if="column.type == vm.columnTypes.entityField.value"
  42 + required name="columnKey" ng-model="column.key"
  43 + aria-label="{{ 'entity-group.column-value' | translate }}">
  44 + <md-option ng-repeat="field in vm.entityField" ng-value="field.value" ng-disabled="field.disable">
  45 + {{field.name | translate}}
  46 + </md-option>
  47 + </md-select>
  48 + <md-input-container md-no-float
  49 + ng-if="column.type != vm.columnTypes.entityField.value &&
  50 + column.type != vm.columnTypes.name.value &&
  51 + column.type != vm.columnTypes.type.value">
  52 + <input required name="columnKeyName"
  53 + placeholder="{{ 'entity-group.column-value' | translate }}"
  54 + ng-model="column.key"
  55 + aria-label="{{ 'entity-group.column-value' | translate }}">
  56 + </md-input-container>
  57 + </td>
  58 + </tr>
  59 + </tbody>
  60 + </table>
  61 + <md-divider></md-divider>
  62 +</md-table-container>
@@ -776,13 +776,16 @@ @@ -776,13 +776,16 @@
776 "details": "Entity details", 776 "details": "Entity details",
777 "no-entities-prompt": "No entities found", 777 "no-entities-prompt": "No entities found",
778 "no-data": "No data to display", 778 "no-data": "No data to display",
779 - "columns-to-display": "Columns to Display" 779 + "columns-to-display": "Columns to Display",
  780 + "import-csv-number-columns-error": "A file should contain at least two columns",
  781 + "import-csv-invalid-format-error": "Invalid file format. Line: '{{line}}'"
780 }, 782 },
781 "entity-group": { 783 "entity-group": {
782 "column-value": "Value", 784 "column-value": "Value",
783 "column-title": "Title", 785 "column-title": "Title",
784 "column-type": { 786 "column-type": {
785 "column-type": "Column type", 787 "column-type": "Column type",
  788 + "client-attribute": "Client attribute",
786 "shared-attribute": "Shared attribute", 789 "shared-attribute": "Shared attribute",
787 "server-attribute": "Server attribute", 790 "server-attribute": "Server attribute",
788 "timeseries": "Timeseries", 791 "timeseries": "Timeseries",