Merge pull request #31 in ~VLE2FE/definma-ui from development to master
* commit '52e0d94e6187d377afe336c5d2b80b82d9d110f1': implemented model list updated to new model format
This commit is contained in:
commit
7187bba40b
@ -32,6 +32,7 @@ import { ServiceWorkerModule } from '@angular/service-worker';
|
|||||||
import { environment } from '../environments/environment';
|
import { environment } from '../environments/environment';
|
||||||
import { HelpComponent } from './help/help.component';
|
import { HelpComponent } from './help/help.component';
|
||||||
import { ModelTemplatesComponent } from './model-templates/model-templates.component';
|
import { ModelTemplatesComponent } from './model-templates/model-templates.component';
|
||||||
|
import { SizePipe } from './size.pipe';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -54,7 +55,8 @@ import { ModelTemplatesComponent } from './model-templates/model-templates.compo
|
|||||||
DocumentationDatabaseComponent,
|
DocumentationDatabaseComponent,
|
||||||
PredictionComponent,
|
PredictionComponent,
|
||||||
HelpComponent,
|
HelpComponent,
|
||||||
ModelTemplatesComponent
|
ModelTemplatesComponent,
|
||||||
|
SizePipe
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
LocalStorageModule.forRoot({
|
LocalStorageModule.forRoot({
|
||||||
|
@ -15,9 +15,6 @@
|
|||||||
<ng-template rbFormValidationMessage="failure">{{nameInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{nameInput.errors.failure}}</ng-template>
|
||||||
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<rb-form-input name="label" label="label" appValidate="string" [(ngModel)]="model.label" #labelInput="ngModel">
|
|
||||||
<ng-template rbFormValidationMessage="failure">{{labelInput.errors.failure}}</ng-template>
|
|
||||||
</rb-form-input>
|
|
||||||
<rb-form-input name="url" label="URL" appValidate="url" required [(ngModel)]="model.url" #urlInput="ngModel">
|
<rb-form-input name="url" label="URL" appValidate="url" required [(ngModel)]="model.url" #urlInput="ngModel">
|
||||||
<ng-template rbFormValidationMessage="failure">{{urlInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{urlInput.errors.failure}}</ng-template>
|
||||||
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
||||||
@ -28,10 +25,9 @@
|
|||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<rb-table class="space-above">
|
<rb-table class="space-above space-below">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Label</th>
|
|
||||||
<th>URL</th>
|
<th>URL</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
@ -41,7 +37,6 @@
|
|||||||
<tr><th>{{group.group}}</th><th></th><th></th><th></th><th></th></tr>
|
<tr><th>{{group.group}}</th><th></th><th></th><th></th><th></th></tr>
|
||||||
<tr *ngFor="let modelItem of group.models">
|
<tr *ngFor="let modelItem of group.models">
|
||||||
<td>{{modelItem.name}}</td>
|
<td>{{modelItem.name}}</td>
|
||||||
<td>{{modelItem.label}}</td>
|
|
||||||
<td>{{modelItem.url}}</td>
|
<td>{{modelItem.url}}</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="rb-ic rb-ic-edit clickable"
|
<span class="rb-ic rb-ic-edit clickable"
|
||||||
@ -54,14 +49,27 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="rb-ic rb-ic-delete clickable"
|
<span class="rb-ic rb-ic-delete clickable"
|
||||||
(click)="deleteModel(group.group, modelItem.name, modalDeleteConfirm)"></span>
|
(click)="delete(modalDeleteConfirm, modelItem.name, group.group)"></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</rb-table>
|
</rb-table>
|
||||||
|
|
||||||
|
<rb-table>
|
||||||
|
<tr>
|
||||||
|
<th>Model files</th>
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
<tr *ngFor="let file of d.arr.modelFiles">
|
||||||
|
<td>{{file.name}}</td>
|
||||||
|
<td>{{file.size | size:'M'}}</td>
|
||||||
|
<td><span class="rb-ic rb-ic-delete clickable" (click)="delete(modalDeleteConfirm, file.name)"></span></td>
|
||||||
|
</tr>
|
||||||
|
</rb-table>
|
||||||
|
|
||||||
<ng-template #modalDeleteConfirm>
|
<ng-template #modalDeleteConfirm>
|
||||||
<rb-alert alertTitle="Are you sure?" type="danger" okBtnLabel="Delete model" cancelBtnLabel="Cancel">
|
<rb-alert alertTitle="Are you sure?" type="danger" okBtnLabel="Delete model" cancelBtnLabel="Cancel">
|
||||||
Do you really want to delete this model?
|
Do you really want to delete?
|
||||||
</rb-alert>
|
</rb-alert>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -35,13 +35,14 @@ export class ModelTemplatesComponent implements OnInit {
|
|||||||
this.d.load('modelGroups', () => {
|
this.d.load('modelGroups', () => {
|
||||||
this.groups = this.d.arr.modelGroups.map(e => e.group);
|
this.groups = this.d.arr.modelGroups.map(e => e.group);
|
||||||
});
|
});
|
||||||
|
this.d.load('modelFiles');
|
||||||
}
|
}
|
||||||
|
|
||||||
saveModel() {
|
saveModel() {
|
||||||
console.log(this.modelGroup);
|
console.log(this.modelGroup);
|
||||||
console.log(this.oldModelGroup);
|
console.log(this.oldModelGroup);
|
||||||
if (this.oldModelGroup !== '' && this.modelGroup !== this.oldModelGroup) { // group was changed, delete model in old group
|
if (this.oldModelGroup !== '' && this.modelGroup !== this.oldModelGroup) { // group was changed, delete model in old group
|
||||||
this.deleteModel(this.oldModelGroup, this.oldModelName);
|
this.deleteModel(null, this.oldModelGroup, this.oldModelName);
|
||||||
}
|
}
|
||||||
this.api.post('/model/' + this.modelGroup, this.model, () => {
|
this.api.post('/model/' + this.modelGroup, this.model, () => {
|
||||||
this.newModel = false;
|
this.newModel = false;
|
||||||
@ -53,7 +54,7 @@ export class ModelTemplatesComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteModel(group, name, modal = null) {
|
delete(modal, name, group = null) {
|
||||||
new Promise(resolve => {
|
new Promise(resolve => {
|
||||||
if (modal) {
|
if (modal) {
|
||||||
this.modal.open(modal).then(result => {
|
this.modal.open(modal).then(result => {
|
||||||
@ -65,10 +66,17 @@ export class ModelTemplatesComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
if (res) {
|
if (res) {
|
||||||
|
if (group) { // delete group
|
||||||
this.api.delete(`/model/${group}/${name}`, () => {
|
this.api.delete(`/model/${group}/${name}`, () => {
|
||||||
this.loadGroups();
|
this.loadGroups();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else { // delete file
|
||||||
|
this.api.delete(`/model/file/${name}`, () => {
|
||||||
|
this.d.arr.modelFiles.splice(this.d.arr.modelFiles.findIndex(e => e.name === name), 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
7
src/app/models/model-file.model.spec.ts
Normal file
7
src/app/models/model-file.model.spec.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { ModelFileModel } from './model-file.model';
|
||||||
|
|
||||||
|
describe('ModelFile.Model', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(new ModelFileModel()).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
6
src/app/models/model-file.model.ts
Normal file
6
src/app/models/model-file.model.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import {BaseModel} from './base.model';
|
||||||
|
|
||||||
|
export class ModelFileModel extends BaseModel {
|
||||||
|
name = '';
|
||||||
|
size = 0;
|
||||||
|
}
|
@ -4,7 +4,6 @@ export class ModelItemModel extends BaseModel {
|
|||||||
group = '';
|
group = '';
|
||||||
models = [{
|
models = [{
|
||||||
name: '',
|
name: '',
|
||||||
url: '',
|
url: ''
|
||||||
label: ''
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
@ -13,21 +13,17 @@
|
|||||||
<div *ngIf="result" class="result" [@inOut]>
|
<div *ngIf="result" class="result" [@inOut]>
|
||||||
<ng-container *ngIf="multipleSamples; else singleSampleResult">
|
<ng-container *ngIf="multipleSamples; else singleSampleResult">
|
||||||
<h4 *ngFor="let prediction of result.predictions; index as i">
|
<h4 *ngFor="let prediction of result.predictions; index as i">
|
||||||
{{spectrumNames[i]}}: {{prediction}} {{activeGroup.models[activeModelIndex].label}}
|
{{spectrumNames[i]}}: {{prediction}}<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
|
||||||
<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
|
|
||||||
</h4>
|
</h4>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-template #singleSampleResult>
|
<ng-template #singleSampleResult>
|
||||||
<h4>
|
<h4>
|
||||||
Average result: {{result.meanPrediction}} {{activeGroup.models[activeModelIndex].label}}
|
Average result: {{result.mean}}<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
|
||||||
<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>,
|
|
||||||
standard deviation: {{result.std}}<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
|
|
||||||
</h4>
|
</h4>
|
||||||
<a href="javascript:" class="rb-details-toggle" rbDetailsToggle #triggerDetails="rbDetailsToggle">Details</a>
|
<a href="javascript:" class="rb-details-toggle" rbDetailsToggle #triggerDetails="rbDetailsToggle">Details</a>
|
||||||
<div *ngIf="triggerDetails.open" class="space-below">
|
<div *ngIf="triggerDetails.open" class="space-below">
|
||||||
<p *ngFor="let prediction of result.predictions; index as i">
|
<p *ngFor="let prediction of result.predictions; index as i">
|
||||||
{{spectrumNames[i]}}: {{prediction}} {{activeGroup.models[activeModelIndex].label}}
|
{{spectrumNames[i]}}: {{prediction}}<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
|
||||||
<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -3,15 +3,10 @@ import {ChartOptions} from 'chart.js';
|
|||||||
import {ApiService} from '../services/api.service';
|
import {ApiService} from '../services/api.service';
|
||||||
import {animate, style, transition, trigger} from '@angular/animations';
|
import {animate, style, transition, trigger} from '@angular/animations';
|
||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
|
import omit from 'lodash/omit';
|
||||||
import {DataService} from '../services/data.service';
|
import {DataService} from '../services/data.service';
|
||||||
import {ModelItemModel} from '../models/model-item.model';
|
import {ModelItemModel} from '../models/model-item.model';
|
||||||
import {HttpClient} from '@angular/common/http';
|
|
||||||
|
|
||||||
interface PredictionResult {
|
|
||||||
meanPrediction: string;
|
|
||||||
std: string;
|
|
||||||
predictions: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-prediction',
|
selector: 'app-prediction',
|
||||||
@ -34,7 +29,7 @@ interface PredictionResult {
|
|||||||
})
|
})
|
||||||
export class PredictionComponent implements OnInit {
|
export class PredictionComponent implements OnInit {
|
||||||
|
|
||||||
result: PredictionResult;
|
result: {predictions: string[], mean: string};
|
||||||
loading = false;
|
loading = false;
|
||||||
activeGroup: ModelItemModel = new ModelItemModel();
|
activeGroup: ModelItemModel = new ModelItemModel();
|
||||||
activeModelIndex = 0;
|
activeModelIndex = 0;
|
||||||
@ -104,9 +99,16 @@ export class PredictionComponent implements OnInit {
|
|||||||
|
|
||||||
loadPrediction() {
|
loadPrediction() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
console.log(this.activeModelIndex);
|
this.api.post<any>(this.activeGroup.models[this.activeModelIndex].url, this.flattenedSpectra, data => {
|
||||||
this.api.post<PredictionResult>(this.activeGroup.models[this.activeModelIndex].url, this.flattenedSpectra, data => {
|
this.result = {
|
||||||
this.result = data;
|
predictions: Object.entries(omit(data, ['mean', 'std', 'label']))
|
||||||
|
.map((p: any) => p[1].map(e => `${p[0]}: ${e} ${data.label[p[0]]}`))
|
||||||
|
.reduce((s, e) => s.map((el, i) => this.clip(el) + ', ' + e[i])),
|
||||||
|
mean: Object.keys(data.mean).map(e =>
|
||||||
|
this.clip(`${e}: ${data.mean[e]} ${data.label[e]}`) + (data.std[e] !== '' ? ` (standard deviation: ${data.std[e]})` : '')
|
||||||
|
).join(', ')
|
||||||
|
};
|
||||||
|
console.log(this.result);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -115,4 +117,8 @@ export class PredictionComponent implements OnInit {
|
|||||||
this.activeGroup = this.d.arr.modelGroups[index];
|
this.activeGroup = this.d.arr.modelGroups[index];
|
||||||
this.result = undefined;
|
this.result = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clip(str) { // clip spaces at start and end
|
||||||
|
return str.replace(/^\s*(.*?)\s*$/, '$1');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,7 +238,7 @@
|
|||||||
[(ngModel)]="measurement.measurement_template"
|
[(ngModel)]="measurement.measurement_template"
|
||||||
(ngModelChange)="clearMeasurement(gIndex, mIndex)">
|
(ngModelChange)="clearMeasurement(gIndex, mIndex)">
|
||||||
<option [value]="sample.condition.condition_template">
|
<option [value]="sample.condition.condition_template">
|
||||||
{{d.id.measurementTemplates[measurement.measurement_template].name}} - current
|
{{d.id.measurementTemplates[measurement.measurement_template].name}} - old
|
||||||
</option>
|
</option>
|
||||||
<option *ngFor="let m of d.latest.measurementTemplates" [value]="m._id">{{m.name}}</option>
|
<option *ngFor="let m of d.latest.measurementTemplates" [value]="m._id">{{m.name}}</option>
|
||||||
</rb-form-select>
|
</rb-form-select>
|
||||||
@ -330,7 +330,7 @@
|
|||||||
<ng-template #modalDeleteConfirm>
|
<ng-template #modalDeleteConfirm>
|
||||||
<rb-alert alertTitle="Are you sure?" type="danger" [okBtnLabel]="'Delete sample' + (samples.length > 1 ? 's' : '')"
|
<rb-alert alertTitle="Are you sure?" type="danger" [okBtnLabel]="'Delete sample' + (samples.length > 1 ? 's' : '')"
|
||||||
cancelBtnLabel="Cancel">
|
cancelBtnLabel="Cancel">
|
||||||
Do you really want to delete {{samples.length > 1 ? 'these samples' : 'this sample'}}?
|
Do you really want to delete {{(samples.length > 1 ? 'samples ' : 'sample ') + sampleNames()}}?
|
||||||
</rb-alert>
|
</rb-alert>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
@ -613,6 +613,10 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
return this.sampleReferenceList.bind(this);
|
return this.sampleReferenceList.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sampleNames() {
|
||||||
|
return this.samples.map(e => e.number).join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
uniqueCfValues(index) { // returns all names until index for unique check
|
uniqueCfValues(index) { // returns all names until index for unique check
|
||||||
return this.customFields ? this.customFields.slice(0, index).map(e => e[0]) : [];
|
return this.customFields ? this.customFields.slice(0, index).map(e => e[0]) : [];
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import {MaterialModel} from '../models/material.model';
|
|||||||
import {BaseModel} from '../models/base.model';
|
import {BaseModel} from '../models/base.model';
|
||||||
import {UserModel} from '../models/user.model';
|
import {UserModel} from '../models/user.model';
|
||||||
import {ModelItemModel} from '../models/model-item.model';
|
import {ModelItemModel} from '../models/model-item.model';
|
||||||
|
import {ModelFileModel} from '../models/model-file.model';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -25,6 +26,7 @@ export class DataService {
|
|||||||
sampleNotesFields: {path: '/sample/notes/fields', model: TemplateModel, type: 'idArray'},
|
sampleNotesFields: {path: '/sample/notes/fields', model: TemplateModel, type: 'idArray'},
|
||||||
users: {path: '/users', model: UserModel, type: 'idArray'},
|
users: {path: '/users', model: UserModel, type: 'idArray'},
|
||||||
modelGroups: {path: '/model/groups', model: ModelItemModel, type: 'array'},
|
modelGroups: {path: '/model/groups', model: ModelItemModel, type: 'array'},
|
||||||
|
modelFiles: {path: '/model/files', model: ModelFileModel, type: 'array'},
|
||||||
user: {path: '/user', model: UserModel, type: 'string'},
|
user: {path: '/user', model: UserModel, type: 'string'},
|
||||||
userKey: {path: '/user/key', model: BaseModel, type: 'string'}
|
userKey: {path: '/user/key', model: BaseModel, type: 'string'}
|
||||||
};
|
};
|
||||||
|
8
src/app/size.pipe.spec.ts
Normal file
8
src/app/size.pipe.spec.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { SizePipe } from './size.pipe';
|
||||||
|
|
||||||
|
describe('SizePipe', () => {
|
||||||
|
it('create an instance', () => {
|
||||||
|
const pipe = new SizePipe();
|
||||||
|
expect(pipe).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
16
src/app/size.pipe.ts
Normal file
16
src/app/size.pipe.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'size'
|
||||||
|
})
|
||||||
|
export class SizePipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(value: number, exp: string): string {
|
||||||
|
const divide = ['', 'k', 'M', 'G', 'T'].indexOf(exp);
|
||||||
|
for (let i = 0; i < divide; i ++) {
|
||||||
|
value = value / 1024;
|
||||||
|
}
|
||||||
|
return `${value.toFixed(2)} ${exp}B`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user