added simple navigation to switch from images to map view;

added simple images view skeleton;
remove unnecessary websocket support;
This commit is contained in:
Marco Zeisler 2021-01-11 18:53:12 +01:00
parent 91161247e8
commit e2a09b465d
16 changed files with 184 additions and 65 deletions

View File

@ -12,21 +12,36 @@ import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {MatButtonModule} from '@angular/material/button';
import {MatInputModule} from '@angular/material/input';
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
import {MatListModule} from '@angular/material/list';
import {MatExpansionModule} from '@angular/material/expansion';
import {MatCardModule} from '@angular/material/card';
import {MatPaginatorModule} from '@angular/material/paginator';
import {MatTableModule} from '@angular/material/table';
import {MatTabsModule} from '@angular/material/tabs';
import { ImagesComponent } from './component/images/images.component';
import { MapComponent } from './component/map/map.component';
@NgModule({
declarations: [LandingComponent],
imports: [
ReactiveFormsModule,
BrowserModule,
BrowserAnimationsModule,
LoggerModule.forRoot({level: environment.log_level, serverLogLevel: NgxLoggerLevel.ERROR}),
HttpClientModule,
MatFormFieldModule,
FormsModule,
MatButtonModule,
MatInputModule,
MatSlideToggleModule
],
declarations: [LandingComponent, ImagesComponent, MapComponent],
imports: [
ReactiveFormsModule,
BrowserModule,
BrowserAnimationsModule,
LoggerModule.forRoot({level: environment.log_level, serverLogLevel: NgxLoggerLevel.ERROR}),
HttpClientModule,
MatFormFieldModule,
FormsModule,
MatButtonModule,
MatInputModule,
MatSlideToggleModule,
MatListModule,
MatExpansionModule,
MatCardModule,
MatPaginatorModule,
MatTableModule,
MatTabsModule,
],
// enables injecting
providers: [
RestService,

View File

@ -0,0 +1,23 @@
<h2>Available Images</h2>
<div>
<span style="width: 9.5em; display: inline-block; margin-left: 1.5em">Name</span>
<span style="width: 15em; display: inline-block">Date</span>
</div>
<mat-expansion-panel *ngFor="let image of images | slice:paginationGetStart():paginationGetEnd()">
<mat-expansion-panel-header (click)="$event.stopPropagation()">
<span style="width: 10em"
>{{image.name}}</span>
<span style="width: 15em"
>{{image.datetime}}</span>
</mat-expansion-panel-header>
TEST
</mat-expansion-panel>
<mat-paginator [length]="images.length"
[pageSize]="pageSizeOptions[0]"
[pageSizeOptions]="pageSizeOptions"
(page)="lastPageEvent = $event"
showFirstLastButtons
></mat-paginator>

View File

@ -0,0 +1,32 @@
import {Component, Input, OnInit} from '@angular/core';
import {PageEvent} from '@angular/material/paginator';
import {ImageMetadata} from '../../interfaces/interface';
@Component({
selector: 'app-images',
templateUrl: './images.component.html',
styleUrls: ['./images.component.css']
})
export class ImagesComponent implements OnInit {
@Input()
images: ImageMetadata[];
pageSizeOptions: number[] = [5, 10, 25, 100];
lastPageEvent: PageEvent;
constructor() { }
ngOnInit(): void {
this.lastPageEvent = {pageIndex: 0, pageSize: this.pageSizeOptions[0], length: this.images.length};
}
paginationGetStart(): number {
return this.lastPageEvent.pageIndex * this.lastPageEvent.pageSize;
}
paginationGetEnd(): number {
return this.paginationGetStart() + this.lastPageEvent.pageSize;
}
}

View File

@ -1 +1,16 @@
<h1>Federated Storage Infrastructure for IoT Sensor Data</h1>
<h1>
Federated Storage Infrastructure for IoT Sensor Data
<span *ngFor="let storage of storageHealth | keyvalue" style="font-size: 11px;">
<span *ngIf="storage.key; else healthFailure" style="color: green; float: right">{{storage.key}} ✓ </span>
<ng-template #healthFailure style="color: red; float: right">{{storage.key}} x </ng-template>
</span>
</h1>
<mat-tab-group *ngIf="images.length > 0">
<mat-tab label="Images">
<app-images [images]="images"></app-images>
</mat-tab>
<mat-tab label="Map">
<app-map [images]="images"></app-map>
</mat-tab>
</mat-tab-group>

View File

@ -1,6 +1,7 @@
import {Component, OnInit} from '@angular/core';
import {RestService} from '../../services/rest.service';
import {NGXLogger} from 'ngx-logger';
import {ImageMetadata, StorageHealth} from '../../interfaces/interface';
@Component({
selector: 'app-landing',
@ -9,12 +10,22 @@ import {NGXLogger} from 'ngx-logger';
})
export class LandingComponent implements OnInit {
storageHealth: StorageHealth;
images: ImageMetadata[] = [];
constructor(
private logger: NGXLogger,
public logger: NGXLogger,
private restService: RestService
) {
}
ngOnInit(): void {
this.restService.healthCheck().toPromise().then(
storageHealth => this.storageHealth = storageHealth
).catch(err => this.logger.error(err));
this.restService.getAllImages().toPromise().then(images => this.images = images).catch(err => this.logger.error(err));
}
}

View File

@ -0,0 +1 @@
<h2>Image Locations</h2>

View File

@ -0,0 +1,20 @@
import {Component, Input, OnInit} from '@angular/core';
import {ImageMetadata} from '../../interfaces/interface';
@Component({
selector: 'app-map',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent implements OnInit {
@Input()
images: ImageMetadata[];
constructor() {
}
ngOnInit(): void {
}
}

View File

@ -0,0 +1,20 @@
export interface StorageHealth {
[storage: string]: boolean;
}
export interface ImageMetadata {
datetime: string;
device_id: string;
filename: string;
frame_num: number;
identifier: string;
latitude: number;
location: [number, number];
longitude: number;
name: string;
place_ident: string;
seq_id: string;
seq_num_frames: number;
sha512: string;
selected?: boolean;
}

View File

@ -3,6 +3,8 @@ import {Injectable} from '@angular/core';
import {NGXLogger} from 'ngx-logger';
import {environment} from '../../environments/environment';
import {Observable} from 'rxjs';
import {tap} from 'rxjs/operators';
import {StorageHealth} from '../interfaces/interface';
@Injectable()
export class RestService {
@ -21,34 +23,50 @@ export class RestService {
}
public getAllImages(): Observable<any> {
return this.http.get<any>(this.getImageUrl + 'all');
return this.http.get<any>(this.getImageUrl + 'all').pipe(
tap(next => this.logger.debug('getAllImages', next))
);
}
public getImage(id: string): Observable<any> {
return this.http.get<any>(this.getImageUrl + id);
return this.http.get<any>(this.getImageUrl + id).pipe(
tap(next => this.logger.debug('getImage', id, next))
);
}
public getImageVersion(id: string, version: string): Observable<any> {
return this.http.get<any>(this.getImageUrl + id + '/version/' + version);
return this.http.get<any>(this.getImageUrl + id + '/version/' + version).pipe(
tap(next => this.logger.debug('getImageVersion', id, version, next))
);
}
public deleteAllImages(): Observable<any> {
return this.http.delete<any>(this.deleteImageUrl + 'all');
return this.http.delete<any>(this.deleteImageUrl + 'all').pipe(
tap(next => this.logger.debug('deleteAllImages', next))
);
}
public deleteImage(id: string): Observable<any> {
return this.http.delete<any>(this.deleteImageUrl + id);
return this.http.delete<any>(this.deleteImageUrl + id).pipe(
tap(next => this.logger.debug('deleteImage', id, next))
);
}
public postImage(id: string, image: any): Observable<any> {
return this.http.post<any>(this.postImageUrl + id, image);
return this.http.post<any>(this.postImageUrl + id, image).pipe(
tap(next => this.logger.debug('postImage', id, image, next))
);
}
public updateImage(id: string, image: any): Observable<any> {
return this.http.put<any>(this.updateImageUrl + id, image);
return this.http.put<any>(this.updateImageUrl + id, image).pipe(
tap(next => this.logger.debug('updateImage', id, image, next))
);
}
public healthCheck(): Observable<any> {
return this.http.get<any>(this.healthCheckUrl);
public healthCheck(): Observable<StorageHealth> {
return this.http.get<StorageHealth>(this.healthCheckUrl).pipe(
tap(next => this.logger.debug('healthCheck', next))
);
}
}

View File

@ -8,6 +8,10 @@ body {
margin: auto;
}
h1 {
h1, h2 {
margin-top: 1em;
}
h2 {
margin-bottom: 1.5em;
}

View File

@ -1,17 +0,0 @@
from django.conf.urls import url
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.sessions import SessionMiddlewareStack
from django.core.asgi import get_asgi_application
from .views.ws_api import CustomConsumer
application = ProtocolTypeRouter({
# Django's ASGI application to handle traditional HTTP requests
"http": get_asgi_application(),
# WebSocket send handler
"websocket": SessionMiddlewareStack(URLRouter([
url(r"^test-ws-endpoint/$", CustomConsumer.as_asgi()),
]))
})

View File

@ -8,9 +8,6 @@ https://docs.djangoproject.com/en/2.0/ref/settings/
import datetime
import os
# set the websocket routing module location here
ASGI_APPLICATION = 'app_be.routing.application'
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
@ -34,7 +31,6 @@ INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'channels',
'rest_framework',
]

View File

@ -22,7 +22,6 @@ from app_be.views.rest_api import TestApiClass, ImageEndpoint, HealthEndpoint
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^test/', TestApiClass.test_api),
url(r'^image/get/all$', ImageEndpoint.image_api_get_all),
url(r'^image/get/(?P<identifier>[\w-]+)$', ImageEndpoint.image_api_get_single),
url(r'^image/get/(?P<identifier>[\w-]+)/version/(?P<version>[\w-]+)$', ImageEndpoint.image_api_get_single_version),

View File

@ -1,18 +0,0 @@
import logging
from channels.generic.websocket import WebsocketConsumer
logger = logging.getLogger(__name__)
class CustomConsumer(WebsocketConsumer):
def connect(self):
self.accept()
def receive(self, text_data=None, bytes_data=None):
self.send('1. response: {}'.format(text_data))
self.send('2. response: {}'.format(text_data))
def disconnect(self, code):
pass