From 0bc2198e39fb7ca971fc4602cb4fbac29d7af09f Mon Sep 17 00:00:00 2001 From: Manuel Imperiale Date: Tue, 4 Feb 2020 12:48:23 +0100 Subject: [PATCH 1/2] UI-23 - Add bulk upload for Things and Channels Signed-off-by: Manuel Imperiale --- .../services/channels/channels.service.ts | 16 ++++ .../common/services/things/things.service.ts | 16 ++++ .../things/channels/channels.component.html | 57 +++++++------- .../things/channels/channels.component.scss | 4 + .../things/channels/channels.component.ts | 72 ++++++++++-------- .../details/channels.details.component.ts | 17 +++-- .../things/devices/devices.component.html | 59 +++++---------- .../things/devices/devices.component.scss | 8 +- .../pages/things/devices/devices.component.ts | 74 ++++++++++--------- 9 files changed, 175 insertions(+), 148 deletions(-) diff --git a/src/app/common/services/channels/channels.service.ts b/src/app/common/services/channels/channels.service.ts index 62c80601ad..c442b21483 100644 --- a/src/app/common/services/channels/channels.service.ts +++ b/src/app/common/services/channels/channels.service.ts @@ -31,6 +31,22 @@ export class ChannelsService { ); } + addChannels(channels: Channel[]) { + return this.http.post(`${environment.channelsUrl}/bulk`, channels, { observe: 'response' }) + .map( + resp => { + return resp; + }, + ) + .catch( + err => { + this.notificationsService.error('Failed to create Channels', + `Error: ${err.status} - ${err.statusText}`); + return Observable.throw(err); + }, + ); + } + getChannel(channel: string) { return this.http.get(`${environment.channelsUrl}/${channel}`) .map( diff --git a/src/app/common/services/things/things.service.ts b/src/app/common/services/things/things.service.ts index dbfed2fb9d..f3454779b4 100644 --- a/src/app/common/services/things/things.service.ts +++ b/src/app/common/services/things/things.service.ts @@ -30,6 +30,22 @@ export class ThingsService { ); } + addThings(things: Thing[]) { + return this.http.post(`${environment.thingsUrl}/bulk`, things, { observe: 'response' }) + .map( + resp => { + return resp; + }, + ) + .catch( + err => { + this.notificationsService.error('Failed to create Things', + `Error: ${err.status} - ${err.statusText}`); + return Observable.throw(err); + }, + ); + } + getThing(thingID: string) { return this.http.get(environment.thingsUrl + '/' + thingID) .map( diff --git a/src/app/pages/things/channels/channels.component.html b/src/app/pages/things/channels/channels.component.html index 675f08f0f2..51b8adcbe1 100644 --- a/src/app/pages/things/channels/channels.component.html +++ b/src/app/pages/things/channels/channels.component.html @@ -1,36 +1,31 @@ -
-
- - + + +
+
{{ totalChanNumber }}   Channels - - -
-
- - - {{ gwCtrlChanNumber }}   Gateways Control - - -
-
- - - {{ gwDataChanNumber }}   Gateways Data - - -
-
- - - {{ loraChanNumber }}   LoRa Applications - - -
-
+
+
+ +
+
+ +
+
+ +
+
+ - - { - row.type = 'channels'; return row; }, editable: false, @@ -68,45 +66,19 @@ export class ChannelsComponent implements OnInit { source: LocalDataSource = new LocalDataSource(); channels: Channel[]; - gwDataChanNumber = 0; - gwCtrlChanNumber = 0; - loraChanNumber = 0; totalChanNumber = 0; offset = 0; - limit = 20; + limit = 100; constructor( private dialogService: NbDialogService, private channelsService: ChannelsService, - private gatewaysService: GatewaysService, - private loraService: LoraService, private notificationsService: NotificationsService, ) { } ngOnInit() { this.getChannels(); - this.getChannelsStats(); - } - - getChannelsStats() { - this.gatewaysService.getCtrlChannels(this.offset, this.limit).subscribe( - (resp: any) => { - this.gwCtrlChanNumber = resp.total; - }, - ); - - this.gatewaysService.getDataChannels(this.offset, this.limit).subscribe( - (resp: any) => { - this.gwDataChanNumber = resp.total; - }, - ); - - this.loraService.getChannels(this.offset, this.limit).subscribe( - (resp: any) => { - this.loraChanNumber = resp.total; - }, - ); } getChannels(): void { @@ -130,7 +102,6 @@ export class ChannelsComponent implements OnInit { resp => { this.notificationsService.success('Channel successfully created', ''); this.getChannels(); - this.getChannelsStats(); }, ); } @@ -162,4 +133,43 @@ export class ChannelsComponent implements OnInit { }, ); } + + onFileSelected(files: FileList) { + if (files && files.length > 0) { + const file: File = files.item(0); + const reader: FileReader = new FileReader(); + reader.readAsText(file); + reader.onload = () => { + const csv: string = reader.result as string; + const lines = csv.split('\n'); + const channels: Channel[] = []; + + lines.forEach( line => { + const col = line.split('|'); + if (col[0] !== '' && col[0] !== '') { + let metadata = {}; + if (col[1] !== undefined) { + try { + metadata = JSON.parse(col[1]); + } catch (e) { + this.notificationsService.warn('Wrong metadata format', ''); + } + } + + const chann = { + name: col[0], + metadata: metadata, + }; + channels.push(chann); + } + }); + + this.channelsService.addChannels(channels).subscribe( + resp => { + this.getChannels(); + }, + ); + }; + } + } } diff --git a/src/app/pages/things/channels/details/channels.details.component.ts b/src/app/pages/things/channels/details/channels.details.component.ts index 019c86a4f4..cacdddbdfd 100644 --- a/src/app/pages/things/channels/details/channels.details.component.ts +++ b/src/app/pages/things/channels/details/channels.details.component.ts @@ -96,6 +96,7 @@ export class ChannelsDetailsComponent implements OnInit { this.thingsService.getThings(this.offset, this.limit).subscribe( (respThings: any) => { respThings.things.forEach(thing => { + // Filter get Things resp and keep only disconnected ones. if (!(this.connections.filter(c => c.id === thing.id).length > 0)) { this.things.push(thing); } @@ -109,12 +110,14 @@ export class ChannelsDetailsComponent implements OnInit { } getchannelMessages() { - this.messagesService.getMessages(this.channel.id, this.connections[0].key).subscribe( - (respMsg: any) => { - if (respMsg.messages) { - this.messages = respMsg.messages; - } - }, - ); + if (this.connections.length > 0) { + this.messagesService.getMessages(this.channel.id, this.connections[0].key).subscribe( + (respMsg: any) => { + if (respMsg.messages) { + this.messages = respMsg.messages; + } + }, + ); + } } } diff --git a/src/app/pages/things/devices/devices.component.html b/src/app/pages/things/devices/devices.component.html index 38c4357f98..c242a62f13 100644 --- a/src/app/pages/things/devices/devices.component.html +++ b/src/app/pages/things/devices/devices.component.html @@ -1,56 +1,37 @@ -
-
- - - {{ thingsNumber }}   Things - - -
- -
- - - {{ loraDevicesNumber }}   LoRa Devices - - -
- -
- - - {{ gatewaysNumber }}   Gateways - - -
- -
+ +
-
- +
+ {{ thingsNumber }}   Devices
-
-
+
+
+
+ +
-
-
+ - + (deleteConfirm)="onDeleteConfirm($event)"> diff --git a/src/app/pages/things/devices/devices.component.scss b/src/app/pages/things/devices/devices.component.scss index 3084edd8f6..e90dce65cb 100644 --- a/src/app/pages/things/devices/devices.component.scss +++ b/src/app/pages/things/devices/devices.component.scss @@ -1,7 +1,7 @@ -button { - margin: 2px; -} - nb-card-header { text-align: center; } + +button { + width: 100%; +} diff --git a/src/app/pages/things/devices/devices.component.ts b/src/app/pages/things/devices/devices.component.ts index 47bc3352ee..dfa24211ce 100644 --- a/src/app/pages/things/devices/devices.component.ts +++ b/src/app/pages/things/devices/devices.component.ts @@ -7,8 +7,6 @@ import { LocalDataSource } from 'ng2-smart-table'; import { Thing } from 'app/common/interfaces/mainflux.interface'; import { ThingsService } from 'app/common/services/things/things.service'; -import { GatewaysService } from 'app/common/services/gateways/gateways.service'; -import { LoraService } from 'app/common/services/lora/lora.service'; import { NotificationsService } from 'app/common/services/notifications/notifications.service'; import { ConfirmationComponent } from 'app/shared/confirmation/confirmation.component'; @@ -41,10 +39,7 @@ export class DevicesComponent implements OnInit { name: { title: 'Name', type: 'string', - placeholder: 'Search name', - filter: { - placeholder: 'Search name', - }, + filter: false, }, id: { title: 'ID', @@ -57,7 +52,6 @@ export class DevicesComponent implements OnInit { type: 'custom', renderComponent: DetailsComponent, valuePrepareFunction: (cell, row) => { - row.type = 'devices'; return row; }, editable: false, @@ -75,38 +69,18 @@ export class DevicesComponent implements OnInit { things: Observable; thingsNumber = 0; - gatewaysNumber = 0; - loraDevicesNumber = 0; offset = 0; - limit = 20; + limit = 100; constructor( private dialogService: NbDialogService, private thingsService: ThingsService, - private gatewaysService: GatewaysService, - private loraService: LoraService, private notificationsService: NotificationsService, ) { } ngOnInit() { this.getThings(); - - this.getThingsStats(); - } - - getThingsStats() { - this.gatewaysService.getGateways(this.offset, this.limit).subscribe( - (resp: any) => { - this.gatewaysNumber = resp.total; - }, - ); - - this.loraService.getDevices(this.offset, this.limit).subscribe( - (resp: any) => { - this.loraDevicesNumber = resp.total; - }, - ); } getThings(): void { @@ -130,7 +104,6 @@ export class DevicesComponent implements OnInit { resp => { this.notificationsService.success('Device successfully created', ''); this.getThings(); - this.getThingsStats(); }, ); } @@ -163,12 +136,41 @@ export class DevicesComponent implements OnInit { ); } - onSelection(event): void { - } - - onClickUpload(event): void { - } - - onClickSave(event): void { + onFileSelected(files: FileList) { + if (files && files.length > 0) { + const file: File = files.item(0); + const reader: FileReader = new FileReader(); + reader.readAsText(file); + reader.onload = () => { + const csv: string = reader.result as string; + const lines = csv.split('\n'); + const things: Thing[] = []; + + lines.forEach( line => { + const col = line.split('|'); + if (col[0] !== '' && col[0] !== '') { + let metadata = {}; + if (col[1] !== undefined) { + try { + metadata = JSON.parse(col[1]); + } catch (e) { + this.notificationsService.warn('Wrong metadata format', ''); + } + } + const thing = { + name: col[0], + metadata: metadata, + }; + things.push(thing); + } + }); + + this.thingsService.addThings(things).subscribe( + resp => { + this.getThings(); + }, + ); + }; + } } } From 6e1115d2c8b98c7fc79f079dcb9b447e0aa4e0f2 Mon Sep 17 00:00:00 2001 From: Manuel Imperiale Date: Wed, 5 Feb 2020 11:19:21 +0100 Subject: [PATCH 2/2] Fix reviews Signed-off-by: Manuel Imperiale --- src/app/pages/things/channels/channels.component.ts | 5 +++-- .../things/channels/details/channels.details.component.ts | 2 +- .../things/devices/details/devices.details.component.ts | 4 +--- src/app/pages/things/devices/devices.component.ts | 5 +++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/app/pages/things/channels/channels.component.ts b/src/app/pages/things/channels/channels.component.ts index b77330de7a..8e69606893 100644 --- a/src/app/pages/things/channels/channels.component.ts +++ b/src/app/pages/things/channels/channels.component.ts @@ -146,7 +146,8 @@ export class ChannelsComponent implements OnInit { lines.forEach( line => { const col = line.split('|'); - if (col[0] !== '' && col[0] !== '') { + const name = col[0]; + if (name !== '' && name !== '') { let metadata = {}; if (col[1] !== undefined) { try { @@ -157,7 +158,7 @@ export class ChannelsComponent implements OnInit { } const chann = { - name: col[0], + name: name, metadata: metadata, }; channels.push(chann); diff --git a/src/app/pages/things/channels/details/channels.details.component.ts b/src/app/pages/things/channels/details/channels.details.component.ts index cacdddbdfd..e455d5395c 100644 --- a/src/app/pages/things/channels/details/channels.details.component.ts +++ b/src/app/pages/things/channels/details/channels.details.component.ts @@ -110,7 +110,7 @@ export class ChannelsDetailsComponent implements OnInit { } getchannelMessages() { - if (this.connections.length > 0) { + if (this.connections.length) { this.messagesService.getMessages(this.channel.id, this.connections[0].key).subscribe( (respMsg: any) => { if (respMsg.messages) { diff --git a/src/app/pages/things/devices/details/devices.details.component.ts b/src/app/pages/things/devices/details/devices.details.component.ts index af030fdb54..61f50bd9ba 100644 --- a/src/app/pages/things/devices/details/devices.details.component.ts +++ b/src/app/pages/things/devices/details/devices.details.component.ts @@ -102,9 +102,7 @@ export class DevicesDetailsComponent implements OnInit { } else { this.messagesService.getMessages(chan.id, this.thing.key, this.thing.id).subscribe( (respMsg: any) => { - if (respMsg.messages) { - this.messages = respMsg.messages; - } + this.messages = respMsg.messages || this.messages; }, ); } diff --git a/src/app/pages/things/devices/devices.component.ts b/src/app/pages/things/devices/devices.component.ts index dfa24211ce..3d4a30f3db 100644 --- a/src/app/pages/things/devices/devices.component.ts +++ b/src/app/pages/things/devices/devices.component.ts @@ -148,7 +148,8 @@ export class DevicesComponent implements OnInit { lines.forEach( line => { const col = line.split('|'); - if (col[0] !== '' && col[0] !== '') { + const name = col[0]; + if (name !== '' && name !== '') { let metadata = {}; if (col[1] !== undefined) { try { @@ -158,7 +159,7 @@ export class DevicesComponent implements OnInit { } } const thing = { - name: col[0], + name: name, metadata: metadata, }; things.push(thing);