Commit 6ba0b943e88118ee544988b7d8335f4b7aa46246

Authored by Vladyslav
Committed by Igor Kulikov
1 parent 82540669

Create new dataKey type entityField (#2282)

* Add support import label

* Create new dataKey type entityField

* Add translate to entityField
... ... @@ -848,7 +848,50 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
848 848 return deferred.promise;
849 849 }
850 850
  851 + function getEntityFieldKeys (entityType, searchText) {
  852 + let entityFieldKeys = [];
  853 + let query = searchText.toLowerCase();
  854 + switch(entityType) {
  855 + case types.entityType.user:
  856 + entityFieldKeys.push(types.entityField.name.keyName);
  857 + entityFieldKeys.push(types.entityField.email.keyName);
  858 + entityFieldKeys.push(types.entityField.firstName.keyName);
  859 + entityFieldKeys.push(types.entityField.lastName.keyName);
  860 + break;
  861 + case types.entityType.tenant:
  862 + case types.entityType.customer:
  863 + entityFieldKeys.push(types.entityField.title.keyName);
  864 + entityFieldKeys.push(types.entityField.email.keyName);
  865 + entityFieldKeys.push(types.entityField.country.keyName);
  866 + entityFieldKeys.push(types.entityField.state.keyName);
  867 + entityFieldKeys.push(types.entityField.city.keyName);
  868 + entityFieldKeys.push(types.entityField.address.keyName);
  869 + entityFieldKeys.push(types.entityField.address2.keyName);
  870 + entityFieldKeys.push(types.entityField.zip.keyName);
  871 + entityFieldKeys.push(types.entityField.phone.keyName);
  872 + break;
  873 + case types.entityType.entityView:
  874 + entityFieldKeys.push(types.entityField.name.keyName);
  875 + entityFieldKeys.push(types.entityField.type.keyName);
  876 + break;
  877 + case types.entityType.device:
  878 + case types.entityType.asset:
  879 + entityFieldKeys.push(types.entityField.name.keyName);
  880 + entityFieldKeys.push(types.entityField.type.keyName);
  881 + entityFieldKeys.push(types.entityField.label.keyName);
  882 + break;
  883 + case types.entityType.dashboard:
  884 + entityFieldKeys.push(types.entityField.title.keyName);
  885 + break;
  886 + }
  887 +
  888 + return query ? entityFieldKeys.filter((entityField) => entityField.toLowerCase().indexOf(query) === 0) : entityFieldKeys;
  889 + }
  890 +
