Showing
20 changed files
with
1187 additions
and
67 deletions
@@ -8375,6 +8375,10 @@ | @@ -8375,6 +8375,10 @@ | ||
8375 | "integrity": "sha1-mnHEh0chjrylHlGmbaaCA4zct78=", | 8375 | "integrity": "sha1-mnHEh0chjrylHlGmbaaCA4zct78=", |
8376 | "dev": true | 8376 | "dev": true |
8377 | }, | 8377 | }, |
8378 | + "material-steppers": { | ||
8379 | + "version": "git://github.com/thingsboard/material-steppers.git#ee6241d63b97b7c4a3f3b240cdb347c02a3dcb0a", | ||
8380 | + "from": "git://github.com/thingsboard/material-steppers.git#master" | ||
8381 | + }, | ||
8378 | "material-ui": { | 8382 | "material-ui": { |
8379 | "version": "0.16.7", | 8383 | "version": "0.16.7", |
8380 | "resolved": "https://registry.npmjs.org/material-ui/-/material-ui-0.16.7.tgz", | 8384 | "resolved": "https://registry.npmjs.org/material-ui/-/material-ui-0.16.7.tgz", |
@@ -65,6 +65,7 @@ | @@ -65,6 +65,7 @@ | ||
65 | "leaflet": "^1.5.1", | 65 | "leaflet": "^1.5.1", |
66 | "leaflet-polylinedecorator": "^1.6.0", | 66 | "leaflet-polylinedecorator": "^1.6.0", |
67 | "leaflet-providers": "^1.8.0", | 67 | "leaflet-providers": "^1.8.0", |
68 | + "material-steppers": "git://github.com/thingsboard/material-steppers.git#master", | ||
68 | "material-ui": "^0.16.1", | 69 | "material-ui": "^0.16.1", |
69 | "material-ui-number-input": "^5.0.16", | 70 | "material-ui-number-input": "^5.0.16", |
70 | "md-color-picker": "0.2.6", | 71 | "md-color-picker": "0.2.6", |
@@ -32,7 +32,8 @@ function AssetService($http, $q, customerService, userService) { | @@ -32,7 +32,8 @@ function AssetService($http, $q, customerService, userService) { | ||
32 | getCustomerAssets: getCustomerAssets, | 32 | getCustomerAssets: getCustomerAssets, |
33 | findByQuery: findByQuery, | 33 | findByQuery: findByQuery, |
34 | fetchAssetsByNameFilter: fetchAssetsByNameFilter, | 34 | fetchAssetsByNameFilter: fetchAssetsByNameFilter, |
35 | - getAssetTypes: getAssetTypes | 35 | + getAssetTypes: getAssetTypes, |
36 | + findByName: findByName | ||
36 | } | 37 | } |
37 | 38 | ||
38 | return service; | 39 | return service; |
@@ -276,4 +277,16 @@ function AssetService($http, $q, customerService, userService) { | @@ -276,4 +277,16 @@ function AssetService($http, $q, customerService, userService) { | ||
276 | return deferred.promise; | 277 | return deferred.promise; |
277 | } | 278 | } |
278 | 279 | ||
280 | + function findByName(assetName, config) { | ||
281 | + config = config || {}; | ||
282 | + var deferred = $q.defer(); | ||
283 | + var url = '/api/tenant/assets?assetName=' + assetName; | ||
284 | + $http.get(url, config).then(function success(response) { | ||
285 | + deferred.resolve(response.data); | ||
286 | + }, function fail() { | ||
287 | + deferred.reject(); | ||
288 | + }); | ||
289 | + return deferred.promise; | ||
290 | + } | ||
291 | + | ||
279 | } | 292 | } |
@@ -30,7 +30,9 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) | @@ -30,7 +30,9 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) | ||
30 | subscribeForEntityAttributes: subscribeForEntityAttributes, | 30 | subscribeForEntityAttributes: subscribeForEntityAttributes, |
31 | unsubscribeForEntityAttributes: unsubscribeForEntityAttributes, | 31 | unsubscribeForEntityAttributes: unsubscribeForEntityAttributes, |
32 | saveEntityAttributes: saveEntityAttributes, | 32 | saveEntityAttributes: saveEntityAttributes, |
33 | - deleteEntityAttributes: deleteEntityAttributes | 33 | + deleteEntityAttributes: deleteEntityAttributes, |
34 | + saveEntityTimeseries: saveEntityTimeseries, | ||
35 | + deleteEntityTimeseries: deleteEntityTimeseries | ||
34 | } | 36 | } |
35 | 37 | ||
36 | return service; | 38 | return service; |
@@ -212,7 +214,8 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) | @@ -212,7 +214,8 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) | ||
212 | } | 214 | } |
213 | } | 215 | } |
214 | 216 | ||
215 | - function saveEntityAttributes(entityType, entityId, attributeScope, attributes) { | 217 | + function saveEntityAttributes(entityType, entityId, attributeScope, attributes, config) { |
218 | + config = config || {}; | ||
216 | var deferred = $q.defer(); | 219 | var deferred = $q.defer(); |
217 | var attributesData = {}; | 220 | var attributesData = {}; |
218 | var deleteAttributes = []; | 221 | var deleteAttributes = []; |
@@ -229,7 +232,7 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) | @@ -229,7 +232,7 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) | ||
229 | } | 232 | } |
230 | if (Object.keys(attributesData).length) { | 233 | if (Object.keys(attributesData).length) { |
231 | var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/' + attributeScope; | 234 | var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/' + attributeScope; |
232 | - $http.post(url, attributesData).then(function success(response) { | 235 | + $http.post(url, attributesData, config).then(function success(response) { |
233 | if (deleteEntityAttributesPromise) { | 236 | if (deleteEntityAttributesPromise) { |
234 | deleteEntityAttributesPromise.then( | 237 | deleteEntityAttributesPromise.then( |
235 | function success() { | 238 | function success() { |
@@ -260,7 +263,57 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) | @@ -260,7 +263,57 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) | ||
260 | return deferred.promise; | 263 | return deferred.promise; |
261 | } | 264 | } |
262 | 265 | ||
263 | - function deleteEntityAttributes(entityType, entityId, attributeScope, attributes) { | 266 | + function saveEntityTimeseries(entityType, entityId, timeseriesScope, timeseries, config) { |
267 | + config = config || {}; | ||
268 | + var deferred = $q.defer(); | ||
269 | + var timeseriesData = {}; | ||
270 | + var deleteTimeseries = []; | ||
271 | + for (var a=0; a<timeseries.length;a++) { | ||
272 | + if (angular.isDefined(timeseries[a].value) && timeseries[a].value !== null) { | ||
273 | + timeseriesData[timeseries[a].key] = timeseries[a].value; | ||
274 | + } else { | ||
275 | + deleteTimeseries.push(timeseries[a]); | ||
276 | + } | ||
277 | + } | ||
278 | + var deleteEntityTimeseriesPromise; | ||
279 | + if (deleteTimeseries.length) { | ||
280 | + deleteEntityTimeseriesPromise = deleteEntityTimeseries(entityType, entityId, deleteTimeseries, config); | ||
281 | + } | ||
282 | + if (Object.keys(timeseriesData).length) { | ||
283 | + var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/' + timeseriesScope; | ||
284 | + $http.post(url, timeseriesData, config).then(function success(response) { | ||
285 | + if (deleteEntityTimeseriesPromise) { | ||
286 | + deleteEntityTimeseriesPromise.then( | ||
287 | + function success() { | ||
288 | + deferred.resolve(response.data); | ||
289 | + }, | ||
290 | + function fail() { | ||
291 | + deferred.reject(); | ||
292 | + } | ||
293 | + ) | ||
294 | + } else { | ||
295 | + deferred.resolve(response.data); | ||
296 | + } | ||
297 | + }, function fail() { | ||
298 | + deferred.reject(); | ||
299 | + }); | ||
300 | + } else if (deleteEntityTimeseriesPromise) { | ||
301 | + deleteEntityTimeseriesPromise.then( | ||
302 | + function success() { | ||
303 | + deferred.resolve(); | ||
304 | + }, | ||
305 | + function fail() { | ||
306 | + deferred.reject(); | ||
307 | + } | ||
308 | + ) | ||
309 | + } else { | ||
310 | + deferred.resolve(); | ||
311 | + } | ||
312 | + return deferred.promise; | ||
313 | + } | ||
314 | + | ||
315 | + function deleteEntityAttributes(entityType, entityId, attributeScope, attributes, config) { | ||
316 | + config = config || {}; | ||
264 | var deferred = $q.defer(); | 317 | var deferred = $q.defer(); |
265 | var keys = ''; | 318 | var keys = ''; |
266 | for (var i = 0; i < attributes.length; i++) { | 319 | for (var i = 0; i < attributes.length; i++) { |
@@ -270,7 +323,7 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) | @@ -270,7 +323,7 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) | ||
270 | keys += attributes[i].key; | 323 | keys += attributes[i].key; |
271 | } | 324 | } |
272 | var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/' + attributeScope + '?keys=' + keys; | 325 | var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/' + attributeScope + '?keys=' + keys; |
273 | - $http.delete(url).then(function success() { | 326 | + $http.delete(url, config).then(function success() { |
274 | deferred.resolve(); | 327 | deferred.resolve(); |
275 | }, function fail() { | 328 | }, function fail() { |
276 | deferred.reject(); | 329 | deferred.reject(); |
@@ -278,5 +331,23 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) | @@ -278,5 +331,23 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) | ||
278 | return deferred.promise; | 331 | return deferred.promise; |
279 | } | 332 | } |
280 | 333 | ||
334 | + function deleteEntityTimeseries(entityType, entityId, timeseries, config) { | ||
335 | + config = config || {}; | ||
336 | + var deferred = $q.defer(); | ||
337 | + var keys = ''; | ||
338 | + for (var i = 0; i < timeseries.length; i++) { | ||
339 | + if (i > 0) { | ||
340 | + keys += ','; | ||
341 | + } | ||
342 | + keys += timeseries[i].key; | ||
343 | + } | ||
344 | + var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/delete' + '?keys=' + keys; | ||
345 | + $http.delete(url, config).then(function success() { | ||
346 | + deferred.resolve(); | ||
347 | + }, function fail() { | ||
348 | + deferred.reject(); | ||
349 | + }); | ||
350 | + return deferred.promise; | ||
351 | + } | ||
281 | 352 | ||
282 | } | 353 | } |
@@ -42,8 +42,9 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | @@ -42,8 +42,9 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | ||
42 | sendOneWayRpcCommand: sendOneWayRpcCommand, | 42 | sendOneWayRpcCommand: sendOneWayRpcCommand, |
43 | sendTwoWayRpcCommand: sendTwoWayRpcCommand, | 43 | sendTwoWayRpcCommand: sendTwoWayRpcCommand, |
44 | findByQuery: findByQuery, | 44 | findByQuery: findByQuery, |
45 | - getDeviceTypes: getDeviceTypes | ||
46 | - } | 45 | + getDeviceTypes: getDeviceTypes, |
46 | + findByName: findByName | ||
47 | + }; | ||
47 | 48 | ||
48 | return service; | 49 | return service; |
49 | 50 | ||
@@ -124,7 +125,7 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | @@ -124,7 +125,7 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | ||
124 | if (!config) { | 125 | if (!config) { |
125 | config = {}; | 126 | config = {}; |
126 | } | 127 | } |
127 | - config = Object.assign(config, { ignoreErrors: ignoreErrors }); | 128 | + config = Object.assign(config, {ignoreErrors: ignoreErrors}); |
128 | $http.get(url, config).then(function success(response) { | 129 | $http.get(url, config).then(function success(response) { |
129 | deferred.resolve(response.data); | 130 | deferred.resolve(response.data); |
130 | }, function fail(response) { | 131 | }, function fail(response) { |
@@ -136,8 +137,8 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | @@ -136,8 +137,8 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | ||
136 | function getDevices(deviceIds, config) { | 137 | function getDevices(deviceIds, config) { |
137 | var deferred = $q.defer(); | 138 | var deferred = $q.defer(); |
138 | var ids = ''; | 139 | var ids = ''; |
139 | - for (var i=0;i<deviceIds.length;i++) { | ||
140 | - if (i>0) { | 140 | + for (var i = 0; i < deviceIds.length; i++) { |
141 | + if (i > 0) { | ||
141 | ids += ','; | 142 | ids += ','; |
142 | } | 143 | } |
143 | ids += deviceIds[i]; | 144 | ids += deviceIds[i]; |
@@ -146,11 +147,11 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | @@ -146,11 +147,11 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | ||
146 | $http.get(url, config).then(function success(response) { | 147 | $http.get(url, config).then(function success(response) { |
147 | var devices = response.data; | 148 | var devices = response.data; |
148 | devices.sort(function (device1, device2) { | 149 | devices.sort(function (device1, device2) { |
149 | - var id1 = device1.id.id; | ||
150 | - var id2 = device2.id.id; | ||
151 | - var index1 = deviceIds.indexOf(id1); | ||
152 | - var index2 = deviceIds.indexOf(id2); | ||
153 | - return index1 - index2; | 150 | + var id1 = device1.id.id; |
151 | + var id2 = device2.id.id; | ||
152 | + var index1 = deviceIds.indexOf(id1); | ||
153 | + var index2 = deviceIds.indexOf(id2); | ||
154 | + return index1 - index2; | ||
154 | }); | 155 | }); |
155 | deferred.resolve(devices); | 156 | deferred.resolve(devices); |
156 | }, function fail(response) { | 157 | }, function fail(response) { |
@@ -159,10 +160,11 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | @@ -159,10 +160,11 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | ||
159 | return deferred.promise; | 160 | return deferred.promise; |
160 | } | 161 | } |
161 | 162 | ||
162 | - function saveDevice(device) { | 163 | + function saveDevice(device, config) { |
164 | + config = config || {}; | ||
163 | var deferred = $q.defer(); | 165 | var deferred = $q.defer(); |
164 | var url = '/api/device'; | 166 | var url = '/api/device'; |
165 | - $http.post(url, device).then(function success(response) { | 167 | + $http.post(url, device, config).then(function success(response) { |
166 | deferred.resolve(response.data); | 168 | deferred.resolve(response.data); |
167 | }, function fail() { | 169 | }, function fail() { |
168 | deferred.reject(); | 170 | deferred.reject(); |
@@ -181,7 +183,8 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | @@ -181,7 +183,8 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | ||
181 | return deferred.promise; | 183 | return deferred.promise; |
182 | } | 184 | } |
183 | 185 | ||
184 | - function getDeviceCredentials(deviceId, sync) { | 186 | + function getDeviceCredentials(deviceId, sync, config) { |
187 | + config = config || {}; | ||
185 | var deferred = $q.defer(); | 188 | var deferred = $q.defer(); |
186 | var url = '/api/device/' + deviceId + '/credentials'; | 189 | var url = '/api/device/' + deviceId + '/credentials'; |
187 | if (sync) { | 190 | if (sync) { |
@@ -196,7 +199,7 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | @@ -196,7 +199,7 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | ||
196 | deferred.reject(); | 199 | deferred.reject(); |
197 | } | 200 | } |
198 | } else { | 201 | } else { |
199 | - $http.get(url, null).then(function success(response) { | 202 | + $http.get(url, config).then(function success(response) { |
200 | deferred.resolve(response.data); | 203 | deferred.resolve(response.data); |
201 | }, function fail() { | 204 | }, function fail() { |
202 | deferred.reject(); | 205 | deferred.reject(); |
@@ -205,10 +208,11 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | @@ -205,10 +208,11 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | ||
205 | return deferred.promise; | 208 | return deferred.promise; |
206 | } | 209 | } |
207 | 210 | ||
208 | - function saveDeviceCredentials(deviceCredentials) { | 211 | + function saveDeviceCredentials(deviceCredentials, config) { |
212 | + config = config || {}; | ||
209 | var deferred = $q.defer(); | 213 | var deferred = $q.defer(); |
210 | var url = '/api/device/credentials'; | 214 | var url = '/api/device/credentials'; |
211 | - $http.post(url, deviceCredentials).then(function success(response) { | 215 | + $http.post(url, deviceCredentials, config).then(function success(response) { |
212 | deferred.resolve(response.data); | 216 | deferred.resolve(response.data); |
213 | }, function fail() { | 217 | }, function fail() { |
214 | deferred.reject(); | 218 | deferred.reject(); |
@@ -297,7 +301,7 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | @@ -297,7 +301,7 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | ||
297 | if (!config) { | 301 | if (!config) { |
298 | config = {}; | 302 | config = {}; |
299 | } | 303 | } |
300 | - config = Object.assign(config, { ignoreErrors: ignoreErrors }); | 304 | + config = Object.assign(config, {ignoreErrors: ignoreErrors}); |
301 | $http.post(url, query, config).then(function success(response) { | 305 | $http.post(url, query, config).then(function success(response) { |
302 | deferred.resolve(response.data); | 306 | deferred.resolve(response.data); |
303 | }, function fail() { | 307 | }, function fail() { |
@@ -317,4 +321,15 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | @@ -317,4 +321,15 @@ function DeviceService($http, $q, $window, userService, attributeService, custom | ||
317 | return deferred.promise; | 321 | return deferred.promise; |
318 | } | 322 | } |
319 | 323 | ||
324 | + function findByName(deviceName, config) { | ||
325 | + config = config || {}; | ||
326 | + var deferred = $q.defer(); | ||
327 | + var url = '/api/tenant/devices?deviceName=' + deviceName; | ||
328 | + $http.get(url, config).then(function success(response) { | ||
329 | + deferred.resolve(response.data); | ||
330 | + }, function fail() { | ||
331 | + deferred.reject(); | ||
332 | + }); | ||
333 | + return deferred.promise; | ||
334 | + } | ||
320 | } | 335 | } |
@@ -38,6 +38,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -38,6 +38,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
38 | createAlarmSourceFromSubscriptionInfo: createAlarmSourceFromSubscriptionInfo, | 38 | createAlarmSourceFromSubscriptionInfo: createAlarmSourceFromSubscriptionInfo, |
39 | getRelatedEntities: getRelatedEntities, | 39 | getRelatedEntities: getRelatedEntities, |
40 | saveRelatedEntity: saveRelatedEntity, | 40 | saveRelatedEntity: saveRelatedEntity, |
41 | + saveEntityParameters: saveEntityParameters, | ||
41 | getRelatedEntity: getRelatedEntity, | 42 | getRelatedEntity: getRelatedEntity, |
42 | deleteRelatedEntity: deleteRelatedEntity, | 43 | deleteRelatedEntity: deleteRelatedEntity, |
43 | moveEntity: moveEntity, | 44 | moveEntity: moveEntity, |
@@ -1072,6 +1073,119 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -1072,6 +1073,119 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
1072 | return deferred.promise; | 1073 | return deferred.promise; |
1073 | } | 1074 | } |
1074 | 1075 | ||
1076 | + function saveEntityRelation(entityType, entityId, entityRelation, config) { | ||
1077 | + const deferred = $q.defer(); | ||
1078 | + let attributesType = Object.keys(types.attributesScope); | ||
1079 | + let allPromise = []; | ||
1080 | + let promise = ""; | ||
1081 | + if (entityRelation.accessToken !== "") { | ||
1082 | + promise = deviceService.getDeviceCredentials(entityId.id, null, config).then(function (response) { | ||
1083 | + response.credentialsId = entityRelation.accessToken; | ||
1084 | + response.credentialsType = "ACCESS_TOKEN"; | ||
1085 | + response.credentialsValue = null; | ||
1086 | + return deviceService.saveDeviceCredentials(response, config).catch(function () { | ||
1087 | + return "error"; | ||
1088 | + }); | ||
1089 | + }); | ||
1090 | + allPromise.push(promise) | ||
1091 | + } | ||
1092 | + for (let i = 0; i < attributesType.length; i++) { | ||
1093 | + let attribute = attributesType[i]; | ||
1094 | + if (entityRelation.attributes[attribute] && entityRelation.attributes[attribute].length !== 0) { | ||
1095 | + promise = attributeService.saveEntityAttributes(entityType, entityId.id, types.attributesScope[attribute].value, entityRelation.attributes[attribute], config).catch(function () { | ||
1096 | + return "error"; | ||
1097 | + }); | ||
1098 | + allPromise.push(promise); | ||
1099 | + } | ||
1100 | + } | ||
1101 | + if (entityRelation.timeseries.length !== 0) { | ||
1102 | + promise = attributeService.saveEntityTimeseries(entityType, entityId.id, "time", entityRelation.timeseries, config).catch(function(){ | ||
1103 | + return "error"; | ||
1104 | + }); | ||
1105 | + allPromise.push(promise); | ||
1106 | + } | ||
1107 | + $q.all(allPromise).then(function success(response) { | ||
1108 | + let isResponseHasError = false; | ||
1109 | + for(let i = 0; i < response.length; i++){ | ||
1110 | + if(response[i] === "error"){ | ||
1111 | + isResponseHasError = true; | ||
1112 | + break; | ||
1113 | + } | ||
1114 | + } | ||
1115 | + isResponseHasError ? deferred.reject() : deferred.resolve(); | ||
1116 | + }); | ||
1117 | + return deferred.promise; | ||
1118 | + } | ||
1119 | + | ||
1120 | + function saveEntityParameters(entityType, entityParameters, update, config) { | ||
1121 | + config = config || {}; | ||
1122 | + const deferred = $q.defer(); | ||
1123 | + let statisticalInfo = {}; | ||
1124 | + let newEntity = { | ||
1125 | + name: entityParameters.name, | ||
1126 | + type: entityParameters.type | ||
1127 | + }; | ||
1128 | + let promise; | ||
1129 | + switch (entityType) { | ||
1130 | + case types.entityType.device: | ||
1131 | + promise = deviceService.saveDevice(newEntity, config); | ||
1132 | + break; | ||
1133 | + case types.entityType.asset: | ||
1134 | + promise = assetService.saveAsset(newEntity, true, config); | ||
1135 | + break; | ||
1136 | + } | ||
1137 | + | ||
1138 | + promise.then(function success(response) { | ||
1139 | + saveEntityRelation(entityType, response.id, entityParameters, config).then(function success() { | ||
1140 | + statisticalInfo.create = { | ||
1141 | + entity: 1 | ||
1142 | + }; | ||
1143 | + deferred.resolve(statisticalInfo); | ||
1144 | + }, function fail() { | ||
1145 | + statisticalInfo.error = { | ||
1146 | + entity: 1 | ||
1147 | + }; | ||
1148 | + deferred.resolve(statisticalInfo); | ||
1149 | + }); | ||
1150 | + }, function fail() { | ||
1151 | + if (update) { | ||
1152 | + let findIdEntity; | ||
1153 | + switch (entityType) { | ||
1154 | + case types.entityType.device: | ||
1155 | + findIdEntity = deviceService.findByName(entityParameters.name, config); | ||
1156 | + break; | ||
1157 | + case types.entityType.asset: | ||
1158 | + findIdEntity = assetService.findByName(entityParameters.name, config); | ||
1159 | + break; | ||
1160 | + } | ||
1161 | + findIdEntity.then(function success(response) { | ||
1162 | + saveEntityRelation(entityType, response.id, entityParameters, config).then(function success() { | ||
1163 | + statisticalInfo.update = { | ||
1164 | + entity: 1 | ||
1165 | + }; | ||
1166 | + deferred.resolve(statisticalInfo); | ||
1167 | + }, function fail() { | ||
1168 | + statisticalInfo.error = { | ||
1169 | + entity: 1 | ||
1170 | + }; | ||
1171 | + deferred.resolve(statisticalInfo); | ||
1172 | + }); | ||
1173 | + }, function fail() { | ||
1174 | + statisticalInfo.error = { | ||
1175 | + entity: 1 | ||
1176 | + }; | ||
1177 | + deferred.resolve(statisticalInfo); | ||
1178 | + }); | ||
1179 | + } else { | ||
1180 | + statisticalInfo.error = { | ||
1181 | + entity: 1 | ||
1182 | + }; | ||
1183 | + deferred.resolve(statisticalInfo); | ||
1184 | + } | ||
1185 | + }); | ||
1186 | + return deferred.promise; | ||
1187 | + } | ||
1188 | + | ||
1075 | function getRelatedEntity(entityId, keys, typeTranslatePrefix) { | 1189 | function getRelatedEntity(entityId, keys, typeTranslatePrefix) { |
1076 | var deferred = $q.defer(); | 1190 | var deferred = $q.defer(); |
1077 | getEntityPromise(entityId.entityType, entityId.id, {ignoreLoading: true}).then( | 1191 | getEntityPromise(entityId.entityType, entityId.id, {ignoreLoading: true}).then( |
@@ -54,6 +54,8 @@ import react from 'ngreact'; | @@ -54,6 +54,8 @@ import react from 'ngreact'; | ||
54 | import '@flowjs/ng-flow/dist/ng-flow-standalone.min'; | 54 | import '@flowjs/ng-flow/dist/ng-flow-standalone.min'; |
55 | import 'ngFlowchart/dist/ngFlowchart'; | 55 | import 'ngFlowchart/dist/ngFlowchart'; |
56 | import 'jstree/dist/jstree.min'; | 56 | import 'jstree/dist/jstree.min'; |
57 | +import 'material-steppers/dist/material-steppers'; | ||
58 | +import 'material-steppers/dist/material-steppers.css' | ||
57 | import 'jstree-bootstrap-theme/dist/themes/proton/style.min.css'; | 59 | import 'jstree-bootstrap-theme/dist/themes/proton/style.min.css'; |
58 | import 'typeface-roboto'; | 60 | import 'typeface-roboto'; |
59 | import 'font-awesome/css/font-awesome.min.css'; | 61 | import 'font-awesome/css/font-awesome.min.css'; |
@@ -127,6 +129,7 @@ angular.module('thingsboard', [ | @@ -127,6 +129,7 @@ angular.module('thingsboard', [ | ||
127 | react.name, | 129 | react.name, |
128 | 'flow', | 130 | 'flow', |
129 | 'flowchart', | 131 | 'flowchart', |
132 | + 'mdSteppers', | ||
130 | thingsboardThirdpartyFix, | 133 | thingsboardThirdpartyFix, |
131 | thingsboardTranslateHandler, | 134 | thingsboardTranslateHandler, |
132 | thingsboardLogin, | 135 | thingsboardLogin, |
@@ -48,7 +48,7 @@ export function AssetCardController(types) { | @@ -48,7 +48,7 @@ export function AssetCardController(types) { | ||
48 | 48 | ||
49 | /*@ngInject*/ | 49 | /*@ngInject*/ |
50 | export function AssetController($rootScope, userService, assetService, customerService, $state, $stateParams, | 50 | export function AssetController($rootScope, userService, assetService, customerService, $state, $stateParams, |
51 | - $document, $mdDialog, $q, $translate, types) { | 51 | + $document, $mdDialog, $q, $translate, types, importExport) { |
52 | 52 | ||
53 | var customerId = $stateParams.customerId; | 53 | var customerId = $stateParams.customerId; |
54 | 54 | ||
@@ -56,6 +56,29 @@ export function AssetController($rootScope, userService, assetService, customerS | @@ -56,6 +56,29 @@ export function AssetController($rootScope, userService, assetService, customerS | ||
56 | 56 | ||
57 | var assetGroupActionsList = []; | 57 | var assetGroupActionsList = []; |
58 | 58 | ||
59 | + var assetAddItemActionsList = [ | ||
60 | + { | ||
61 | + onAction: function ($event) { | ||
62 | + vm.grid.addItem($event); | ||
63 | + }, | ||
64 | + name: function() { return $translate.instant('action.add') }, | ||
65 | + details: function() { return $translate.instant('asset.add-asset-text') }, | ||
66 | + icon: "insert_drive_file" | ||
67 | + }, | ||
68 | + { | ||
69 | + onAction: function ($event) { | ||
70 | + importExport.importEntities($event, types.entityType.asset).then( | ||
71 | + function() { | ||
72 | + vm.grid.refreshList(); | ||
73 | + } | ||
74 | + ); | ||
75 | + }, | ||
76 | + name: function() { return $translate.instant('action.import') }, | ||
77 | + details: function() { return $translate.instant('asset.import') }, | ||
78 | + icon: "file_upload" | ||
79 | + } | ||
80 | + ]; | ||
81 | + | ||
59 | var vm = this; | 82 | var vm = this; |
60 | 83 | ||
61 | vm.types = types; | 84 | vm.types = types; |
@@ -77,6 +100,7 @@ export function AssetController($rootScope, userService, assetService, customerS | @@ -77,6 +100,7 @@ export function AssetController($rootScope, userService, assetService, customerS | ||
77 | 100 | ||
78 | actionsList: assetActionsList, | 101 | actionsList: assetActionsList, |
79 | groupActionsList: assetGroupActionsList, | 102 | groupActionsList: assetGroupActionsList, |
103 | + addItemActions: assetAddItemActionsList, | ||
80 | 104 | ||
81 | onGridInited: gridInited, | 105 | onGridInited: gridInited, |
82 | 106 | ||
@@ -294,6 +318,8 @@ export function AssetController($rootScope, userService, assetService, customerS | @@ -294,6 +318,8 @@ export function AssetController($rootScope, userService, assetService, customerS | ||
294 | } else if (vm.assetsScope === 'customer_user') { | 318 | } else if (vm.assetsScope === 'customer_user') { |
295 | vm.assetGridConfig.addItemAction = {}; | 319 | vm.assetGridConfig.addItemAction = {}; |
296 | } | 320 | } |
321 | + vm.assetGridConfig.addItemActions = []; | ||
322 | + | ||
297 | } | 323 | } |
298 | 324 | ||
299 | vm.assetGridConfig.refreshParamsFunc = refreshAssetsParamsFunction; | 325 | vm.assetGridConfig.refreshParamsFunc = refreshAssetsParamsFunction; |
@@ -350,6 +350,40 @@ export default angular.module('thingsboard.types', []) | @@ -350,6 +350,40 @@ export default angular.module('thingsboard.types', []) | ||
350 | rulenode: "RULE_NODE", | 350 | rulenode: "RULE_NODE", |
351 | entityView: "ENTITY_VIEW" | 351 | entityView: "ENTITY_VIEW" |
352 | }, | 352 | }, |
353 | + importEntityColumnType: { | ||
354 | + name: { | ||
355 | + name: 'import.column-type.name', | ||
356 | + value: 'name' | ||
357 | + }, | ||
358 | + type: { | ||
359 | + name: 'import.column-type.type', | ||
360 | + value: 'type' | ||
361 | + }, | ||
362 | + clientAttribute: { | ||
363 | + name: 'import.column-type.client-attribute', | ||
364 | + value: 'CLIENT_ATTRIBUTE' | ||
365 | + }, | ||
366 | + sharedAttribute: { | ||
367 | + name: 'import.column-type.shared-attribute', | ||
368 | + value: 'SHARED_ATTRIBUTE' | ||
369 | + }, | ||
370 | + serverAttribute: { | ||
371 | + name: 'import.column-type.server-attribute', | ||
372 | + value: 'SERVER_ATTRIBUTE' | ||
373 | + }, | ||
374 | + timeseries: { | ||
375 | + name: 'import.column-type.timeseries', | ||
376 | + value: 'TIMESERIES' | ||
377 | + }, | ||
378 | + entityField: { | ||
379 | + name: 'import.column-type.entity-field', | ||
380 | + value: 'ENTITY_FIELD' | ||
381 | + }, | ||
382 | + accessToken: { | ||
383 | + name: 'import.column-type.access-token', | ||
384 | + value: 'ACCESS_TOKEN' | ||
385 | + } | ||
386 | + }, | ||
353 | aliasEntityType: { | 387 | aliasEntityType: { |
354 | current_customer: "CURRENT_CUSTOMER" | 388 | current_customer: "CURRENT_CUSTOMER" |
355 | }, | 389 | }, |
@@ -49,7 +49,7 @@ export function DeviceCardController(types) { | @@ -49,7 +49,7 @@ export function DeviceCardController(types) { | ||
49 | 49 | ||
50 | /*@ngInject*/ | 50 | /*@ngInject*/ |
51 | export function DeviceController($rootScope, userService, deviceService, customerService, $state, $stateParams, | 51 | export function DeviceController($rootScope, userService, deviceService, customerService, $state, $stateParams, |
52 | - $document, $mdDialog, $q, $translate, types) { | 52 | + $document, $mdDialog, $q, $translate, types, importExport) { |
53 | 53 | ||
54 | var customerId = $stateParams.customerId; | 54 | var customerId = $stateParams.customerId; |
55 | 55 | ||
@@ -57,6 +57,29 @@ export function DeviceController($rootScope, userService, deviceService, custome | @@ -57,6 +57,29 @@ export function DeviceController($rootScope, userService, deviceService, custome | ||
57 | 57 | ||
58 | var deviceGroupActionsList = []; | 58 | var deviceGroupActionsList = []; |
59 | 59 | ||
60 | + var deviceAddItemActionsList = [ | ||
61 | + { | ||
62 | + onAction: function ($event) { | ||
63 | + vm.grid.addItem($event); | ||
64 | + }, | ||
65 | + name: function() { return $translate.instant('action.add') }, | ||
66 | + details: function() { return $translate.instant('device.add-device-text') }, | ||
67 | + icon: "insert_drive_file" | ||
68 | + }, | ||
69 | + { | ||
70 | + onAction: function ($event) { | ||
71 | + importExport.importEntities($event, types.entityType.device).then( | ||
72 | + function() { | ||
73 | + vm.grid.refreshList(); | ||
74 | + } | ||
75 | + ); | ||
76 | + }, | ||
77 | + name: function() { return $translate.instant('action.import') }, | ||
78 | + details: function() { return $translate.instant('device.import') }, | ||
79 | + icon: "file_upload" | ||
80 | + } | ||
81 | + ]; | ||
82 | + | ||
60 | var vm = this; | 83 | var vm = this; |
61 | 84 | ||
62 | vm.types = types; | 85 | vm.types = types; |
@@ -78,12 +101,12 @@ export function DeviceController($rootScope, userService, deviceService, custome | @@ -78,12 +101,12 @@ export function DeviceController($rootScope, userService, deviceService, custome | ||
78 | 101 | ||
79 | actionsList: deviceActionsList, | 102 | actionsList: deviceActionsList, |
80 | groupActionsList: deviceGroupActionsList, | 103 | groupActionsList: deviceGroupActionsList, |
104 | + addItemActions: deviceAddItemActionsList, | ||
81 | 105 | ||
82 | onGridInited: gridInited, | 106 | onGridInited: gridInited, |
83 | 107 | ||
84 | addItemTemplateUrl: addDeviceTemplate, | 108 | addItemTemplateUrl: addDeviceTemplate, |
85 | 109 | ||
86 | - addItemText: function() { return $translate.instant('device.add-device-text') }, | ||
87 | noItemsText: function() { return $translate.instant('device.no-devices-text') }, | 110 | noItemsText: function() { return $translate.instant('device.no-devices-text') }, |
88 | itemDetailsText: function() { return $translate.instant('device.device-details') }, | 111 | itemDetailsText: function() { return $translate.instant('device.device-details') }, |
89 | isDetailsReadOnly: isCustomerUser, | 112 | isDetailsReadOnly: isCustomerUser, |
@@ -314,7 +337,6 @@ export function DeviceController($rootScope, userService, deviceService, custome | @@ -314,7 +337,6 @@ export function DeviceController($rootScope, userService, deviceService, custome | ||
314 | icon: "add" | 337 | icon: "add" |
315 | }; | 338 | }; |
316 | 339 | ||
317 | - | ||
318 | } else if (vm.devicesScope === 'customer_user') { | 340 | } else if (vm.devicesScope === 'customer_user') { |
319 | deviceActionsList.push( | 341 | deviceActionsList.push( |
320 | { | 342 | { |
@@ -329,6 +351,8 @@ export function DeviceController($rootScope, userService, deviceService, custome | @@ -329,6 +351,8 @@ export function DeviceController($rootScope, userService, deviceService, custome | ||
329 | 351 | ||
330 | vm.deviceGridConfig.addItemAction = {}; | 352 | vm.deviceGridConfig.addItemAction = {}; |
331 | } | 353 | } |
354 | + vm.deviceGridConfig.addItemActions = []; | ||
355 | + | ||
332 | } | 356 | } |
333 | 357 | ||
334 | vm.deviceGridConfig.refreshParamsFunc = refreshDevicesParamsFunction; | 358 | vm.deviceGridConfig.refreshParamsFunc = refreshDevicesParamsFunction; |
@@ -21,6 +21,7 @@ export default function GlobalInterceptor($rootScope, $q, $injector) { | @@ -21,6 +21,7 @@ export default function GlobalInterceptor($rootScope, $q, $injector) { | ||
21 | var userService; | 21 | var userService; |
22 | var types; | 22 | var types; |
23 | var http; | 23 | var http; |
24 | + var timeout; | ||
24 | 25 | ||
25 | var internalUrlPrefixes = [ | 26 | var internalUrlPrefixes = [ |
26 | '/api/auth/token', | 27 | '/api/auth/token', |
@@ -71,6 +72,13 @@ export default function GlobalInterceptor($rootScope, $q, $injector) { | @@ -71,6 +72,13 @@ export default function GlobalInterceptor($rootScope, $q, $injector) { | ||
71 | return http; | 72 | return http; |
72 | } | 73 | } |
73 | 74 | ||
75 | + function getTimeout() { | ||
76 | + if (!timeout) { | ||
77 | + timeout = $injector.get("$timeout"); | ||
78 | + } | ||
79 | + return timeout; | ||
80 | + } | ||
81 | + | ||
74 | function rejectionErrorCode(rejection) { | 82 | function rejectionErrorCode(rejection) { |
75 | if (rejection && rejection.data && rejection.data.errorCode) { | 83 | if (rejection && rejection.data && rejection.data.errorCode) { |
76 | return rejection.data.errorCode; | 84 | return rejection.data.errorCode; |
@@ -144,12 +152,20 @@ export default function GlobalInterceptor($rootScope, $q, $injector) { | @@ -144,12 +152,20 @@ export default function GlobalInterceptor($rootScope, $q, $injector) { | ||
144 | return response; | 152 | return response; |
145 | } | 153 | } |
146 | 154 | ||
155 | + function retryRequest (httpConfig) { | ||
156 | + var thisTimeout = 1000 + Math.random() * 3000; | ||
157 | + return getTimeout()(function() { | ||
158 | + return getHttp()(httpConfig); | ||
159 | + }, thisTimeout); | ||
160 | + } | ||
161 | + | ||
147 | function responseError(rejection) { | 162 | function responseError(rejection) { |
148 | if (rejection.config.url.startsWith('/api/')) { | 163 | if (rejection.config.url.startsWith('/api/')) { |
149 | updateLoadingState(rejection.config, false); | 164 | updateLoadingState(rejection.config, false); |
150 | } | 165 | } |
151 | var unhandled = false; | 166 | var unhandled = false; |
152 | var ignoreErrors = rejection.config.ignoreErrors; | 167 | var ignoreErrors = rejection.config.ignoreErrors; |
168 | + var resendRequest = rejection.config.resendRequest; | ||
153 | if (rejection.refreshTokenPending || rejection.status === 401) { | 169 | if (rejection.refreshTokenPending || rejection.status === 401) { |
154 | var errorCode = rejectionErrorCode(rejection); | 170 | var errorCode = rejectionErrorCode(rejection); |
155 | if (rejection.refreshTokenPending || (errorCode && errorCode === getTypes().serverErrorCode.jwtTokenExpired)) { | 171 | if (rejection.refreshTokenPending || (errorCode && errorCode === getTypes().serverErrorCode.jwtTokenExpired)) { |
@@ -161,6 +177,10 @@ export default function GlobalInterceptor($rootScope, $q, $injector) { | @@ -161,6 +177,10 @@ export default function GlobalInterceptor($rootScope, $q, $injector) { | ||
161 | if (!ignoreErrors) { | 177 | if (!ignoreErrors) { |
162 | $rootScope.$broadcast('forbidden'); | 178 | $rootScope.$broadcast('forbidden'); |
163 | } | 179 | } |
180 | + } else if (rejection.status === 429) { | ||
181 | + if (resendRequest) { | ||
182 | + return retryRequest(rejection.config); | ||
183 | + } | ||
164 | } else if (rejection.status === 0 || rejection.status === -1) { | 184 | } else if (rejection.status === 0 || rejection.status === -1) { |
165 | getToast().showError(getTranslate().instant('error.unable-to-connect')); | 185 | getToast().showError(getTranslate().instant('error.unable-to-connect')); |
166 | } else if (!rejection.config.url.startsWith('/api/plugins/rpc')) { | 186 | } else if (!rejection.config.url.startsWith('/api/plugins/rpc')) { |
@@ -96,6 +96,7 @@ export default angular.module('thingsboard.help', []) | @@ -96,6 +96,7 @@ export default angular.module('thingsboard.help', []) | ||
96 | assets: helpBaseUrl + "/docs/user-guide/ui/assets", | 96 | assets: helpBaseUrl + "/docs/user-guide/ui/assets", |
97 | devices: helpBaseUrl + "/docs/user-guide/ui/devices", | 97 | devices: helpBaseUrl + "/docs/user-guide/ui/devices", |
98 | entityViews: helpBaseUrl + "/docs/user-guide/ui/entity-views", | 98 | entityViews: helpBaseUrl + "/docs/user-guide/ui/entity-views", |
99 | + entitiesImport: helpBaseUrl + "/docs/user-guide/bulk-provisioning", | ||
99 | dashboards: helpBaseUrl + "/docs/user-guide/ui/dashboards", | 100 | dashboards: helpBaseUrl + "/docs/user-guide/ui/dashboards", |
100 | users: helpBaseUrl + "/docs/user-guide/ui/users", | 101 | users: helpBaseUrl + "/docs/user-guide/ui/users", |
101 | widgetsBundles: helpBaseUrl + "/docs/user-guide/ui/widget-library#bundles", | 102 | widgetsBundles: helpBaseUrl + "/docs/user-guide/ui/widget-library#bundles", |
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 './import-dialog.scss'; | ||
17 | + | ||
18 | +/*@ngInject*/ | ||
19 | +export default function ImportDialogCsvController($scope, $mdDialog, toast, importTitle, importFileLabel, entityType, importExport, types, $mdStepper, $timeout) { | ||
20 | + | ||
21 | + var vm = this; | ||
22 | + | ||
23 | + vm.cancel = cancel; | ||
24 | + vm.fileAdded = fileAdded; | ||
25 | + vm.clearFile = clearFile; | ||
26 | + vm.nextStep = nextStep; | ||
27 | + vm.previousStep = previousStep; | ||
28 | + | ||
29 | + vm.importParameters = { | ||
30 | + delim: ',', | ||
31 | + isUpdate: true, | ||
32 | + isHeader: true | ||
33 | + }; | ||
34 | + | ||
35 | + vm.importTitle = importTitle; | ||
36 | + vm.importFileLabel = importFileLabel; | ||
37 | + vm.entityType = entityType; | ||
38 | + | ||
39 | + vm.isVertical = true; | ||
40 | + vm.isLinear = true; | ||
41 | + vm.isAlternative = false; | ||
42 | + vm.isMobileStepText = true; | ||
43 | + vm.isImportData = false; | ||
44 | + | ||
45 | + vm.parseData = []; | ||
46 | + | ||
47 | + vm.delimiters = [{ | ||
48 | + key: ',', | ||
49 | + value: ',' | ||
50 | + }, { | ||
51 | + key: ';', | ||
52 | + value: ';' | ||
53 | + }, { | ||
54 | + key: '|', | ||
55 | + value: '|' | ||
56 | + }, { | ||
57 | + key: '\t', | ||
58 | + value: 'Tab' | ||
59 | + }]; | ||
60 | + | ||
61 | + vm.progressCreate = 0; | ||
62 | + | ||
63 | + var parseData = {}; | ||
64 | + | ||
65 | + function fileAdded($file) { | ||
66 | + if ($file.getExtension() === 'csv') { | ||
67 | + var reader = new FileReader(); | ||
68 | + reader.onload = function (event) { | ||
69 | + $scope.$apply(function () { | ||
70 | + if (event.target.result) { | ||
71 | + vm.theFormStep1.$setDirty(); | ||
72 | + var importCSV = event.target.result; | ||
73 | + if (importCSV && importCSV.length > 0) { | ||
74 | + try { | ||
75 | + vm.importData = importCSV; | ||
76 | + vm.fileName = $file.name; | ||
77 | + } catch (err) { | ||
78 | + vm.fileName = null; | ||
79 | + toast.showError(err.message); | ||
80 | + } | ||
81 | + } | ||
82 | + } | ||
83 | + }); | ||
84 | + }; | ||
85 | + reader.readAsText($file.file); | ||
86 | + } | ||
87 | + } | ||
88 | + | ||
89 | + function parseCSV(importData) { | ||
90 | + var config = { | ||
91 | + delim: vm.importParameters.delim, | ||
92 | + header: vm.importParameters.isHeader | ||
93 | + }; | ||
94 | + return importExport.convertCSVToJson(importData, config); | ||
95 | + } | ||
96 | + | ||
97 | + function createColumnsData(parseData) { | ||
98 | + vm.columnsParam = []; | ||
99 | + var columnParam = {}; | ||
100 | + for (var i = 0; i < parseData.headers.length; i++) { | ||
101 | + if (vm.importParameters.isHeader && parseData.headers[i].search(/^(name|type)$/im) === 0) { | ||
102 | + columnParam = { | ||
103 | + type: types.importEntityColumnType[parseData.headers[i].toLowerCase()].value, | ||
104 | + key: parseData.headers[i].toLowerCase(), | ||
105 | + sampleData: parseData.rows[0][i] | ||
106 | + }; | ||
107 | + } else { | ||
108 | + columnParam = { | ||
109 | + type: types.importEntityColumnType.serverAttribute.value, | ||
110 | + key: vm.importParameters.isHeader ? parseData.headers[i] : "", | ||
111 | + sampleData: parseData.rows[0][i] | ||
112 | + }; | ||
113 | + } | ||
114 | + vm.columnsParam.push(columnParam); | ||
115 | + } | ||
116 | + } | ||
117 | + | ||
118 | + function addEntities (importData, parameterColumns) { | ||
119 | + var entitiesData = []; | ||
120 | + var sentDataLength = 0; | ||
121 | + var config = { | ||
122 | + ignoreErrors: true, | ||
123 | + resendRequest: true | ||
124 | + }; | ||
125 | + for (var i = 0; i < importData.rows.length; i++) { | ||
126 | + var entityData = { | ||
127 | + name: "", | ||
128 | + type: "", | ||
129 | + accessToken: "", | ||
130 | + attributes: { | ||
131 | + server: [], | ||
132 | + shared: [] | ||
133 | + }, | ||
134 | + timeseries: [] | ||
135 | + }; | ||
136 | + for (var j = 0; j < parameterColumns.length; j++) { | ||
137 | + switch (parameterColumns[j].type) { | ||
138 | + case types.importEntityColumnType.serverAttribute.value: | ||
139 | + entityData.attributes.server.push({ | ||
140 | + key: parameterColumns[j].key, | ||
141 | + value: importData.rows[i][j] | ||
142 | + }); | ||
143 | + break; | ||
144 | + case types.importEntityColumnType.timeseries.value: | ||
145 | + entityData.timeseries.push({ | ||
146 | + key: parameterColumns[j].key, | ||
147 | + value: importData.rows[i][j] | ||
148 | + }); | ||
149 | + break; | ||
150 | + case types.importEntityColumnType.sharedAttribute.value: | ||
151 | + entityData.attributes.shared.push({ | ||
152 | + key: parameterColumns[j].key, | ||
153 | + value: importData.rows[i][j] | ||
154 | + }); | ||
155 | + break; | ||
156 | + case types.importEntityColumnType.accessToken.value: | ||
157 | + entityData.accessToken = importData.rows[i][j]; | ||
158 | + break; | ||
159 | + case types.importEntityColumnType.name.value: | ||
160 | + entityData.name = importData.rows[i][j]; | ||
161 | + break; | ||
162 | + case types.importEntityColumnType.type.value: | ||
163 | + entityData.type = importData.rows[i][j]; | ||
164 | + break; | ||
165 | + } | ||
166 | + } | ||
167 | + entitiesData.push(entityData); | ||
168 | + } | ||
169 | + $scope.$on('createImportEntityCompleted', function () { | ||
170 | + sentDataLength++; | ||
171 | + vm.progressCreate = Math.round((sentDataLength / importData.rows.length) * 100); | ||
172 | + }); | ||
173 | + importExport.createMultiEntity(entitiesData, vm.entityType, vm.importParameters.isUpdate, config).then(function (response) { | ||
174 | + vm.statistical = response; | ||
175 | + vm.isImportData = false; | ||
176 | + $mdStepper('import-stepper').next(); | ||
177 | + }); | ||
178 | + } | ||
179 | + | ||
180 | + function clearFile() { | ||
181 | + vm.theFormStep1.$setDirty(); | ||
182 | + vm.fileName = null; | ||
183 | + vm.importData = null; | ||
184 | + } | ||
185 | + | ||
186 | + function previousStep(step) { | ||
187 | + let steppers = $mdStepper('import-stepper'); | ||
188 | + switch (step) { | ||
189 | + case 1: | ||
190 | + steppers.back(); | ||
191 | + $timeout(function () { | ||
192 | + vm.theFormStep1.$setDirty(); | ||
193 | + }); | ||
194 | + break; | ||
195 | + default: | ||
196 | + steppers.back(); | ||
197 | + break; | ||
198 | + } | ||
199 | + } | ||
200 | + | ||
201 | + function nextStep(step) { | ||
202 | + let steppers = $mdStepper('import-stepper'); | ||
203 | + switch (step) { | ||
204 | + case 2: | ||
205 | + steppers.next(); | ||
206 | + break; | ||
207 | + case 3: | ||
208 | + parseData = parseCSV(vm.importData); | ||
209 | + if (parseData === -1) { | ||
210 | + steppers.back(); | ||
211 | + $timeout(function () { | ||
212 | + clearFile(); | ||
213 | + }); | ||
214 | + } else { | ||
215 | + createColumnsData(parseData); | ||
216 | + steppers.next(); | ||
217 | + } | ||
218 | + break; | ||
219 | + case 4: | ||
220 | + steppers.next(); | ||
221 | + vm.isImportData = true; | ||
222 | + addEntities(parseData, vm.columnsParam); | ||
223 | + break; | ||
224 | + case 6: | ||
225 | + $mdDialog.hide(); | ||
226 | + break; | ||
227 | + } | ||
228 | + | ||
229 | + } | ||
230 | + | ||
231 | + function cancel() { | ||
232 | + if($mdStepper('import-stepper').currentStep > 2){ | ||
233 | + $mdDialog.hide(); | ||
234 | + } else { | ||
235 | + $mdDialog.cancel(); | ||
236 | + } | ||
237 | + } | ||
238 | +} |
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-dialog aria-label="{{ vm.importTitle | translate }}" class="tb-import-stepper" tb-help="'entitiesImport'" | ||
19 | + help-container-id="help-container"> | ||
20 | + <md-toolbar> | ||
21 | + <div class="md-toolbar-tools"> | ||
22 | + <h2 translate>{{ vm.importTitle }}</h2> | ||
23 | + <span flex></span> | ||
24 | + <div id="help-container"></div> | ||
25 | + <md-button class="md-icon-button" ng-click="vm.cancel()" ng-disabled="vm.isImportData"> | ||
26 | + <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> | ||
27 | + </md-button> | ||
28 | + </div> | ||
29 | + </md-toolbar> | ||
30 | + | ||
31 | + <span style="max-height: 5px; height: 5px;" flex></span> | ||
32 | + <md-dialog-content> | ||
33 | + <md-stepper id="import-stepper" md-mobile-step-text="vm.isMobileStepText" md-vertical="vm.isVertical" | ||
34 | + md-linear="vm.isLinear" md-alternative="vm.isAlternative"> | ||
35 | + <md-step md-label="{{ 'import.stepper-text.select-file' | translate }}"> | ||
36 | + <md-step-body> | ||
37 | + <form name="vm.theFormStep1"> | ||
38 | + <fieldset ng-disabled="$root.loading"> | ||
39 | + <div layout="column" layout-padding> | ||
40 | + <div class="tb-container"> | ||
41 | + <label class="tb-label" translate>{{ vm.importFileLabel }}</label> | ||
42 | + <div flow-init="{singleFile:true}" | ||
43 | + flow-file-added="vm.fileAdded( $file )" class="tb-file-select-container"> | ||
44 | + <div class="tb-file-clear-container"> | ||
45 | + <md-button ng-click="vm.clearFile()" | ||
46 | + class="tb-file-clear-btn md-icon-button md-primary" | ||
47 | + aria-label="{{ 'action.remove' | translate }}"> | ||
48 | + <md-tooltip md-direction="top"> | ||
49 | + {{ 'action.remove' | translate }} | ||
50 | + </md-tooltip> | ||
51 | + <md-icon aria-label="{{ 'action.remove' | translate }}" | ||
52 | + class="material-icons"> | ||
53 | + close | ||
54 | + </md-icon> | ||
55 | + </md-button> | ||
56 | + </div> | ||
57 | + <div class="alert tb-flow-drop" flow-drop> | ||
58 | + <label for="select" translate>import.drop-file-csv</label> | ||
59 | + <input class="file-input" flow-btn | ||
60 | + flow-attrs="{accept:'.csv,application/csv,text/csv'}" | ||
61 | + id="select"> | ||
62 | + </div> | ||
63 | + </div> | ||
64 | + </div> | ||
65 | + <div> | ||
66 | + <div ng-show="!vm.fileName" translate>import.no-file</div> | ||
67 | + <div ng-show="vm.fileName">{{ vm.fileName }}</div> | ||
68 | + </div> | ||
69 | + </div> | ||
70 | + </fieldset> | ||
71 | + </form> | ||
72 | + </md-step-body> | ||
73 | + | ||
74 | + <md-step-actions layout="row"> | ||
75 | + <span flex></span> | ||
76 | + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()"> | ||
77 | + {{ 'action.cancel' | translate }} | ||
78 | + </md-button> | ||
79 | + <md-button class="md-primary md-raised" | ||
80 | + ng-disabled="$root.loading || !vm.theFormStep1.$dirty || !vm.theFormStep1.$valid || !vm.importData" | ||
81 | + ng-click="vm.nextStep(2);"> | ||
82 | + {{ 'action.continue' | translate }} | ||
83 | + </md-button> | ||
84 | + </md-step-actions> | ||
85 | + </md-step> | ||
86 | + | ||
87 | + <md-step md-label="{{ 'import.stepper-text.configuration' | translate }}"> | ||
88 | + <md-step-body> | ||
89 | + <div layout="column"> | ||
90 | + <md-input-container> | ||
91 | + <label translate>import.csv-delimiter</label> | ||
92 | + <md-select ng-model="vm.importParameters.delim"> | ||
93 | + <md-option ng-repeat="delimiter in vm.delimiters" ng-value="delimiter.key"> | ||
94 | + {{delimiter.value}} | ||
95 | + </md-option> | ||
96 | + </md-select> | ||
97 | + </md-input-container> | ||
98 | + <md-checkbox ng-model="vm.importParameters.isHeader" | ||
99 | + aria-label="First line contains column names"> | ||
100 | + {{ 'import.csv-first-line-header' | translate }} | ||
101 | + </md-checkbox> | ||
102 | + <md-checkbox ng-model="vm.importParameters.isUpdate" aria-label="Update attributes/telemetry"> | ||
103 | + {{ 'import.csv-update-data' | translate }} | ||
104 | + </md-checkbox> | ||
105 | + </div> | ||
106 | + </md-step-body> | ||
107 | + | ||
108 | + <md-step-actions layout="row"> | ||
109 | + <md-button ng-disabled="$root.loading" ng-click="vm.previousStep(1);">Back | ||
110 | + </md-button> | ||
111 | + <span flex></span> | ||
112 | + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()"> | ||
113 | + {{ 'action.cancel' | translate }} | ||
114 | + </md-button> | ||
115 | + <md-button class="md-primary md-raised" ng-disabled="$root.loading" ng-click="vm.nextStep(3);"> | ||
116 | + {{ 'action.continue' | translate }} | ||
117 | + </md-button> | ||
118 | + </md-step-actions> | ||
119 | + </md-step> | ||
120 | + | ||
121 | + <md-step md-label="{{ 'import.stepper-text.column-type' | translate }}"> | ||
122 | + <md-step-body> | ||
123 | + <form name="vm.theFormStep3"> | ||
124 | + <tb-table-columns-assignment columns="vm.columnsParam" the-form="vm.theFormStep3" | ||
125 | + entity-type="vm.entityType"></tb-table-columns-assignment> | ||
126 | + </form> | ||
127 | + </md-step-body> | ||
128 | + | ||
129 | + <md-step-actions layout="row"> | ||
130 | + <md-button ng-disabled="$root.loading" ng-click="vm.previousStep();">Back | ||
131 | + </md-button> | ||
132 | + <span flex></span> | ||
133 | + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()"> | ||
134 | + {{ 'action.cancel' | translate }} | ||
135 | + </md-button> | ||
136 | + <md-button class="md-primary md-raised" | ||
137 | + ng-disabled="$root.loading || !vm.theFormStep3.$dirty || !vm.theFormStep3.$valid" | ||
138 | + ng-click="vm.nextStep(4);"> | ||
139 | + {{ 'action.continue' | translate }} | ||
140 | + </md-button> | ||
141 | + </md-step-actions> | ||
142 | + </md-step> | ||
143 | + | ||
144 | + <md-step md-label="{{ 'import.stepper-text.creat-entities' | translate }}"> | ||
145 | + <md-step-body> | ||
146 | + <md-progress-linear class="md-warn tb-import-progress" md-mode="determinate" | ||
147 | + value="{{vm.progressCreate}}"></md-progress-linear> | ||
148 | + </md-step-body> | ||
149 | + </md-step> | ||
150 | + <md-step md-label="{{ 'import.stepper-text.done' | translate }}"> | ||
151 | + <md-step-body layout="column"> | ||
152 | + <div> | ||
153 | + <p class="md-body-1" translate translate-values="{count: vm.statistical.create.entity}" | ||
154 | + ng-if="vm.statistical.create && vm.statistical.create.entity">import.message.create-entities</p> | ||
155 | + <p class="md-body-1" translate translate-values="{count: vm.statistical.update.entity}" | ||
156 | + ng-if="vm.statistical.update && vm.statistical.update.entity">import.message.update-entities</p> | ||
157 | + <p class="md-body-1" translate translate-values="{count: vm.statistical.error.entity}" | ||
158 | + ng-if="vm.statistical.error && vm.statistical.error.entity">import.message.error-entities</p> | ||
159 | + </div> | ||
160 | + </md-step-body> | ||
161 | + <md-step-actions layout="row"> | ||
162 | + <span flex></span> | ||
163 | + <md-button class="md-primary md-raised" ng-disabled="$root.loading" ng-click="vm.nextStep(6);"> | ||
164 | + {{ 'action.ok' | translate }} | ||
165 | + </md-button> | ||
166 | + </md-step-actions> | ||
167 | + </md-step> | ||
168 | + | ||
169 | + </md-stepper> | ||
170 | + </md-dialog-content> | ||
171 | +</md-dialog> |
@@ -13,6 +13,8 @@ | @@ -13,6 +13,8 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | +@import "../../scss/constants.scss"; | ||
17 | + | ||
16 | $previewSize: 100px !default; | 18 | $previewSize: 100px !default; |
17 | 19 | ||
18 | .tb-file-select-container { | 20 | .tb-file-select-container { |
@@ -40,3 +42,21 @@ $previewSize: 100px !default; | @@ -40,3 +42,21 @@ $previewSize: 100px !default; | ||
40 | top: 50%; | 42 | top: 50%; |
41 | transform: translate(0%, -50%) !important; | 43 | transform: translate(0%, -50%) !important; |
42 | } | 44 | } |
45 | + | ||
46 | +.tb-table-select{ | ||
47 | + md-input-container{ | ||
48 | + margin: 0; | ||
49 | + | ||
50 | + .md-errors-spacer{ | ||
51 | + min-height: 0; | ||
52 | + } | ||
53 | + } | ||
54 | +} | ||
55 | + | ||
56 | +.md-stepper-indicator-wrapper{ | ||
57 | + background-color: transparent; | ||
58 | +} | ||
59 | + | ||
60 | +.tb-import-progress{ | ||
61 | + margin: 7px 0; | ||
62 | +} |
@@ -13,9 +13,10 @@ | @@ -13,9 +13,10 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -/* eslint-disable import/no-unresolved, import/default */ | 16 | +/* eslint-disable import/no-unresolved, import/default*/ |
17 | 17 | ||
18 | import importDialogTemplate from './import-dialog.tpl.html'; | 18 | import importDialogTemplate from './import-dialog.tpl.html'; |
19 | +import importDialogCSVTemplate from './import-dialog-csv.tpl.html'; | ||
19 | import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html'; | 20 | import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html'; |
20 | 21 | ||
21 | /* eslint-enable import/no-unresolved, import/default */ | 22 | /* eslint-enable import/no-unresolved, import/default */ |
@@ -24,7 +25,7 @@ import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html'; | @@ -24,7 +25,7 @@ import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html'; | ||
24 | /* eslint-disable no-undef, angular/window-service, angular/document-service */ | 25 | /* eslint-disable no-undef, angular/window-service, angular/document-service */ |
25 | 26 | ||
26 | /*@ngInject*/ | 27 | /*@ngInject*/ |
27 | -export default function ImportExport($log, $translate, $q, $mdDialog, $document, $http, itembuffer, utils, types, | 28 | +export default function ImportExport($log, $translate, $q, $mdDialog, $document, $http, itembuffer, utils, types, $rootScope, |
28 | dashboardUtils, entityService, dashboardService, ruleChainService, widgetService, toast, attributeService) { | 29 | dashboardUtils, entityService, dashboardService, ruleChainService, widgetService, toast, attributeService) { |
29 | 30 | ||
30 | 31 | ||
@@ -41,7 +42,10 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -41,7 +42,10 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
41 | importWidgetsBundle: importWidgetsBundle, | 42 | importWidgetsBundle: importWidgetsBundle, |
42 | exportExtension: exportExtension, | 43 | exportExtension: exportExtension, |
43 | importExtension: importExtension, | 44 | importExtension: importExtension, |
44 | - exportToPc: exportToPc | 45 | + importEntities: importEntities, |
46 | + convertCSVToJson: convertCSVToJson, | ||
47 | + exportToPc: exportToPc, | ||
48 | + createMultiEntity: createMultiEntity | ||
45 | }; | 49 | }; |
46 | 50 | ||
47 | return service; | 51 | return service; |
@@ -54,11 +58,11 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -54,11 +58,11 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
54 | var bundleAlias = widgetsBundle.alias; | 58 | var bundleAlias = widgetsBundle.alias; |
55 | var isSystem = widgetsBundle.tenantId.id === types.id.nullUid; | 59 | var isSystem = widgetsBundle.tenantId.id === types.id.nullUid; |
56 | widgetService.getBundleWidgetTypes(bundleAlias, isSystem).then( | 60 | widgetService.getBundleWidgetTypes(bundleAlias, isSystem).then( |
57 | - function success (widgetTypes) { | 61 | + function success(widgetTypes) { |
58 | prepareExport(widgetsBundle); | 62 | prepareExport(widgetsBundle); |
59 | var widgetsBundleItem = { | 63 | var widgetsBundleItem = { |
60 | - widgetsBundle: prepareExport(widgetsBundle), | ||
61 | - widgetTypes: [] | 64 | + widgetsBundle: prepareExport(widgetsBundle), |
65 | + widgetTypes: [] | ||
62 | }; | 66 | }; |
63 | for (var t in widgetTypes) { | 67 | for (var t in widgetTypes) { |
64 | var widgetType = widgetTypes[t]; | 68 | var widgetType = widgetTypes[t]; |
@@ -68,10 +72,10 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -68,10 +72,10 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
68 | widgetsBundleItem.widgetTypes.push(prepareExport(widgetType)); | 72 | widgetsBundleItem.widgetTypes.push(prepareExport(widgetType)); |
69 | } | 73 | } |
70 | var name = widgetsBundle.title; | 74 | var name = widgetsBundle.title; |
71 | - name = name.toLowerCase().replace(/\W/g,"_"); | 75 | + name = name.toLowerCase().replace(/\W/g, "_"); |
72 | exportToPc(widgetsBundleItem, name + '.json'); | 76 | exportToPc(widgetsBundleItem, name + '.json'); |
73 | }, | 77 | }, |
74 | - function fail (rejection) { | 78 | + function fail(rejection) { |
75 | var message = rejection; | 79 | var message = rejection; |
76 | if (!message) { | 80 | if (!message) { |
77 | message = $translate.instant('error.unknown-error'); | 81 | message = $translate.instant('error.unknown-error'); |
@@ -168,7 +172,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -168,7 +172,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
168 | delete widgetType.bundleAlias; | 172 | delete widgetType.bundleAlias; |
169 | } | 173 | } |
170 | var name = widgetType.name; | 174 | var name = widgetType.name; |
171 | - name = name.toLowerCase().replace(/\W/g,"_"); | 175 | + name = name.toLowerCase().replace(/\W/g, "_"); |
172 | exportToPc(prepareExport(widgetType), name + '.json'); | 176 | exportToPc(prepareExport(widgetType), name + '.json'); |
173 | }, | 177 | }, |
174 | function fail(rejection) { | 178 | function fail(rejection) { |
@@ -209,8 +213,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -209,8 +213,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
209 | 213 | ||
210 | function validateImportedWidgetType(widgetType) { | 214 | function validateImportedWidgetType(widgetType) { |
211 | if (angular.isUndefined(widgetType.name) | 215 | if (angular.isUndefined(widgetType.name) |
212 | - || angular.isUndefined(widgetType.descriptor)) | ||
213 | - { | 216 | + || angular.isUndefined(widgetType.descriptor)) { |
214 | return false; | 217 | return false; |
215 | } | 218 | } |
216 | return true; | 219 | return true; |
@@ -228,7 +231,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -228,7 +231,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
228 | metadata: prepareRuleChainMetaData(ruleChainMetaData) | 231 | metadata: prepareRuleChainMetaData(ruleChainMetaData) |
229 | }; | 232 | }; |
230 | var name = ruleChain.name; | 233 | var name = ruleChain.name; |
231 | - name = name.toLowerCase().replace(/\W/g,"_"); | 234 | + name = name.toLowerCase().replace(/\W/g, "_"); |
232 | exportToPc(ruleChainExport, name + '.json'); | 235 | exportToPc(ruleChainExport, name + '.json'); |
233 | }, | 236 | }, |
234 | (rejection) => { | 237 | (rejection) => { |
@@ -253,7 +256,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -253,7 +256,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
253 | 256 | ||
254 | function prepareRuleChainMetaData(ruleChainMetaData) { | 257 | function prepareRuleChainMetaData(ruleChainMetaData) { |
255 | delete ruleChainMetaData.ruleChainId; | 258 | delete ruleChainMetaData.ruleChainId; |
256 | - for (var i=0;i<ruleChainMetaData.nodes.length;i++) { | 259 | + for (var i = 0; i < ruleChainMetaData.nodes.length; i++) { |
257 | var node = ruleChainMetaData.nodes[i]; | 260 | var node = ruleChainMetaData.nodes[i]; |
258 | delete node.ruleChainId; | 261 | delete node.ruleChainId; |
259 | ruleChainMetaData.nodes[i] = prepareExport(node); | 262 | ruleChainMetaData.nodes[i] = prepareExport(node); |
@@ -305,7 +308,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -305,7 +308,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
305 | function exportWidget(dashboard, sourceState, sourceLayout, widget) { | 308 | function exportWidget(dashboard, sourceState, sourceLayout, widget) { |
306 | var widgetItem = itembuffer.prepareWidgetItem(dashboard, sourceState, sourceLayout, widget); | 309 | var widgetItem = itembuffer.prepareWidgetItem(dashboard, sourceState, sourceLayout, widget); |
307 | var name = widgetItem.widget.config.title; | 310 | var name = widgetItem.widget.config.title; |
308 | - name = name.toLowerCase().replace(/\W/g,"_"); | 311 | + name = name.toLowerCase().replace(/\W/g, "_"); |
309 | exportToPc(prepareExport(widgetItem), name + '.json'); | 312 | exportToPc(prepareExport(widgetItem), name + '.json'); |
310 | } | 313 | } |
311 | 314 | ||
@@ -415,10 +418,10 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -415,10 +418,10 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
415 | var aliasIds = Object.keys(entityAliases); | 418 | var aliasIds = Object.keys(entityAliases); |
416 | if (aliasIds.length > 0) { | 419 | if (aliasIds.length > 0) { |
417 | processEntityAliases(entityAliases, aliasIds).then( | 420 | processEntityAliases(entityAliases, aliasIds).then( |
418 | - function(missingEntityAliases) { | 421 | + function (missingEntityAliases) { |
419 | if (Object.keys(missingEntityAliases).length > 0) { | 422 | if (Object.keys(missingEntityAliases).length > 0) { |
420 | - editMissingAliases($event, [ widget ], | ||
421 | - true, 'dashboard.widget-import-missing-aliases-title', missingEntityAliases).then( | 423 | + editMissingAliases($event, [widget], |
424 | + true, 'dashboard.widget-import-missing-aliases-title', missingEntityAliases).then( | ||
422 | function success(updatedEntityAliases) { | 425 | function success(updatedEntityAliases) { |
423 | for (var aliasId in updatedEntityAliases) { | 426 | for (var aliasId in updatedEntityAliases) { |
424 | var entityAlias = updatedEntityAliases[aliasId]; | 427 | var entityAlias = updatedEntityAliases[aliasId]; |
@@ -483,14 +486,14 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -483,14 +486,14 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
483 | function success(targetLayout) { | 486 | function success(targetLayout) { |
484 | itembuffer.addWidgetToDashboard(dashboard, targetState, targetLayout, widget, | 487 | itembuffer.addWidgetToDashboard(dashboard, targetState, targetLayout, widget, |
485 | aliasesInfo, onAliasesUpdateFunction, originalColumns, originalSize, -1, -1).then( | 488 | aliasesInfo, onAliasesUpdateFunction, originalColumns, originalSize, -1, -1).then( |
486 | - function() { | ||
487 | - deferred.resolve( | ||
488 | - { | ||
489 | - widget: widget, | ||
490 | - layoutId: targetLayout | ||
491 | - } | ||
492 | - ); | ||
493 | - } | 489 | + function () { |
490 | + deferred.resolve( | ||
491 | + { | ||
492 | + widget: widget, | ||
493 | + layoutId: targetLayout | ||
494 | + } | ||
495 | + ); | ||
496 | + } | ||
494 | ); | 497 | ); |
495 | }, | 498 | }, |
496 | function fail() { | 499 | function fail() { |
@@ -505,7 +508,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -505,7 +508,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
505 | dashboardService.getDashboard(dashboardId).then( | 508 | dashboardService.getDashboard(dashboardId).then( |
506 | function success(dashboard) { | 509 | function success(dashboard) { |
507 | var name = dashboard.title; | 510 | var name = dashboard.title; |
508 | - name = name.toLowerCase().replace(/\W/g,"_"); | 511 | + name = name.toLowerCase().replace(/\W/g, "_"); |
509 | exportToPc(prepareDashboardExport(dashboard), name + '.json'); | 512 | exportToPc(prepareDashboardExport(dashboard), name + '.json'); |
510 | }, | 513 | }, |
511 | function fail(rejection) { | 514 | function fail(rejection) { |
@@ -538,13 +541,13 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -538,13 +541,13 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
538 | dashboard = dashboardUtils.validateAndUpdateDashboard(dashboard); | 541 | dashboard = dashboardUtils.validateAndUpdateDashboard(dashboard); |
539 | var entityAliases = dashboard.configuration.entityAliases; | 542 | var entityAliases = dashboard.configuration.entityAliases; |
540 | if (entityAliases) { | 543 | if (entityAliases) { |
541 | - var aliasIds = Object.keys( entityAliases ); | 544 | + var aliasIds = Object.keys(entityAliases); |
542 | if (aliasIds.length > 0) { | 545 | if (aliasIds.length > 0) { |
543 | processEntityAliases(entityAliases, aliasIds).then( | 546 | processEntityAliases(entityAliases, aliasIds).then( |
544 | - function(missingEntityAliases) { | ||
545 | - if (Object.keys( missingEntityAliases ).length > 0) { | 547 | + function (missingEntityAliases) { |
548 | + if (Object.keys(missingEntityAliases).length > 0) { | ||
546 | editMissingAliases($event, dashboard.configuration.widgets, | 549 | editMissingAliases($event, dashboard.configuration.widgets, |
547 | - false, 'dashboard.dashboard-import-missing-aliases-title', missingEntityAliases).then( | 550 | + false, 'dashboard.dashboard-import-missing-aliases-title', missingEntityAliases).then( |
548 | function success(updatedEntityAliases) { | 551 | function success(updatedEntityAliases) { |
549 | for (var aliasId in updatedEntityAliases) { | 552 | for (var aliasId in updatedEntityAliases) { |
550 | entityAliases[aliasId] = updatedEntityAliases[aliasId]; | 553 | entityAliases[aliasId] = updatedEntityAliases[aliasId]; |
@@ -575,6 +578,34 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -575,6 +578,34 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
575 | return deferred.promise; | 578 | return deferred.promise; |
576 | } | 579 | } |
577 | 580 | ||
581 | + function importEntities($event, entityType) { | ||
582 | + var deferred = $q.defer(); | ||
583 | + | ||
584 | + switch (entityType) { | ||
585 | + case types.entityType.device: | ||
586 | + openImportDialogCSV($event, entityType, 'device.import', 'device.device-file').then( | ||
587 | + function success() { | ||
588 | + deferred.resolve(); | ||
589 | + }, | ||
590 | + function fail() { | ||
591 | + deferred.reject(); | ||
592 | + } | ||
593 | + ); | ||
594 | + return deferred.promise; | ||
595 | + case types.entityType.asset: | ||
596 | + openImportDialogCSV($event, entityType, 'asset.import', 'asset.asset-file').then( | ||
597 | + function success() { | ||
598 | + deferred.resolve(); | ||
599 | + }, | ||
600 | + function fail() { | ||
601 | + deferred.reject(); | ||
602 | + } | ||
603 | + ); | ||
604 | + return deferred.promise; | ||
605 | + } | ||
606 | + | ||
607 | + } | ||
608 | + | ||
578 | function saveImportedDashboard(dashboard, deferred) { | 609 | function saveImportedDashboard(dashboard, deferred) { |
579 | dashboardService.saveDashboard(dashboard).then( | 610 | dashboardService.saveDashboard(dashboard).then( |
580 | function success() { | 611 | function success() { |
@@ -594,14 +625,13 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -594,14 +625,13 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
594 | } | 625 | } |
595 | 626 | ||
596 | 627 | ||
597 | - | ||
598 | function exportExtension(extensionId) { | 628 | function exportExtension(extensionId) { |
599 | 629 | ||
600 | getExtension(extensionId) | 630 | getExtension(extensionId) |
601 | .then( | 631 | .then( |
602 | function success(extension) { | 632 | function success(extension) { |
603 | var name = extension.title; | 633 | var name = extension.title; |
604 | - name = name.toLowerCase().replace(/\W/g,"_"); | 634 | + name = name.toLowerCase().replace(/\W/g, "_"); |
605 | exportToPc(prepareExport(extension), name + '.json'); | 635 | exportToPc(prepareExport(extension), name + '.json'); |
606 | }, | 636 | }, |
607 | function fail(rejection) { | 637 | function fail(rejection) { |
@@ -661,7 +691,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -661,7 +691,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
661 | function validateImportedExtension(configuration) { | 691 | function validateImportedExtension(configuration) { |
662 | if (configuration.length) { | 692 | if (configuration.length) { |
663 | for (let i = 0; i < configuration.length; i++) { | 693 | for (let i = 0; i < configuration.length; i++) { |
664 | - if (angular.isUndefined(configuration[i].configuration) || angular.isUndefined(configuration[i].id )|| angular.isUndefined(configuration[i].type)) { | 694 | + if (angular.isUndefined(configuration[i].configuration) || angular.isUndefined(configuration[i].id) || angular.isUndefined(configuration[i].type)) { |
665 | return false; | 695 | return false; |
666 | } | 696 | } |
667 | } | 697 | } |
@@ -692,7 +722,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -692,7 +722,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
692 | var aliasId = aliasIds[index]; | 722 | var aliasId = aliasIds[index]; |
693 | var entityAlias = entityAliases[aliasId]; | 723 | var entityAlias = entityAliases[aliasId]; |
694 | entityService.checkEntityAlias(entityAlias).then( | 724 | entityService.checkEntityAlias(entityAlias).then( |
695 | - function(result) { | 725 | + function (result) { |
696 | if (result) { | 726 | if (result) { |
697 | checkNextEntityAliasOrComplete(index, aliasIds, entityAliases, missingEntityAliases, deferred); | 727 | checkNextEntityAliasOrComplete(index, aliasIds, entityAliases, missingEntityAliases, deferred); |
698 | } else { | 728 | } else { |
@@ -732,6 +762,124 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -732,6 +762,124 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
732 | return deferred.promise; | 762 | return deferred.promise; |
733 | } | 763 | } |
734 | 764 | ||
765 | + function splitCSV(str, sep) { | ||
766 | + for (var foo = str.split(sep = sep || ","), x = foo.length - 1, tl; x >= 0; x--) { | ||
767 | + if (foo[x].replace(/"\s+$/, '"').charAt(foo[x].length - 1) == '"') { | ||
768 | + if ((tl = foo[x].replace(/^\s+"/, '"')).length > 1 && tl.charAt(0) == '"') { | ||
769 | + foo[x] = foo[x].replace(/^\s*"|"\s*$/g, '').replace(/""/g, '"'); | ||
770 | + } else if (x) { | ||
771 | + foo.splice(x - 1, 2, [foo[x - 1], foo[x]].join(sep)); | ||
772 | + } else foo = foo.shift().split(sep).concat(foo); | ||
773 | + } else foo[x].replace(/""/g, '"'); | ||
774 | + } | ||
775 | + return foo; | ||
776 | + } | ||
777 | + | ||
778 | + function isNumeric(str) { | ||
779 | + str = str.replace(',', '.'); | ||
780 | + return !isNaN(parseFloat(str)) && isFinite(str); | ||
781 | + } | ||
782 | + | ||
783 | + function convertStringToJSType(str) { | ||
784 | + if (isNumeric(str.replace(',', '.'))) { | ||
785 | + return parseFloat(str.replace(',', '.')); | ||
786 | + } | ||
787 | + if (str.search(/^(true|false)$/im) === 0) { | ||
788 | + return str === 'true'; | ||
789 | + } | ||
790 | + if (str === "") { | ||
791 | + return null; | ||
792 | + } | ||
793 | + return str; | ||
794 | + } | ||
795 | + | ||
796 | + function convertCSVToJson(csvdata, config) { | ||
797 | + config = config || {}; | ||
798 | + const delim = config.delim || ","; | ||
799 | + const header = config.header || false; | ||
800 | + let result = {}; | ||
801 | + | ||
802 | + let csvlines = csvdata.split(/[\r\n]+/); | ||
803 | + let csvheaders = splitCSV(csvlines[0], delim); | ||
804 | + if (csvheaders.length < 2) { | ||
805 | + toast.showError($translate.instant('import.import-csv-number-columns-error')); | ||
806 | + return -1; | ||
807 | + } | ||
808 | + let csvrows = header ? csvlines.slice(1, csvlines.length) : csvlines; | ||
809 | + | ||
810 | + result.headers = csvheaders; | ||
811 | + result.rows = []; | ||
812 | + | ||
813 | + for (let r in csvrows) { | ||
814 | + if (csvrows.hasOwnProperty(r)) { | ||
815 | + let row = csvrows[r]; | ||
816 | + | ||
817 | + if (row.length === 0) | ||
818 | + break; | ||
819 | + | ||
820 | + let rowitems = splitCSV(row, delim); | ||
821 | + if (rowitems.length !== result.headers.length) { | ||
822 | + toast.showError($translate.instant('import.import-csv-invalid-format-error', {line: (header ? result.rows.length + 2: result.rows.length + 1)})); | ||
823 | + return -1; | ||
824 | + } | ||
825 | + for (let i = 0; i < rowitems.length; i++) { | ||
826 | + rowitems[i] = convertStringToJSType(rowitems[i]); | ||
827 | + } | ||
828 | + result.rows.push(rowitems); | ||
829 | + } | ||
830 | + } | ||
831 | + return result; | ||
832 | + } | ||
833 | + | ||
834 | + function sumObject(obj1, obj2){ | ||
835 | + Object.keys(obj2).map(function(key) { | ||
836 | + if (angular.isObject(obj2[key])) { | ||
837 | + obj1[key] = obj1[key] || {}; | ||
838 | + angular.merge(obj1[key], sumObject(obj1[key], obj2[key])); | ||
839 | + } else { | ||
840 | + obj1[key] = (obj1[key] || 0) + obj2[key]; | ||
841 | + } | ||
842 | + }); | ||
843 | + return obj1; | ||
844 | + } | ||
845 | + | ||
846 | + function qAllWithProgress(promises) { | ||
847 | + promises.forEach(function(p) { | ||
848 | + p.then(function() { | ||
849 | + $rootScope.$broadcast('createImportEntityCompleted', {}); | ||
850 | + }); | ||
851 | + }); | ||
852 | + return $q.all(promises); | ||
853 | + } | ||
854 | + | ||
855 | + function createMultiEntity(arrayData, entityType, updateData, config) { | ||
856 | + let partSize = 100; | ||
857 | + partSize = arrayData.length > partSize ? partSize : arrayData.length; | ||
858 | + let allPromise = []; | ||
859 | + let statisticalInfo = {}; | ||
860 | + let deferred = $q.defer(); | ||
861 | + | ||
862 | + for(let i = 0; i < partSize; i++){ | ||
863 | + const promise = entityService.saveEntityParameters(entityType, arrayData[i], updateData, config); | ||
864 | + allPromise.push(promise); | ||
865 | + } | ||
866 | + | ||
867 | + qAllWithProgress(allPromise).then(function success(response) { | ||
868 | + for (let i = 0; i < response.length; i++){ | ||
869 | + statisticalInfo = sumObject(statisticalInfo, response[i]); | ||
870 | + } | ||
871 | + arrayData.splice(0, partSize); | ||
872 | + if(arrayData.length > 0){ | ||
873 | + deferred.resolve(createMultiEntity(arrayData, entityType, updateData, config).then(function (response) { | ||
874 | + return sumObject(statisticalInfo, response); | ||
875 | + })); | ||
876 | + } else { | ||
877 | + deferred.resolve(statisticalInfo); | ||
878 | + } | ||
879 | + }); | ||
880 | + return deferred.promise; | ||
881 | + } | ||
882 | + | ||
735 | // Common functions | 883 | // Common functions |
736 | 884 | ||
737 | function prepareExport(data) { | 885 | function prepareExport(data) { |
@@ -771,8 +919,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -771,8 +919,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
771 | 919 | ||
772 | if (window.navigator && window.navigator.msSaveOrOpenBlob) { | 920 | if (window.navigator && window.navigator.msSaveOrOpenBlob) { |
773 | window.navigator.msSaveOrOpenBlob(blob, filename); | 921 | window.navigator.msSaveOrOpenBlob(blob, filename); |
774 | - } | ||
775 | - else{ | 922 | + } else { |
776 | var e = document.createEvent('MouseEvents'), | 923 | var e = document.createEvent('MouseEvents'), |
777 | a = document.createElement('a'); | 924 | a = document.createElement('a'); |
778 | 925 | ||
@@ -807,6 +954,34 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -807,6 +954,34 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
807 | return deferred.promise; | 954 | return deferred.promise; |
808 | } | 955 | } |
809 | 956 | ||
957 | + function openImportDialogCSV($event, entityType, importTitle, importFileLabel) { | ||
958 | + var deferred = $q.defer(); | ||
959 | + $mdDialog.show({ | ||
960 | + controller: 'ImportDialogCSVController', | ||
961 | + controllerAs: 'vm', | ||
962 | + templateUrl: importDialogCSVTemplate, | ||
963 | + locals: { | ||
964 | + importTitle: importTitle, | ||
965 | + importFileLabel: importFileLabel, | ||
966 | + entityType: entityType | ||
967 | + }, | ||
968 | + parent: angular.element($document[0].body), | ||
969 | + onComplete: fixedDialogSize, | ||
970 | + multiple: true, | ||
971 | + fullscreen: true, | ||
972 | + targetEvent: $event | ||
973 | + }).then(function () { | ||
974 | + deferred.resolve(); | ||
975 | + }, function () { | ||
976 | + deferred.reject(); | ||
977 | + }); | ||
978 | + return deferred.promise; | ||
979 | + } | ||
980 | + | ||
981 | + function fixedDialogSize(scope, element) { | ||
982 | + let dialogElement = element[0].getElementsByTagName('md-dialog'); | ||
983 | + dialogElement[0].style.width = dialogElement[0].offsetWidth + 2 + "px"; | ||
984 | + } | ||
810 | } | 985 | } |
811 | 986 | ||
812 | /* eslint-enable no-undef, angular/window-service, angular/document-service */ | 987 | /* eslint-enable no-undef, angular/window-service, angular/document-service */ |
@@ -15,9 +15,13 @@ | @@ -15,9 +15,13 @@ | ||
15 | */ | 15 | */ |
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'; | ||
19 | +import TableColumnsAssignment from './table-columns-assignment.directive'; | ||
18 | 20 | ||
19 | 21 | ||
20 | export default angular.module('thingsboard.importexport', []) | 22 | export default angular.module('thingsboard.importexport', []) |
21 | .factory('importExport', ImportExport) | 23 | .factory('importExport', ImportExport) |
22 | .controller('ImportDialogController', ImportDialogController) | 24 | .controller('ImportDialogController', ImportDialogController) |
25 | + .controller('ImportDialogCSVController', ImportDialogCSVController) | ||
26 | + .directive('tbTableColumnsAssignment', TableColumnsAssignment) | ||
23 | .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 | + | ||
17 | +/* eslint-disable import/no-unresolved, import/default */ | ||
18 | + | ||
19 | +import tableColumnsAssignment from './table-columns-assignment.tpl.html'; | ||
20 | + | ||
21 | +/* eslint-enable import/no-unresolved, import/default */ | ||
22 | + | ||
23 | +/*@ngInject*/ | ||
24 | +export default function TableColumnsAssignment() { | ||
25 | + return { | ||
26 | + restrict: "E", | ||
27 | + scope: true, | ||
28 | + bindToController: { | ||
29 | + theForm: '=?', | ||
30 | + columns: '=', | ||
31 | + entityType: '=', | ||
32 | + }, | ||
33 | + templateUrl: tableColumnsAssignment, | ||
34 | + controller: TableColumnsAssignmentController, | ||
35 | + controllerAs: 'vm' | ||
36 | + }; | ||
37 | +} | ||
38 | + | ||
39 | +/*@ngInject*/ | ||
40 | +function TableColumnsAssignmentController($scope, types, $timeout) { | ||
41 | + var vm = this; | ||
42 | + | ||
43 | + vm.columnTypes = {}; | ||
44 | + | ||
45 | + vm.columnTypes.name = types.importEntityColumnType.name; | ||
46 | + vm.columnTypes.type = types.importEntityColumnType.type; | ||
47 | + | ||
48 | + switch (vm.entityType) { | ||
49 | + case types.entityType.device: | ||
50 | + vm.columnTypes.sharedAttribute = types.importEntityColumnType.sharedAttribute; | ||
51 | + vm.columnTypes.serverAttribute = types.importEntityColumnType.serverAttribute; | ||
52 | + vm.columnTypes.timeseries = types.importEntityColumnType.timeseries; | ||
53 | + vm.columnTypes.accessToken = types.importEntityColumnType.accessToken; | ||
54 | + break; | ||
55 | + case types.entityType.asset: | ||
56 | + vm.columnTypes.serverAttribute = types.importEntityColumnType.serverAttribute; | ||
57 | + vm.columnTypes.timeseries = types.importEntityColumnType.timeseries; | ||
58 | + break; | ||
59 | + } | ||
60 | + | ||
61 | + $scope.$watch('vm.columns', function(newVal){ | ||
62 | + if (newVal) { | ||
63 | + var isSelectName = false; | ||
64 | + var isSelectType = false; | ||
65 | + var isSelectCredentials = false; | ||
66 | + for (var i = 0; i < newVal.length; i++) { | ||
67 | + switch (newVal[i].type) { | ||
68 | + case types.importEntityColumnType.name.value: | ||
69 | + isSelectName = true; | ||
70 | + break; | ||
71 | + case types.importEntityColumnType.type.value: | ||
72 | + isSelectType = true; | ||
73 | + break; | ||
74 | + case types.importEntityColumnType.accessToken.value: | ||
75 | + isSelectCredentials = true; | ||
76 | + break; | ||
77 | + } | ||
78 | + } | ||
79 | + if(isSelectName && isSelectType) { | ||
80 | + vm.theForm.$setDirty(); | ||
81 | + } else { | ||
82 | + vm.theForm.$setPristine(); | ||
83 | + } | ||
84 | + $timeout(function () { | ||
85 | + vm.columnTypes.name.disable = isSelectName; | ||
86 | + vm.columnTypes.type.disable = isSelectType; | ||
87 | + if (angular.isDefined(vm.columnTypes.accessToken)) { | ||
88 | + vm.columnTypes.accessToken.disable = isSelectCredentials; | ||
89 | + } | ||
90 | + }); | ||
91 | + } | ||
92 | + }, true); | ||
93 | +} |
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 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>{{ 'import.column-example' | translate }}</th> | ||
24 | + <th md-column>{{ 'import.column-type.column-type' | translate }}</th> | ||
25 | + <th md-column>{{ 'import.column-key' | translate }}</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="{{ 'import.column-type.column-type' | translate }}"> | ||
35 | + <md-option ng-repeat="type in vm.columnTypes" ng-value="type.value" ng-disabled="type.disable"> | ||
36 | + {{type.name | translate}} | ||
37 | + </md-option> | ||
38 | + </md-select> | ||
39 | + </td> | ||
40 | + <td md-cell> | ||
41 | + <md-input-container md-no-float | ||
42 | + ng-if="column.type != vm.columnTypes.name.value && | ||
43 | + column.type != vm.columnTypes.type.value && | ||
44 | + column.type != vm.columnTypes.accessToken.value"> | ||
45 | + <input required name="columnKeyName" | ||
46 | + placeholder="{{ 'import.column-value' | translate }}" | ||
47 | + ng-model="column.key" | ||
48 | + aria-label="{{ 'import.column-value' | translate }}"> | ||
49 | + </md-input-container> | ||
50 | + </td> | ||
51 | + </tr> | ||
52 | + </tbody> | ||
53 | + </table> | ||
54 | + <md-divider></md-divider> | ||
55 | +</md-table-container> |
@@ -48,7 +48,8 @@ | @@ -48,7 +48,8 @@ | ||
48 | "paste-reference": "Paste reference", | 48 | "paste-reference": "Paste reference", |
49 | "import": "Import", | 49 | "import": "Import", |
50 | "export": "Export", | 50 | "export": "Export", |
51 | - "share-via": "Share via {{provider}}" | 51 | + "share-via": "Share via {{provider}}", |
52 | + "continue": "Continue" | ||
52 | }, | 53 | }, |
53 | "aggregation": { | 54 | "aggregation": { |
54 | "aggregation": "Aggregation", | 55 | "aggregation": "Aggregation", |
@@ -246,7 +247,9 @@ | @@ -246,7 +247,9 @@ | ||
246 | "select-asset": "Select asset", | 247 | "select-asset": "Select asset", |
247 | "no-assets-matching": "No assets matching '{{entity}}' were found.", | 248 | "no-assets-matching": "No assets matching '{{entity}}' were found.", |
248 | "asset-required": "Asset is required", | 249 | "asset-required": "Asset is required", |
249 | - "name-starts-with": "Asset name starts with" | 250 | + "name-starts-with": "Asset name starts with", |
251 | + "import": "Import assets", | ||
252 | + "asset-file": "Asset file" | ||
250 | }, | 253 | }, |
251 | "attribute": { | 254 | "attribute": { |
252 | "attributes": "Attributes", | 255 | "attributes": "Attributes", |
@@ -665,7 +668,9 @@ | @@ -665,7 +668,9 @@ | ||
665 | "is-gateway": "Is gateway", | 668 | "is-gateway": "Is gateway", |
666 | "public": "Public", | 669 | "public": "Public", |
667 | "device-public": "Device is public", | 670 | "device-public": "Device is public", |
668 | - "select-device": "Select device" | 671 | + "select-device": "Select device", |
672 | + "import": "Import device", | ||
673 | + "device-file": "Device file" | ||
669 | }, | 674 | }, |
670 | "dialog": { | 675 | "dialog": { |
671 | "close": "Close dialog" | 676 | "close": "Close dialog" |
@@ -1093,7 +1098,40 @@ | @@ -1093,7 +1098,40 @@ | ||
1093 | }, | 1098 | }, |
1094 | "import": { | 1099 | "import": { |
1095 | "no-file": "No file selected", | 1100 | "no-file": "No file selected", |
1096 | - "drop-file": "Drop a JSON file or click to select a file to upload." | 1101 | + "drop-file": "Drop a JSON file or click to select a file to upload.", |
1102 | + "drop-file-csv": "Drop a CSV file or click to select a file to upload.", | ||
1103 | + "column-value": "Value", | ||
1104 | + "column-title": "Title", | ||
1105 | + "column-example": "Example value data", | ||
1106 | + "column-key": "Attribute/telemetry key", | ||
1107 | + "csv-delimiter": "CSV delimiter", | ||
1108 | + "csv-first-line-header": "First line contains column names", | ||
1109 | + "csv-update-data": "Update attributes/telemetry", | ||
1110 | + "import-csv-number-columns-error": "A file should contain at least two columns", | ||
1111 | + "import-csv-invalid-format-error": "Invalid file format. Line: '{{line}}'", | ||
1112 | + "column-type": { | ||
1113 | + "name": "Name", | ||
1114 | + "type": "Type", | ||
1115 | + "column-type": "Column type", | ||
1116 | + "client-attribute": "Client attribute", | ||
1117 | + "shared-attribute": "Shared attribute", | ||
1118 | + "server-attribute": "Server attribute", | ||
1119 | + "timeseries": "Timeseries", | ||
1120 | + "entity-field": "Entity field", | ||
1121 | + "access-token": "Access token" | ||
1122 | + }, | ||
1123 | + "stepper-text":{ | ||
1124 | + "select-file": "Select a file", | ||
1125 | + "configuration": "Import configuration", | ||
1126 | + "column-type": "Select columns type", | ||
1127 | + "creat-entities": "Creating new entities", | ||
1128 | + "done": "Done" | ||
1129 | + }, | ||
1130 | + "message": { | ||
1131 | + "create-entities": "{{count}} new entities were successfully created.", | ||
1132 | + "update-entities": "{{count}} entities were successfully updated.", | ||
1133 | + "error-entities": "There was an error creating {{count}} entities." | ||
1134 | + } | ||
1097 | }, | 1135 | }, |
1098 | "item": { | 1136 | "item": { |
1099 | "selected": "Selected" | 1137 | "selected": "Selected" |