Commit 07cd8b9978b810cd2c0bffcaad453fcab9a09233
1 parent
62f43369
Fix dashboard device button, clear code and counted all statistical info
Showing
11 changed files
with
332 additions
and
160 deletions
... | ... | @@ -238,23 +238,23 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) |
238 | 238 | function success() { |
239 | 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 | 245 | } else { |
246 | 246 | deferred.resolve(response.data); |
247 | 247 | } |
248 | - }, function fail(response) { | |
249 | - deferred.reject(response); | |
248 | + }, function fail() { | |
249 | + deferred.reject(); | |
250 | 250 | }); |
251 | 251 | } else if (deleteEntityAttributesPromise) { |
252 | 252 | deleteEntityAttributesPromise.then( |
253 | 253 | function success() { |
254 | 254 | deferred.resolve(); |
255 | 255 | }, |
256 | - function fail(response) { | |
257 | - deferred.reject(response); | |
256 | + function fail() { | |
257 | + deferred.reject(); | |
258 | 258 | } |
259 | 259 | ) |
260 | 260 | } else { |
... | ... | @@ -287,23 +287,23 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) |
287 | 287 | function success() { |
288 | 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 | 294 | } else { |
295 | 295 | deferred.resolve(response.data); |
296 | 296 | } |
297 | - }, function fail(response) { | |
298 | - deferred.reject(response); | |
297 | + }, function fail() { | |
298 | + deferred.reject(); | |
299 | 299 | }); |
300 | 300 | } else if (deleteEntityTimeseriesPromise) { |
301 | 301 | deleteEntityTimeseriesPromise.then( |
302 | 302 | function success() { |
303 | 303 | deferred.resolve(); |
304 | 304 | }, |
305 | - function fail(response) { | |
306 | - deferred.reject(response); | |
305 | + function fail() { | |
306 | + deferred.reject(); | |
307 | 307 | } |
308 | 308 | ) |
309 | 309 | } else { |
... | ... | @@ -325,8 +325,8 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) |
325 | 325 | var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/' + attributeScope + '?keys=' + keys; |
326 | 326 | $http.delete(url, config).then(function success() { |
327 | 327 | deferred.resolve(); |
328 | - }, function fail(response) { | |
329 | - deferred.reject(response); | |
328 | + }, function fail() { | |
329 | + deferred.reject(); | |
330 | 330 | }); |
331 | 331 | return deferred.promise; |
332 | 332 | } |
... | ... | @@ -344,8 +344,8 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) |
344 | 344 | var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/delete' + '?keys=' + keys; |
345 | 345 | $http.delete(url, config).then(function success() { |
346 | 346 | deferred.resolve(); |
347 | - }, function fail(response) { | |
348 | - deferred.reject(response); | |
347 | + }, function fail() { | |
348 | + deferred.reject(); | |
349 | 349 | }); |
350 | 350 | return deferred.promise; |
351 | 351 | } | ... | ... |
... | ... | @@ -43,8 +43,7 @@ function DeviceService($http, $q, $window, userService, attributeService, custom |
43 | 43 | sendOneWayRpcCommand: sendOneWayRpcCommand, |
44 | 44 | sendTwoWayRpcCommand: sendTwoWayRpcCommand, |
45 | 45 | findByQuery: findByQuery, |
46 | - getDeviceTypes: getDeviceTypes, | |
47 | - findByName: findByName | |
46 | + getDeviceTypes: getDeviceTypes | |
48 | 47 | } |
49 | 48 | |
50 | 49 | return service; |
... | ... | @@ -167,8 +166,8 @@ function DeviceService($http, $q, $window, userService, attributeService, custom |
167 | 166 | var url = '/api/device'; |
168 | 167 | $http.post(url, device, config).then(function success(response) { |
169 | 168 | deferred.resolve(response.data); |
170 | - }, function fail(response) { | |
171 | - deferred.reject(response); | |
169 | + }, function fail() { | |
170 | + deferred.reject(); | |
172 | 171 | }); |
173 | 172 | return deferred.promise; |
174 | 173 | } |
... | ... | @@ -178,18 +177,36 @@ function DeviceService($http, $q, $window, userService, attributeService, custom |
178 | 177 | let attributesType = Object.keys(types.attributesScope); |
179 | 178 | let allPromise = []; |
180 | 179 | let promise = ""; |
180 | + let statisticalInfo = {}; | |
181 | 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 | 193 | allPromise.push(promise); |
185 | 194 | } |
186 | 195 | } |
187 | 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 | 206 | allPromise.push(promise); |
190 | 207 | } |
191 | 208 | $q.all(allPromise).then(function success() { |
192 | - deferred.resolve(); | |
209 | + deferred.resolve(statisticalInfo); | |
193 | 210 | }); |
194 | 211 | return deferred.promise; |
195 | 212 | } |
... | ... | @@ -197,26 +214,28 @@ function DeviceService($http, $q, $window, userService, attributeService, custom |
197 | 214 | function saveDeviceParameters(deviceParameters, update, config) { |
198 | 215 | config = config || {}; |
199 | 216 | const deferred = $q.defer(); |
200 | - let statisticalInfo = { | |
201 | - create: {}, | |
202 | - update: {}, | |
203 | - error: {} | |
204 | - }; | |
217 | + let statisticalInfo = {}; | |
205 | 218 | let newDevice = { |
206 | 219 | name: deviceParameters.name, |
207 | 220 | type: deviceParameters.type |
208 | 221 | }; |
209 | 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 | 228 | deferred.resolve(statisticalInfo); |
213 | 229 | }); |
214 | - }, function fail(response) { | |
215 | - console.log(response); // eslint-disable-line | |
230 | + }, function fail() { | |
216 | 231 | if (update) { |
217 | 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 | 239 | deferred.resolve(statisticalInfo); |
221 | 240 | }); |
222 | 241 | }, function fail() { |
... | ... | @@ -384,8 +403,8 @@ function DeviceService($http, $q, $window, userService, attributeService, custom |
384 | 403 | var url = '/api/tenant/devices?deviceName=' + deviceName; |
385 | 404 | $http.get(url, config).then(function success(response) { |
386 | 405 | deferred.resolve(response.data); |
387 | - }, function fail(response) { | |
388 | - deferred.reject(response); | |
406 | + }, function fail() { | |
407 | + deferred.reject(); | |
389 | 408 | }); |
390 | 409 | return deferred.promise; |
391 | 410 | } | ... | ... |
... | ... | @@ -352,6 +352,10 @@ export default angular.module('thingsboard.types', []) |
352 | 352 | }, |
353 | 353 | entityGroup: { |
354 | 354 | columnType: { |
355 | + clientAttribute: { | |
356 | + name: 'entity-group.column-type.client-attribute', | |
357 | + value: 'CLIENT_ATTRIBUTE' | |
358 | + }, | |
355 | 359 | sharedAttribute: { |
356 | 360 | name: 'entity-group.column-type.shared-attribute', |
357 | 361 | value: 'SHARED_ATTRIBUTE' | ... | ... |
... | ... | @@ -68,7 +68,7 @@ export function DeviceController($rootScope, userService, deviceService, custome |
68 | 68 | }, |
69 | 69 | { |
70 | 70 | onAction: function ($event) { |
71 | - importExport.importDevices($event).then( | |
71 | + importExport.importDevices($event, types.entityType.device).then( | |
72 | 72 | function() { |
73 | 73 | vm.grid.refreshList(); |
74 | 74 | } |
... | ... | @@ -107,7 +107,6 @@ export function DeviceController($rootScope, userService, deviceService, custome |
107 | 107 | |
108 | 108 | addItemTemplateUrl: addDeviceTemplate, |
109 | 109 | |
110 | - // addItemText: function() { return $translate.instant('device.add-device-text') }, | |
111 | 110 | noItemsText: function() { return $translate.instant('device.no-devices-text') }, |
112 | 111 | itemDetailsText: function() { return $translate.instant('device.device-details') }, |
113 | 112 | isDetailsReadOnly: isCustomerUser, |
... | ... | @@ -338,6 +337,7 @@ export function DeviceController($rootScope, userService, deviceService, custome |
338 | 337 | icon: "add" |
339 | 338 | }; |
340 | 339 | |
340 | + vm.deviceGridConfig.addItemActions = []; | |
341 | 341 | |
342 | 342 | } else if (vm.devicesScope === 'customer_user') { |
343 | 343 | deviceActionsList.push( |
... | ... | @@ -352,6 +352,7 @@ export function DeviceController($rootScope, userService, deviceService, custome |
352 | 352 | ); |
353 | 353 | |
354 | 354 | vm.deviceGridConfig.addItemAction = {}; |
355 | + vm.deviceGridConfig.addItemActions = []; | |
355 | 356 | } |
356 | 357 | } |
357 | 358 | ... | ... |
... | ... | @@ -16,7 +16,7 @@ |
16 | 16 | import './import-dialog.scss'; |
17 | 17 | |
18 | 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 | 21 | var vm = this; |
22 | 22 | |
... | ... | @@ -27,63 +27,80 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo |
27 | 27 | |
28 | 28 | vm.addDevices = addDevices; |
29 | 29 | vm.importParams = { |
30 | + delim: ',', | |
30 | 31 | isUpdate: true, |
31 | 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 | 81 | vm.importTitle = importTitle; |
35 | 82 | vm.importFileLabel = importFileLabel; |
83 | + vm.entityType = entityType; | |
36 | 84 | |
37 | 85 | vm.columnsParam = []; |
38 | 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 | 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 | 104 | function fileAdded($file) { |
88 | 105 | if ($file.getExtension() === 'csv') { |
89 | 106 | var reader = new FileReader(); |
... | ... | @@ -94,9 +111,8 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo |
94 | 111 | var importCSV = event.target.result; |
95 | 112 | if (importCSV && importCSV.length > 0) { |
96 | 113 | try { |
97 | - vm.importData = importCSV; | |
114 | + parseCSV(importCSV); | |
98 | 115 | vm.fileName = $file.name; |
99 | - parseCSVData(vm.importData); | |
100 | 116 | } catch (err) { |
101 | 117 | vm.fileName = null; |
102 | 118 | toast.showError(err.message); |
... | ... | @@ -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 | 129 | var columnParam = {}; |
114 | 130 | var config = { |
115 | 131 | delim: vm.importParams.delim, |
... | ... | @@ -202,10 +218,14 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo |
202 | 218 | function clearFile() { |
203 | 219 | $scope.theForm.$setDirty(); |
204 | 220 | vm.fileName = null; |
205 | - vm.importData = null; | |
221 | + parseData = null; | |
206 | 222 | vm.columnsParam = []; |
207 | 223 | } |
208 | 224 | |
225 | + function cancel() { | |
226 | + $mdDialog.cancel(); | |
227 | + } | |
228 | + | |
209 | 229 | function importFromJson() { |
210 | 230 | $scope.theForm.$setPristine(); |
211 | 231 | $mdDialog.hide(vm.importData); | ... | ... |
... | ... | @@ -63,66 +63,26 @@ |
63 | 63 | </div> |
64 | 64 | </fieldset> |
65 | 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 | 73 | </md-input-container> |
70 | 74 | <md-input-container class="md-block"> |
71 | 75 | <md-checkbox ng-model="vm.importParams.isHeader" aria-label="Checkbox 1"> |
72 | - Use first line is header | |
76 | + First line is header | |
73 | 77 | </md-checkbox> |
74 | 78 | </md-input-container> |
75 | 79 | <md-input-container class="md-block"> |
76 | 80 | <md-checkbox ng-model="vm.importParams.isUpdate" aria-label="Checkbox 1"> |
77 | - Update parameter device | |
81 | + Update parameters | |
78 | 82 | </md-checkbox> |
79 | 83 | </md-input-container> |
80 | 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> </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 | 86 | </div> |
127 | 87 | </md-dialog-content> |
128 | 88 | <md-dialog-actions layout="row"> | ... | ... |
... | ... | @@ -578,9 +578,9 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, |
578 | 578 | return deferred.promise; |
579 | 579 | } |
580 | 580 | |
581 | - function importDevices($event) { | |
581 | + function importDevices($event, entityType) { | |
582 | 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 | 584 | function success() { |
585 | 585 | // if (!validateImportedDashboard(dashboard)) { |
586 | 586 | // toast.showError($translate.instant('dashboard.invalid-dashboard-file-error')); |
... | ... | @@ -783,10 +783,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, |
783 | 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 | 786 | function splitCSV(str, sep) { |
791 | 787 | for (var foo = str.split(sep = sep || ","), x = foo.length - 1, tl; x >= 0; x--) { |
792 | 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 | 801 | return !isNaN(parseFloat(str)) && isFinite(str); |
806 | 802 | } |
807 | 803 | |
808 | - function parseStringToFormatJS(str) { | |
804 | + function convertStringToJSType(str) { | |
809 | 805 | if (isNumeric(str.replace(',', '.'))) { |
810 | 806 | return parseFloat(str.replace(',', '.')); |
811 | 807 | } |
... | ... | @@ -827,7 +823,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, |
827 | 823 | let csvlines = csvdata.split(/[\r\n]+/); |
828 | 824 | let csvheaders = splitCSV(csvlines[0], delim); |
829 | 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 | 827 | return -1; |
832 | 828 | } |
833 | 829 | let csvrows = header ? csvlines.slice(1, csvlines.length) : csvlines; |
... | ... | @@ -844,34 +840,47 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, |
844 | 840 | |
845 | 841 | let rowitems = splitCSV(row, delim); |
846 | 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 | 844 | return -1; |
849 | 845 | } |
850 | 846 | for (let i = 0; i < rowitems.length; i++) { |
851 | - rowitems[i] = parseStringToFormatJS(rowitems[i]); | |
847 | + rowitems[i] = convertStringToJSType(rowitems[i]); | |
852 | 848 | } |
853 | 849 | result.rows.push(rowitems); |
854 | 850 | } |
855 | 851 | } |
856 | 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 | 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 | 871 | switch (entityType) { |
863 | 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 | 875 | allPromise.push(promise); |
867 | 876 | } |
868 | 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 | 885 | return deferred.promise; |
877 | 886 | } |
... | ... | @@ -950,7 +959,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, |
950 | 959 | return deferred.promise; |
951 | 960 | } |
952 | 961 | |
953 | - function openImportDialogCSV($event, importTitle, importFileLabel) { | |
962 | + function openImportDialogCSV($event, entityType, importTitle, importFileLabel) { | |
954 | 963 | var deferred = $q.defer(); |
955 | 964 | $mdDialog.show({ |
956 | 965 | controller: 'ImportDialogCSVController', |
... | ... | @@ -958,7 +967,8 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, |
958 | 967 | templateUrl: importDialogCSVTemplate, |
959 | 968 | locals: { |
960 | 969 | importTitle: importTitle, |
961 | - importFileLabel: importFileLabel | |
970 | + importFileLabel: importFileLabel, | |
971 | + entityType: entityType | |
962 | 972 | }, |
963 | 973 | parent: angular.element($document[0].body), |
964 | 974 | multiple: true, | ... | ... |
... | ... | @@ -16,10 +16,12 @@ |
16 | 16 | import ImportExport from './import-export.service'; |
17 | 17 | import ImportDialogController from './import-dialog.controller'; |
18 | 18 | import ImportDialogCSVController from './import-dialog-csv.controller'; |
19 | +import TableColumnsAssignment from './table-columns-assignment.directive'; | |
19 | 20 | |
20 | 21 | |
21 | 22 | export default angular.module('thingsboard.importexport', []) |
22 | 23 | .factory('importExport', ImportExport) |
23 | 24 | .controller('ImportDialogController', ImportDialogController) |
24 | 25 | .controller('ImportDialogCSVController', ImportDialogCSVController) |
26 | + .directive('tbTableColumnsAssignment', TableColumnsAssignment) | |
25 | 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> </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 | 776 | "details": "Entity details", |
777 | 777 | "no-entities-prompt": "No entities found", |
778 | 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 | 783 | "entity-group": { |
782 | 784 | "column-value": "Value", |
783 | 785 | "column-title": "Title", |
784 | 786 | "column-type": { |
785 | 787 | "column-type": "Column type", |
788 | + "client-attribute": "Client attribute", | |
786 | 789 | "shared-attribute": "Shared attribute", |
787 | 790 | "server-attribute": "Server attribute", |
788 | 791 | "timeseries": "Timeseries", | ... | ... |