851 891 function getEntityKeys(entityType, entityId, query, type, config) {
  892 + if (type === types.dataKeyType.entityField) {
  893 + return $q.when(getEntityFieldKeys(entityType, query));
  894 + }
852 895 var deferred = $q.defer();
853 896 var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/';
854 897 if (type === types.dataKeyType.timeseries) {
... ...
... ... @@ -350,6 +350,11 @@ export default class Subscription {
350 350 dataKey: dataKey,
351 351 data: []
352 352 };
  353 + if (dataKey.type === this.ctx.types.dataKeyType.entityField) {
  354 + if(datasource.entity && datasource.entity[this.ctx.types.entityField[dataKey.name].value]){
  355 + datasourceData.data.push([Date.now(), datasource.entity[this.ctx.types.entityField[dataKey.name].value]]);
  356 + }
  357 + }
353 358 this.data.push(datasourceData);
354 359 this.hiddenData.push({data: []});
355 360 if (this.displayLegend) {
... ... @@ -878,8 +883,14 @@ export default class Subscription {
878 883 };
879 884 }
880 885
  886 + var entityFieldKey = false;
  887 +
881 888 for (var a = 0; a < datasource.dataKeys.length; a++) {
882   - this.data[index + a].data = [];
  889 + if (datasource.dataKeys[a].type !== this.ctx.types.dataKeyType.entityField) {
  890 + this.data[index + a].data = [];
  891 + } else {
  892 + entityFieldKey = true;
  893 + }
883 894 }
884 895
885 896 index += datasource.dataKeys.length;
... ... @@ -891,7 +902,7 @@ export default class Subscription {
891 902 }
892 903
893 904 var forceUpdate = false;
894   - if (datasource.unresolvedStateEntity ||
  905 + if (datasource.unresolvedStateEntity || entityFieldKey ||
895 906 !datasource.dataKeys.length ||
896 907 (datasource.type === this.ctx.types.datasourceType.entity && !datasource.entityId)
897 908 ) {
... ...
... ... @@ -78,7 +78,8 @@ export default function AppConfig($provide,
78 78 $mdIconProvider.iconSet('mdi', mdiIconSet);
79 79
80 80 ngMdIconServiceProvider
81   - .addShape('alpha-a-circle-outline', '<path d="M11,7H13A2,2 0 0,1 15,9V17H13V13H11V17H9V9A2,2 0 0,1 11,7M11,9V11H13V9H11M12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2Z" />');
  81 + .addShape('alpha-a-circle-outline', '<path d="M11,7H13A2,2 0 0,1 15,9V17H13V13H11V17H9V9A2,2 0 0,1 11,7M11,9V11H13V9H11M12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2Z" />')
  82 + .addShape('alpha-e-circle-outline', '<path d="M9,7H15V9H11V11H15V13H11V15H15V17H9V7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" />');
82 83
83 84 configureTheme();
84 85
... ... @@ -170,4 +171,4 @@ export default function AppConfig($provide,
170 171
171 172 return aliases;
172 173 }
173   -}
\ No newline at end of file
  174 +}
... ...
... ... @@ -322,7 +322,8 @@ export default angular.module('thingsboard.types', [])
322 322 timeseries: "timeseries",
323 323 attribute: "attribute",
324 324 function: "function",
325   - alarm: "alarm"
  325 + alarm: "alarm",
  326 + entityField: "entityField"
326 327 },
327 328 contentType: {
328 329 "JSON": {
... ... @@ -467,6 +468,84 @@ export default angular.module('thingsboard.types', [])
467 468 list: 'entity.type-current-customer'
468 469 }
469 470 },
  471 + entityField: {
  472 + createdTime: {
  473 + keyName: 'createdTime',
  474 + name: 'entity-field.created-time',
  475 + value: 'createdTime',
  476 + time: true
  477 + },
  478 + name: {
  479 + keyName: 'name',
  480 + name: 'entity-field.name',
  481 + value: 'name'
  482 + },
  483 + type: {
  484 + keyName: 'type',
  485 + name: 'entity-field.type',
  486 + value: 'type'
  487 + },
  488 + firstName: {
  489 + keyName: 'firstName',
  490 + name: 'entity-field.first-name',
  491 + value: 'firstName'
  492 + },
  493 + lastName: {
  494 + keyName: 'lastName',
  495 + name: 'entity-field.last-name',
  496 + value: 'lastName'
  497 + },
  498 + email: {
  499 + keyName: 'email',
  500 + name: 'entity-field.email',
  501 + value: 'email'
  502 + },
  503 + title: {
  504 + keyName: 'title',
  505 + name: 'entity-field.title',
  506 + value: 'title'
  507 + },
  508 + country: {
  509 + keyName: 'country',
  510 + name: 'entity-field.country',
  511 + value: 'country'
  512 + },
  513 + state: {
  514 + keyName: 'state',
  515 + name: 'entity-field.state',
  516 + value: 'state'
  517 + },
  518 + city: {
  519 + keyName: 'city',
  520 + name: 'entity-field.city',
  521 + value: 'city'
  522 + },
  523 + address: {
  524 + keyName: 'address',
  525 + name: 'entity-field.address',
  526 + value: 'address'
  527 + },
  528 + address2: {
  529 + keyName: 'address2',
  530 + name: 'entity-field.address2',
  531 + value: 'address2'
  532 + },
  533 + zip: {
  534 + keyName: 'zip',
  535 + name: 'entity-field.zip',
  536 + value: 'zip'
  537 + },
  538 + phone: {
  539 + keyName: 'phone',
  540 + name: 'entity-field.phone',
  541 + value: 'phone'
  542 + },
  543 + label: {
  544 + keyName: 'label',
  545 + name: 'entity-field.label',
  546 + value: 'label'
  547 + }
  548 + },
470 549 entitySearchDirection: {
471 550 from: "FROM",
472 551 to: "TO"
... ...
... ... @@ -16,9 +16,7 @@
16 16
17 17 -->
18 18 <md-content class="md-padding" layout="column">
19   - <md-autocomplete ng-if="model.type === types.dataKeyType.timeseries ||
20   - model.type === types.dataKeyType.attribute ||
21   - model.type === types.dataKeyType.alarm"
  19 + <md-autocomplete ng-if="model.type !== types.dataKeyType.function"
22 20 style="padding-bottom: 8px;"
23 21 ng-required="true"
24 22 md-no-cache="true"
... ... @@ -87,4 +85,4 @@
87 85 prevOrigValue - {{ 'datakey.prev-orig-value-description' | translate }}
88 86 </label>
89 87 </section>
90   -</md-content>
\ No newline at end of file
  88 +</md-content>
... ...
... ... @@ -124,7 +124,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
124 124 var alarmDataKeys = [];
125 125 for (var d in ngModelCtrl.$viewValue.dataKeys) {
126 126 var dataKey = ngModelCtrl.$viewValue.dataKeys[d];
127   - if ((dataKey.type === types.dataKeyType.timeseries) || (dataKey.type === types.dataKeyType.attribute)) {
  127 + if ((dataKey.type === types.dataKeyType.timeseries) || (dataKey.type === types.dataKeyType.attribute) || (dataKey.type === types.dataKeyType.entityField)) {
128 128 dataKeys.push(dataKey);
129 129 } else if (dataKey.type === types.dataKeyType.alarm) {
130 130 alarmDataKeys.push(dataKey);
... ... @@ -219,7 +219,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
219 219 w.triggerHandler('resize');
220 220 }
221 221 }).then(function (newDataKey) {
222   - if ((newDataKey.type === types.dataKeyType.timeseries) || (newDataKey.type === types.dataKeyType.attribute)) {
  222 + if ((newDataKey.type === types.dataKeyType.timeseries) || (newDataKey.type === types.dataKeyType.attribute) || (newDataKey.type === types.dataKeyType.entityField)) {
223 223 let index = scope.dataKeys.indexOf(dataKey);
224 224 scope.dataKeys[index] = newDataKey;
225 225 } else if (newDataKey.type === types.dataKeyType.alarm) {
... ... @@ -246,10 +246,16 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
246 246 items.push({ name: dataKeys[i], type: types.dataKeyType.timeseries });
247 247 }
248 248 if (scope.widgetType == types.widgetType.latest.value) {
249   - scope.fetchEntityKeys({entityAliasId: scope.entityAlias.id, query: searchText, type: types.dataKeyType.attribute})
250   - .then(function (dataKeys) {
  249 + var keysType = [types.dataKeyType.attribute, types.dataKeyType.entityField];
  250 + var promises = [];
  251 + keysType.forEach((type) => {
  252 + promises.push(scope.fetchEntityKeys({entityAliasId: scope.entityAlias.id, query: searchText, type: type}));
  253 + });
  254 + $q.all(promises).then(function (dataKeys) {
251 255 for (var i = 0; i < dataKeys.length; i++) {
252   - items.push({ name: dataKeys[i], type: types.dataKeyType.attribute });
  256 + for (var j = 0; j < dataKeys[i].length; j++) {
  257 + items.push({name: dataKeys[i][j], type: keysType[i]});
  258 + }
253 259 }
254 260 deferred.resolve(items);
255 261 }, function (e) {
... ...
... ... @@ -43,6 +43,10 @@
43 43 <md-tooltip>{{'datakey.attributes' | translate }}</md-tooltip>
44 44 <ng-md-icon size="16" icon="alpha-a-circle-outline"></ng-md-icon>
45 45 </span>
  46 + <span ng-show="item.type==types.dataKeyType.entityField">
  47 + <md-tooltip>{{'datakey.entityField' | translate }}</md-tooltip>
  48 + <ng-md-icon size="16" icon="alpha-e-circle-outline"></ng-md-icon>
  49 + </span>
46 50 <span ng-show="item.type==types.dataKeyType.timeseries">
47 51 <md-tooltip>{{'datakey.timeseries' | translate }}</md-tooltip>
48 52 <ng-md-icon size="16" icon="timeline"></ng-md-icon>
... ... @@ -60,6 +64,10 @@
60 64 <md-tooltip>{{'datakey.attributes' | translate }}</md-tooltip>
61 65 <ng-md-icon size="16" icon="alpha-a-circle-outline" ng-click="createKey($event, types.dataKeyType.attribute, '#datakey_chips')"></ng-md-icon>
62 66 </span>
  67 + <span ng-show="widgetType == types.widgetType.latest.value">
  68 + <md-tooltip>{{'datakey.entityField' | translate }}</md-tooltip>
  69 + <ng-md-icon size="16" icon="alpha-e-circle-outline" ng-click="createKey($event, types.dataKeyType.entityField, '#datakey_chips')"></ng-md-icon>
  70 + </span>
63 71 <span>
64 72 <md-tooltip>{{'datakey.timeseries' | translate }}</md-tooltip>
65 73 <ng-md-icon size="16" icon="timeline" ng-click="createKey($event, types.dataKeyType.timeseries, '#datakey_chips')"></ng-md-icon>
... ... @@ -82,6 +90,10 @@
82 90 <md-tooltip>{{'datakey.attributes' | translate }}</md-tooltip>
83 91 <ng-md-icon size="20" icon="alpha-a-circle-outline"></ng-md-icon>
84 92 </span>
  93 + <span ng-show="$chip.type==types.dataKeyType.entityField">
  94 + <md-tooltip>{{'datakey.entityField' | translate }}</md-tooltip>
  95 + <ng-md-icon size="20" icon="alpha-e-circle-outline"></ng-md-icon>
  96 + </span>
85 97 <span ng-show="$chip.type==types.dataKeyType.timeseries">
86 98 <md-tooltip>{{'datakey.timeseries' | translate }}</md-tooltip>
87 99 <ng-md-icon size="20" icon="timeline"></ng-md-icon>
... ...
... ... @@ -423,10 +423,10 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout
423 423 }
424 424
425 425 var label = chip;
426   - if (type === types.dataKeyType.alarm) {
427   - var alarmField = types.alarmFields[chip];
428   - if (alarmField) {
429   - label = $translate.instant(alarmField.name)+'';
  426 + if (type === types.dataKeyType.alarm || type === types.dataKeyType.entityField) {
  427 + var keyField = type === types.dataKeyType.alarm ? types.alarmFields[chip] : types.entityField[chip];
  428 + if (keyField) {
  429 + label = $translate.instant(keyField.name)+'';
430 430 }
431 431 }
432 432 label = scope.genNextLabel(label);
... ...
... ... @@ -1102,6 +1102,22 @@
1102 1102 "copyId": "Αντιγραφή ID ομάδας οντοτήτων",
1103 1103 "idCopiedMessage": "Το ID της ομάδας οντοτήτων έχει αντιγραφεί στο πρόχειρο"
1104 1104 },
  1105 + "entity-field": {
  1106 + "created-time": "Δημιουργήθηκε",
  1107 + "name": "Όνομα",
  1108 + "type": "Τύπος",
  1109 + "first-name": "Όνομα",
  1110 + "last-name": "Επίθετο",
  1111 + "email": "Email",
  1112 + "title": "Τίτλος",
  1113 + "country": "Χώρα",
  1114 + "state": "Νομός",
  1115 + "city": "Πόλη",
  1116 + "address": "Διεύθυνση",
  1117 + "address2": "Διεύθυνση 2",
  1118 + "zip": "Τ.Κ.",
  1119 + "phone": "Τηλέφωνο"
  1120 + },
1105 1121 "entity-view": {
1106 1122 "entity-view": "Όψη Οντότητας",
1107 1123 "entity-view-required": "Απαιτείται προβολή οντότητας.",
... ... @@ -2603,4 +2619,4 @@
2603 2619 "el_GR": "Ελληνικά"
2604 2620 }
2605 2621 }
2606   -}
\ No newline at end of file
  2622 +}
... ...
... ... @@ -815,6 +815,23 @@
815 815 "no-data": "No data to display",
816 816 "columns-to-display": "Columns to Display"
817 817 },
  818 + "entity-field": {
  819 + "created-time": "Created time",
  820 + "name": "Name",
  821 + "type": "Type",
  822 + "first-name": "First name",
  823 + "last-name": "Last name",
  824 + "email": "Email",
  825 + "title": "Title",
  826 + "country": "Country",
  827 + "state": "State",
  828 + "city": "City",
  829 + "address": "Address",
  830 + "address2": "Address 2",
  831 + "zip": "Zip",
  832 + "phone": "Phone",
  833 + "label": "Label"
  834 + },
818 835 "entity-view": {
819 836 "entity-view": "Entity View",
820 837 "entity-view-required": "Entity view is required.",
... ...
... ... @@ -808,6 +808,22 @@
808 808 "no-data": "No hay datos para mostrar",
809 809 "columns-to-display": "Columnas a mostrar"
810 810 },
  811 + "entity-field": {
  812 + "created-time": "Tiempo de creación",
  813 + "name": "Nombre",
  814 + "type": "Tipo",
  815 + "first-name": "Nombre",
  816 + "last-name": "Apellido",
  817 + "email": "Correo electrónico",
  818 + "title": "Título",
  819 + "country": "País",
  820 + "state": "Estado",
  821 + "city": "Ciudad",
  822 + "address": "Dirección",
  823 + "address2": "Dirección 2",
  824 + "zip": "Código postal",
  825 + "phone": "Teléfono"
  826 + },
811 827 "entity-view": {
812 828 "entity-view": "Vista de entidad",
813 829 "entity-view-required": "Vista de entidad es requerido.",
... ...
... ... @@ -809,6 +809,22 @@
809 809 "use-entity-name-filter": "Utiliser un filtre",
810 810 "user-name-starts-with": "Utilisateurs dont les noms commencent par '{{prefix}}'"
811 811 },
  812 + "entity-field": {
  813 + "address": "Adresse",
  814 + "address2": "Adresse 2",
  815 + "city": "Ville",
  816 + "country": "Pays",
  817 + "created-time": "Heure de création",
  818 + "email": "Email",
  819 + "first-name": "Prénom",
  820 + "last-name": "Nom de famille",
  821 + "name": "Nom",
  822 + "phone": "Téléphone",
  823 + "state": "Prov",
  824 + "title": "Titre",
  825 + "type": "Type",
  826 + "zip": "Code postal"
  827 + },
812 828 "entity-view": {
813 829 "add": "Ajouter une vue d'entité",
814 830 "add-alias": "Ajouter un alias de vue d'entité",
... ...
... ... @@ -814,6 +814,23 @@
814 814 "no-data": "Нет данных для отображения",
815 815 "columns-to-display": "Отобразить следующие колонки"
816 816 },
  817 + "entity-field": {
  818 + "created-time": "Время создания",
  819 + "name": "Название",
  820 + "type": "Тип",
  821 + "first-name": "Имя",
  822 + "last-name": "Фамилия",
  823 + "email": "Электронная почта",
  824 + "title": "Название",
  825 + "country": "Страна",
  826 + "state": "Штат/Область",
  827 + "city": "Город",
  828 + "address": "Адрес",
  829 + "address2": "Адрес 2",
  830 + "zip": "Индекс",
  831 + "phone": "Телефон",
  832 + "label": "Метка"
  833 + },
817 834 "entity-view": {
818 835 "entity-view": "Представление Объекта",
819 836 "entity-view-required": "Представление объекта обязательно.",
... ...
... ... @@ -956,6 +956,23 @@
956 956 "list-of-integrations": "{ count, plural, 1 {Одна інтеграція} other {Список # інтеграцій} }",
957 957 "integration-name-starts-with": "Інтеграції, імена яких починаються з '{{prefix}}'"
958 958 },
  959 + "entity-field": {
  960 + "created-time": "Час створення",
  961 + "name": "Ім'я",
  962 + "type": "Тип",
  963 + "first-name": "Ім'я",
  964 + "last-name": "Прізвище",
  965 + "email": "Електронна пошта",
  966 + "title": "Назва",
  967 + "country": "Країна",
  968 + "state": "Штат",
  969 + "city": "Місто",
  970 + "address": "Адреса",
  971 + "address2": "Адреса 2",
  972 + "zip": "Zip",
  973 + "phone": "Телефон",
  974 + "label": "Мітка"
  975 + },
959 976 "entity-group": {
960 977 "entity-group": "Група сутності",
961 978 "details": "Деталі",
... ...