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 {MatButtonModule} from '@angular/material/button';
|
||||||
import {MatInputModule} from '@angular/material/input';
|
import {MatInputModule} from '@angular/material/input';
|
||||||
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
|
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({
|
@NgModule({
|
||||||
declarations: [LandingComponent],
|
declarations: [LandingComponent, ImagesComponent, MapComponent],
|
||||||
imports: [
|
imports: [
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
LoggerModule.forRoot({level: environment.log_level, serverLogLevel: NgxLoggerLevel.ERROR}),
|
LoggerModule.forRoot({level: environment.log_level, serverLogLevel: NgxLoggerLevel.ERROR}),
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatInputModule,
|
MatInputModule,
|
||||||
MatSlideToggleModule
|
MatSlideToggleModule,
|
||||||
],
|
MatListModule,
|
||||||
|
MatExpansionModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatPaginatorModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatTabsModule,
|
||||||
|
|
||||||
|
],
|
||||||
// enables injecting
|
// enables injecting
|
||||||
providers: [
|
providers: [
|
||||||
RestService,
|
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 {Component, OnInit} from '@angular/core';
|
||||||
import {RestService} from '../../services/rest.service';
|
import {RestService} from '../../services/rest.service';
|
||||||
import {NGXLogger} from 'ngx-logger';
|
import {NGXLogger} from 'ngx-logger';
|
||||||
|
import {ImageMetadata, StorageHealth} from '../../interfaces/interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-landing',
|
selector: 'app-landing',
|
||||||
@ -9,12 +10,22 @@ import {NGXLogger} from 'ngx-logger';
|
|||||||
})
|
})
|
||||||
export class LandingComponent implements OnInit {
|
export class LandingComponent implements OnInit {
|
||||||
|
|
||||||
|
storageHealth: StorageHealth;
|
||||||
|
images: ImageMetadata[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private logger: NGXLogger,
|
public logger: NGXLogger,
|
||||||
private restService: RestService
|
private restService: RestService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
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 {NGXLogger} from 'ngx-logger';
|
||||||
import {environment} from '../../environments/environment';
|
import {environment} from '../../environments/environment';
|
||||||
import {Observable} from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
|
import {tap} from 'rxjs/operators';
|
||||||
|
import {StorageHealth} from '../interfaces/interface';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RestService {
|
export class RestService {
|
||||||
@ -21,34 +23,50 @@ export class RestService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getAllImages(): Observable<any> {
|
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> {
|
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> {
|
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> {
|
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> {
|
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> {
|
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> {
|
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> {
|
public healthCheck(): Observable<StorageHealth> {
|
||||||
return this.http.get<any>(this.healthCheckUrl);
|
return this.http.get<StorageHealth>(this.healthCheckUrl).pipe(
|
||||||
|
tap(next => this.logger.debug('healthCheck', next))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,10 @@ body {
|
|||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1, h2 {
|
||||||
margin-top: 1em;
|
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 datetime
|
||||||
import os
|
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, ...)
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
PROJECT_ROOT = 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.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'channels',
|
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,6 @@ from app_be.views.rest_api import TestApiClass, ImageEndpoint, HealthEndpoint
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
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/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-]+)$', ImageEndpoint.image_api_get_single),
|
||||||
url(r'^image/get/(?P<identifier>[\w-]+)/version/(?P<version>[\w-]+)$', ImageEndpoint.image_api_get_single_version),
|
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