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 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>&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 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>&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 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",
... ...