added simple navigation to switch from images to map view;
added simple images view skeleton; remove unnecessary websocket support;
This commit is contained in:
parent
91161247e8
commit
e2a09b465d
@ -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,
|
||||
|
||||
23
frontend/src/app/component/images/images.component.html
Normal file
23
frontend/src/app/component/images/images.component.html
Normal 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>
|
||||
32
frontend/src/app/component/images/images.component.ts
Normal file
32
frontend/src/app/component/images/images.component.ts
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
0
frontend/src/app/component/map/map.component.css
Normal file
0
frontend/src/app/component/map/map.component.css
Normal file
1
frontend/src/app/component/map/map.component.html
Normal file
1
frontend/src/app/component/map/map.component.html
Normal file
@ -0,0 +1 @@
|
||||
<h2>Image Locations</h2>
|
||||
20
frontend/src/app/component/map/map.component.ts
Normal file
20
frontend/src/app/component/map/map.component.ts
Normal 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 {
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,10 @@ body {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
h1, h2 {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
@ -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()),
|
||||
]))
|
||||
})
|
||||
@ -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',
|
||||
]
|
||||
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user