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:
		@@ -32,6 +32,7 @@ import { ServiceWorkerModule } from '@angular/service-worker';
 | 
			
		||||
import { environment } from '../environments/environment';
 | 
			
		||||
import { HelpComponent } from './help/help.component';
 | 
			
		||||
import { ModelTemplatesComponent } from './model-templates/model-templates.component';
 | 
			
		||||
import { SizePipe } from './size.pipe';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations: [
 | 
			
		||||
@@ -54,7 +55,8 @@ import { ModelTemplatesComponent } from './model-templates/model-templates.compo
 | 
			
		||||
    DocumentationDatabaseComponent,
 | 
			
		||||
    PredictionComponent,
 | 
			
		||||
    HelpComponent,
 | 
			
		||||
    ModelTemplatesComponent
 | 
			
		||||
    ModelTemplatesComponent,
 | 
			
		||||
    SizePipe
 | 
			
		||||
  ],
 | 
			
		||||
  imports: [
 | 
			
		||||
    LocalStorageModule.forRoot({
 | 
			
		||||
 
 | 
			
		||||
@@ -15,9 +15,6 @@
 | 
			
		||||
    <ng-template rbFormValidationMessage="failure">{{nameInput.errors.failure}}</ng-template>
 | 
			
		||||
    <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
			
		||||
  </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">
 | 
			
		||||
    <ng-template rbFormValidationMessage="failure">{{urlInput.errors.failure}}</ng-template>
 | 
			
		||||
    <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
			
		||||
@@ -28,10 +25,9 @@
 | 
			
		||||
  </rb-icon-button>
 | 
			
		||||
</form>
 | 
			
		||||
 | 
			
		||||
<rb-table class="space-above">
 | 
			
		||||
<rb-table class="space-above space-below">
 | 
			
		||||
  <tr>
 | 
			
		||||
    <th>Name</th>
 | 
			
		||||
    <th>Label</th>
 | 
			
		||||
    <th>URL</th>
 | 
			
		||||
    <th></th>
 | 
			
		||||
    <th></th>
 | 
			
		||||
@@ -41,7 +37,6 @@
 | 
			
		||||
    <tr><th>{{group.group}}</th><th></th><th></th><th></th><th></th></tr>
 | 
			
		||||
    <tr *ngFor="let modelItem of group.models">
 | 
			
		||||
      <td>{{modelItem.name}}</td>
 | 
			
		||||
      <td>{{modelItem.label}}</td>
 | 
			
		||||
      <td>{{modelItem.url}}</td>
 | 
			
		||||
      <td>
 | 
			
		||||
        <span class="rb-ic rb-ic-edit clickable"
 | 
			
		||||
@@ -54,14 +49,27 @@
 | 
			
		||||
      </td>
 | 
			
		||||
      <td>
 | 
			
		||||
        <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>
 | 
			
		||||
    </tr>
 | 
			
		||||
  </ng-container>
 | 
			
		||||
</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>
 | 
			
		||||
  <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>
 | 
			
		||||
</ng-template>
 | 
			
		||||
 
 | 
			
		||||
@@ -35,13 +35,14 @@ export class ModelTemplatesComponent implements OnInit {
 | 
			
		||||
    this.d.load('modelGroups', () => {
 | 
			
		||||
      this.groups = this.d.arr.modelGroups.map(e => e.group);
 | 
			
		||||
    });
 | 
			
		||||
    this.d.load('modelFiles');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  saveModel() {
 | 
			
		||||
    console.log(this.modelGroup);
 | 
			
		||||
    console.log(this.oldModelGroup);
 | 
			
		||||
    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.newModel = false;
 | 
			
		||||
@@ -53,7 +54,7 @@ export class ModelTemplatesComponent implements OnInit {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  deleteModel(group, name, modal = null) {
 | 
			
		||||
  delete(modal, name, group = null) {
 | 
			
		||||
    new Promise(resolve => {
 | 
			
		||||
      if (modal) {
 | 
			
		||||
        this.modal.open(modal).then(result => {
 | 
			
		||||
@@ -65,9 +66,16 @@ export class ModelTemplatesComponent implements OnInit {
 | 
			
		||||
      }
 | 
			
		||||
    }).then(res => {
 | 
			
		||||
      if (res) {
 | 
			
		||||
        this.api.delete(`/model/${group}/${name}`, () => {
 | 
			
		||||
          this.loadGroups();
 | 
			
		||||
        });
 | 
			
		||||
        if (group) {  // delete group
 | 
			
		||||
          this.api.delete(`/model/${group}/${name}`, () => {
 | 
			
		||||
            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 = '';
 | 
			
		||||
  models = [{
 | 
			
		||||
    name: '',
 | 
			
		||||
    url: '',
 | 
			
		||||
    label: ''
 | 
			
		||||
    url: ''
 | 
			
		||||
  }];
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,21 +13,17 @@
 | 
			
		||||
<div *ngIf="result" class="result" [@inOut]>
 | 
			
		||||
  <ng-container *ngIf="multipleSamples; else singleSampleResult">
 | 
			
		||||
    <h4 *ngFor="let prediction of result.predictions; index as i">
 | 
			
		||||
      {{spectrumNames[i]}}: {{prediction}} {{activeGroup.models[activeModelIndex].label}}
 | 
			
		||||
      <a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
 | 
			
		||||
      {{spectrumNames[i]}}: {{prediction}}<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
 | 
			
		||||
    </h4>
 | 
			
		||||
  </ng-container>
 | 
			
		||||
  <ng-template #singleSampleResult>
 | 
			
		||||
    <h4>
 | 
			
		||||
      Average result: {{result.meanPrediction}} {{activeGroup.models[activeModelIndex].label}}
 | 
			
		||||
      <a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>,
 | 
			
		||||
      standard deviation: {{result.std}}<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
 | 
			
		||||
      Average result: {{result.mean}}<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
 | 
			
		||||
    </h4>
 | 
			
		||||
    <a href="javascript:" class="rb-details-toggle" rbDetailsToggle #triggerDetails="rbDetailsToggle">Details</a>
 | 
			
		||||
    <div *ngIf="triggerDetails.open" class="space-below">
 | 
			
		||||
      <p *ngFor="let prediction of result.predictions; index as i">
 | 
			
		||||
        {{spectrumNames[i]}}: {{prediction}} {{activeGroup.models[activeModelIndex].label}}
 | 
			
		||||
        <a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
 | 
			
		||||
        {{spectrumNames[i]}}: {{prediction}}<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
 | 
			
		||||
      </p>
 | 
			
		||||
    </div>
 | 
			
		||||
  </ng-template>
 | 
			
		||||
 
 | 
			
		||||
@@ -3,15 +3,10 @@ import {ChartOptions} from 'chart.js';
 | 
			
		||||
import {ApiService} from '../services/api.service';
 | 
			
		||||
import {animate, style, transition, trigger} from '@angular/animations';
 | 
			
		||||
import cloneDeep from 'lodash/cloneDeep';
 | 
			
		||||
import omit from 'lodash/omit';
 | 
			
		||||
import {DataService} from '../services/data.service';
 | 
			
		||||
import {ModelItemModel} from '../models/model-item.model';
 | 
			
		||||
import {HttpClient} from '@angular/common/http';
 | 
			
		||||
 | 
			
		||||
interface PredictionResult {
 | 
			
		||||
  meanPrediction: string;
 | 
			
		||||
  std: string;
 | 
			
		||||
  predictions: string[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-prediction',
 | 
			
		||||
@@ -34,7 +29,7 @@ interface PredictionResult {
 | 
			
		||||
})
 | 
			
		||||
export class PredictionComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
  result: PredictionResult;
 | 
			
		||||
  result: {predictions: string[], mean: string};
 | 
			
		||||
  loading = false;
 | 
			
		||||
  activeGroup: ModelItemModel = new ModelItemModel();
 | 
			
		||||
  activeModelIndex = 0;
 | 
			
		||||
@@ -104,9 +99,16 @@ export class PredictionComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
  loadPrediction() {
 | 
			
		||||
    this.loading = true;
 | 
			
		||||
    console.log(this.activeModelIndex);
 | 
			
		||||
    this.api.post<PredictionResult>(this.activeGroup.models[this.activeModelIndex].url, this.flattenedSpectra, data => {
 | 
			
		||||
      this.result = data;
 | 
			
		||||
    this.api.post<any>(this.activeGroup.models[this.activeModelIndex].url, this.flattenedSpectra, data => {
 | 
			
		||||
      this.result = {
 | 
			
		||||
        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;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
@@ -115,4 +117,8 @@ export class PredictionComponent implements OnInit {
 | 
			
		||||
    this.activeGroup = this.d.arr.modelGroups[index];
 | 
			
		||||
    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"
 | 
			
		||||
                            (ngModelChange)="clearMeasurement(gIndex, mIndex)">
 | 
			
		||||
              <option [value]="sample.condition.condition_template">
 | 
			
		||||
                {{d.id.measurementTemplates[measurement.measurement_template].name}} - current
 | 
			
		||||
                {{d.id.measurementTemplates[measurement.measurement_template].name}} - old
 | 
			
		||||
              </option>
 | 
			
		||||
              <option *ngFor="let m of d.latest.measurementTemplates" [value]="m._id">{{m.name}}</option>
 | 
			
		||||
            </rb-form-select>
 | 
			
		||||
@@ -330,7 +330,7 @@
 | 
			
		||||
<ng-template #modalDeleteConfirm>
 | 
			
		||||
  <rb-alert alertTitle="Are you sure?" type="danger" [okBtnLabel]="'Delete sample' + (samples.length > 1 ? 's' : '')"
 | 
			
		||||
            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>
 | 
			
		||||
</ng-template>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -613,6 +613,10 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
			
		||||
    return this.sampleReferenceList.bind(this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  sampleNames() {
 | 
			
		||||
    return this.samples.map(e => e.number).join(', ');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uniqueCfValues(index) {  // returns all names until index for unique check
 | 
			
		||||
    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 {UserModel} from '../models/user.model';
 | 
			
		||||
import {ModelItemModel} from '../models/model-item.model';
 | 
			
		||||
import {ModelFileModel} from '../models/model-file.model';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root'
 | 
			
		||||
@@ -25,6 +26,7 @@ export class DataService {
 | 
			
		||||
    sampleNotesFields: {path: '/sample/notes/fields', model: TemplateModel, type: 'idArray'},
 | 
			
		||||
    users: {path: '/users', model: UserModel, type: 'idArray'},
 | 
			
		||||
    modelGroups: {path: '/model/groups', model: ModelItemModel, type: 'array'},
 | 
			
		||||
    modelFiles: {path: '/model/files', model: ModelFileModel, type: 'array'},
 | 
			
		||||
    user: {path: '/user', model: UserModel, 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`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user