Merge pull request #52 in DEFINMA/definma-ui from f/prediction-delta to master

* commit 'f7f351d6a303899fd86628d142e8585e03809636':
  Add very simple prediction delta UI
  Allow viscosity number predictions to be saved
This commit is contained in:
Hartenstein Ruben (PEA4-Fe) 2021-02-25 11:53:49 +01:00
commit 0878ed9f10
9 changed files with 113 additions and 7 deletions

View File

@ -11,6 +11,7 @@ import {UsersComponent} from './users/users.component';
import {ChangelogComponent} from './changelog/changelog.component'; import {ChangelogComponent} from './changelog/changelog.component';
import {DocumentationDatabaseComponent} from './documentation/documentation-database/documentation-database.component'; import {DocumentationDatabaseComponent} from './documentation/documentation-database/documentation-database.component';
import {PredictionComponent} from './prediction/prediction.component'; import {PredictionComponent} from './prediction/prediction.component';
import {PredictionDeltaComponent} from './prediction-delta/prediction-delta.component';
import {ModelTemplatesComponent} from './model-templates/model-templates.component'; import {ModelTemplatesComponent} from './model-templates/model-templates.component';
import {DocumentationArchitectureComponent} from import {DocumentationArchitectureComponent} from
'./documentation/documentation-architecture/documentation-architecture.component'; './documentation/documentation-architecture/documentation-architecture.component';
@ -23,6 +24,7 @@ const routes: Routes = [
{path: '', component: HomeComponent}, {path: '', component: HomeComponent},
{path: 'home', component: HomeComponent}, {path: 'home', component: HomeComponent},
{path: 'prediction', component: PredictionComponent}, {path: 'prediction', component: PredictionComponent},
{path: 'prediction-delta', component: PredictionDeltaComponent},
{path: 'models', component: ModelTemplatesComponent}, {path: 'models', component: ModelTemplatesComponent},
{path: 'samples', component: SamplesComponent, canActivate: [LoginService]}, {path: 'samples', component: SamplesComponent, canActivate: [LoginService]},
{path: 'samples/new', component: SampleComponent, canActivate: [LoginService]}, {path: 'samples/new', component: SampleComponent, canActivate: [LoginService]},

View File

@ -2,6 +2,7 @@
<nav *rbMainNavItems> <nav *rbMainNavItems>
<a routerLink="/home" routerLinkActive="active" rbLoadingLink>Home</a> <a routerLink="/home" routerLinkActive="active" rbLoadingLink>Home</a>
<a routerLink="/prediction" routerLinkActive="active" rbLoadingLink *ngIf="login.hasPrediction">Prediction</a> <a routerLink="/prediction" routerLinkActive="active" rbLoadingLink *ngIf="login.hasPrediction">Prediction</a>
<a routerLink="/prediction-delta" routerLinkActive="active" rbLoadingLink *ngIf="login.hasPrediction">Prediction Delta</a>
<a routerLink="/models" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.dev">Models</a> <a routerLink="/models" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.dev">Models</a>
<a routerLink="/samples" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.read">Samples</a> <a routerLink="/samples" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.read">Samples</a>
<a routerLink="/materials" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.dev">Materials</a> <a routerLink="/materials" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.dev">Materials</a>

View File

@ -37,6 +37,7 @@ import { DocumentationArchitectureComponent } from
import { MaterialsComponent } from './materials/materials.component'; import { MaterialsComponent } from './materials/materials.component';
import { MaterialComponent } from './material/material.component'; import { MaterialComponent } from './material/material.component';
import { DocumentationModelsComponent } from './documentation/documentation-models/documentation-models.component'; import { DocumentationModelsComponent } from './documentation/documentation-models/documentation-models.component';
import { PredictionDeltaComponent } from './prediction-delta/prediction-delta.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -64,7 +65,8 @@ import { DocumentationModelsComponent } from './documentation/documentation-mode
DocumentationArchitectureComponent, DocumentationArchitectureComponent,
MaterialsComponent, MaterialsComponent,
MaterialComponent, MaterialComponent,
DocumentationModelsComponent DocumentationModelsComponent,
PredictionDeltaComponent
], ],
imports: [ imports: [
LocalStorageModule.forRoot({ LocalStorageModule.forRoot({

View File

@ -0,0 +1,10 @@
<h4>Enter the names of two predictions to compare</h4>
<rb-form-input label="Prediction A" [(ngModel)]="nameA"></rb-form-input>
<rb-form-input label="Prediction B" [(ngModel)]="nameB"></rb-form-input>
<p *ngIf="delta">
The result is {{ delta }}.
</p>
<rb-icon-button icon="forward-right" mode="primary" (click)="compare()">
Compare Predictions
</rb-icon-button>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PredictionDeltaComponent } from './prediction-delta.component';
describe('PredictionDeltaComponent', () => {
let component: PredictionDeltaComponent;
let fixture: ComponentFixture<PredictionDeltaComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PredictionDeltaComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PredictionDeltaComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,41 @@
import { Component } from '@angular/core';
import { animate, style, transition, trigger } from '@angular/animations';
import { ApiService } from '../services/api.service';
@Component({
selector: 'app-prediction-delta',
templateUrl: './prediction-delta.component.html',
styleUrls: ['./prediction-delta.component.scss'],
animations: [
trigger(
'inOut', [
transition(':enter', [
style({height: 0, opacity: 0}),
animate('0.5s ease-out', style({height: '*', opacity: 1}))
]),
transition(':leave', [
style({height: '*', opacity: 1}),
animate('0.5s ease-in', style({height: 0, opacity: 0}))
])
]
)
]
})
export class PredictionDeltaComponent {
nameA: String;
nameB: String;
delta: Number;
constructor(
private api: ApiService,
) {}
// Compares the two predictions
compare() {
let data = { nameA: this.nameA, nameB: this.nameB };
this.api.post<Number>('/prediction/compare', data, result => {
this.delta = result;
});
}
}

View File

@ -40,7 +40,7 @@
</div> </div>
<div class="file-input space-below"> <div class="file-input space-below">
<rb-form-file name="spectrum-upload" label="spectrum file" maxSize="10000000" class="space-below" multiple <rb-form-file name="spectrum-upload" label="Spectrum File" maxSize="10000000" class="space-below" multiple
(ngModelChange)="fileToArray($event)" placeholder="Select file or drag and drop" dragDrop ngModel> (ngModelChange)="fileToArray($event)" placeholder="Select file or drag and drop" dragDrop ngModel>
</rb-form-file> </rb-form-file>
@ -72,6 +72,20 @@
Export to PDF Export to PDF
</rb-icon-button> </rb-icon-button>
<!-- Save Prediction -->
<!-- Only available for single sample viscosity number predictions -->
<div *ngIf="spectrumNames.length && !multipleSamples && activeGroup.group === 'Viscosity Number'">
<rb-form-input name="prediction-name" label="Prediction Name" [(ngModel)]="predictionName"></rb-form-input>
<rb-icon-button icon="forward-right" mode="secondary" (click)="savePrediction()" [rbModal]="modalSuccess">
Save Prediction
</rb-icon-button>
<ng-template #modalSuccess>
<rb-alert alertTitle="Success" type="success" okBtnLabel="Got it">
Prediction saved!
</rb-alert>
</ng-template>
</div>
<div class="dpt-chart space-below"> <div class="dpt-chart space-below">
<canvas baseChart <canvas baseChart
class="dpt-chart" class="dpt-chart"

View File

@ -39,8 +39,11 @@ export class PredictionComponent implements OnInit {
loading = false; loading = false;
activeGroup: ModelItemModel = new ModelItemModel(); activeGroup: ModelItemModel = new ModelItemModel();
activeModelIndex = 0; activeModelIndex = 0;
predictionName: String;
// If true, spectra belong to different samples, otherwise multiple spectra from the same sample are given // If true, spectra belong to different samples, otherwise multiple spectra from the same sample are given
multipleSamples = false; multipleSamples = false;
spectrumNames: string[] = []; spectrumNames: string[] = [];
spectrum: string[][] = [[]]; spectrum: string[][] = [[]];
flattenedSpectra = []; flattenedSpectra = [];
@ -122,20 +125,28 @@ export class PredictionComponent implements OnInit {
}); });
} }
groupChange(index) { // Group was changed // Group was changed
groupChange(index) {
this.activeGroup = this.d.arr.modelGroups[index]; this.activeGroup = this.d.arr.modelGroups[index];
this.activeModelIndex = 0; this.activeModelIndex = 0;
this.result = undefined; this.result = undefined;
} }
// Save prediction to database
// This is not suited for multiple samples
savePrediction() {
let prediction = { name: this.predictionName, value: this.result.mean[0].value };
this.api.post<any>('/prediction/new', prediction);
}
// Aggregates spectrum names and prediction values into an associative array // Aggregates spectrum names and prediction values into an associative array
prepareExport() { prepareExport() {
const zip = (a, b) => a.map((k, i) => [k, b[i]]); const zip = (a, b) => a.map((k, i) => [k, b[i]]);
const values = this.result.predictions const values = this.result.predictions
.map(prediction => prediction .map(prediction => prediction
.filter(field => field.category === 'Prediction') .filter(field => field.category === 'Prediction')
.map(field => field.value)); .map(field => field.value));
return zip(this.spectrumNames, values); return zip(this.spectrumNames, values);
} }
// Generates a timestamp suitable for file naming // Generates a timestamp suitable for file naming