diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index bdd4e17..5bcbf68 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -13,6 +13,8 @@ import {DocumentationDatabaseComponent} from './documentation/documentation-data
import {PredictionComponent} from './prediction/prediction.component';
import {ModelTemplatesComponent} from './model-templates/model-templates.component';
import {DocumentationArchitectureComponent} from './documentation/documentation-architecture/documentation-architecture.component';
+import {MaterialsComponent} from './materials/materials.component';
+import {MaterialComponent} from './material/material.component';
const routes: Routes = [
@@ -23,6 +25,8 @@ const routes: Routes = [
{path: 'samples', component: SamplesComponent, canActivate: [LoginService]},
{path: 'samples/new', component: SampleComponent, canActivate: [LoginService]},
{path: 'samples/edit/:id', component: SampleComponent, canActivate: [LoginService]},
+ {path: 'materials', component: MaterialsComponent, canActivate: [LoginService]},
+ {path: 'materials/edit/:id', component: MaterialComponent, canActivate: [LoginService]},
{path: 'templates', component: TemplatesComponent, canActivate: [LoginService]},
{path: 'changelog', component: ChangelogComponent, canActivate: [LoginService]},
{path: 'users', component: UsersComponent, canActivate: [LoginService]},
diff --git a/src/app/app.component.html b/src/app/app.component.html
index fd3479b..75d8741 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -4,6 +4,7 @@
Prediction
Models
Samples
+ Materials
Templates
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index c4b92f8..a2441d2 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -32,6 +32,8 @@ import { HelpComponent } from './help/help.component';
import { ModelTemplatesComponent } from './model-templates/model-templates.component';
import { SizePipe } from './size.pipe';
import { DocumentationArchitectureComponent } from './documentation/documentation-architecture/documentation-architecture.component';
+import { MaterialsComponent } from './materials/materials.component';
+import { MaterialComponent } from './material/material.component';
@NgModule({
declarations: [
@@ -56,7 +58,9 @@ import { DocumentationArchitectureComponent } from './documentation/documentatio
HelpComponent,
ModelTemplatesComponent,
SizePipe,
- DocumentationArchitectureComponent
+ DocumentationArchitectureComponent,
+ MaterialsComponent,
+ MaterialComponent
],
imports: [
LocalStorageModule.forRoot({
diff --git a/src/app/material/material.component.html b/src/app/material/material.component.html
new file mode 100644
index 0000000..9c195ff
--- /dev/null
+++ b/src/app/material/material.component.html
@@ -0,0 +1,65 @@
+
Edit material
+
+
+
+
+
+
+ Do you really want to delete {{material.name}}?
+
+
+
diff --git a/src/app/material/material.component.scss b/src/app/material/material.component.scss
new file mode 100644
index 0000000..314fe09
--- /dev/null
+++ b/src/app/material/material.component.scss
@@ -0,0 +1,3 @@
+.delete-material {
+ float: right;
+}
diff --git a/src/app/material/material.component.spec.ts b/src/app/material/material.component.spec.ts
new file mode 100644
index 0000000..7293b78
--- /dev/null
+++ b/src/app/material/material.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MaterialComponent } from './material.component';
+
+describe('MaterialComponent', () => {
+ let component: MaterialComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ MaterialComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MaterialComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/material/material.component.ts b/src/app/material/material.component.ts
new file mode 100644
index 0000000..586ff28
--- /dev/null
+++ b/src/app/material/material.component.ts
@@ -0,0 +1,137 @@
+import {AfterContentChecked, Component, OnInit, TemplateRef, ViewChild} from '@angular/core';
+import {MaterialModel} from '../models/material.model';
+import {ApiService} from '../services/api.service';
+import {ActivatedRoute, Router} from '@angular/router';
+import {DataService} from '../services/data.service';
+import strCompare from 'str-compare';
+import {ModalService} from '@inst-iot/bosch-angular-ui-components';
+import {AutocompleteService} from '../services/autocomplete.service';
+import {NgForm, Validators} from '@angular/forms';
+import {ValidationService} from '../services/validation.service';
+import {ErrorComponent} from '../error/error.component';
+
+@Component({
+ selector: 'app-material',
+ templateUrl: './material.component.html',
+ styleUrls: ['./material.component.scss']
+})
+export class MaterialComponent implements OnInit, AfterContentChecked {
+
+ @ViewChild('materialForm') materialForm: NgForm;
+
+ material: MaterialModel;
+ materialNames: string[] = [];
+
+ modalText = {list: '', suggestion: ''};
+ loading = 0;
+ checkFormAfterInit = true;
+
+ constructor(
+ private api: ApiService,
+ private route: ActivatedRoute,
+ public d: DataService,
+ private modal: ModalService,
+ public autocomplete: AutocompleteService,
+ private router: Router,
+ private validation: ValidationService
+ ) { }
+
+ ngOnInit(): void {
+ this.loading = 5;
+ this.api.get('/material/' + this.route.snapshot.paramMap.get('id'), data => {
+ this.material = new MaterialModel().deserialize(data);
+ this.loading--;
+ this.d.load('materials', () => {
+ this.materialNames = this.d.arr.materials.map(e => e.name).filter(e => e !== this.material.name);
+ this.loading--;
+ });
+ });
+ this.d.load('materialSuppliers', () => {
+ this.loading--;
+ });
+ this.d.load('materialGroups', () => {
+ this.loading--;
+ });
+ this.d.load('materialTemplates', () => {
+ this.loading--;
+ });
+ }
+
+ ngAfterContentChecked() {
+ if (this.materialForm && this.material.properties.material_template) { // material template is set
+ this.d.id.materialTemplates[this.material.properties.material_template].parameters.forEach((parameter, i) => {
+ this.attachValidator(this.materialForm, 'materialParameter' + i, parameter.range);
+ });
+ }
+
+ if (this.checkFormAfterInit && this.materialForm !== undefined && this.materialForm.form.get('propertiesSelect')) {
+ this.checkFormAfterInit = false;
+ Object.keys(this.materialForm.form.controls).forEach(field => {
+ this.materialForm.form.get(field).updateValueAndValidity();
+ });
+ }
+ }
+
+ // attach validators specified in range to input with name
+ attachValidator(form, name: string, range: {[prop: string]: any}) {
+ if (form && form.form.get(name)) {
+ const validators = [];
+ if (range.hasOwnProperty('required')) {
+ validators.push(Validators.required);
+ }
+ if (range.hasOwnProperty('values')) {
+ validators.push(this.validation.generate('stringOf', [range.values]));
+ }
+ else if (range.hasOwnProperty('min') && range.hasOwnProperty('max')) {
+ validators.push(this.validation.generate('minMax', [range.min, range.max]));
+ }
+ else if (range.hasOwnProperty('min')) {
+ validators.push(this.validation.generate('min', [range.min]));
+ }
+ else if (range.hasOwnProperty('max')) {
+ validators.push(this.validation.generate('max', [range.max]));
+ }
+ form.form.get(name).setValidators(validators);
+ }
+ }
+
+ materialSave() {
+ this.api.put('/material/' + this.material._id, this.material.sendFormat(), () => {
+ this.router.navigate(['/materials']);
+ });
+ }
+
+ deleteConfirm(modal) {
+ this.modal.open(modal).then(result => {
+ if (result) {
+ this.api.delete('/material/' + this.material._id, (ignore, error) => {
+ if (error) {
+ const modalRef = this.modal.openComponent(ErrorComponent);
+ modalRef.instance.message = 'Cannot delete material as it is still in use!';
+ }
+ else {
+ this.router.navigate(['/materials']);
+ }
+ });
+ }
+ });
+ }
+
+ checkTypo(event, list, mKey, modal: TemplateRef) {
+ // user did not click on suggestion and entry is not in list
+ if (!(event.relatedTarget && (event.relatedTarget.className.indexOf('rb-dropdown-item') >= 0 ||
+ event.relatedTarget.className.indexOf('close-btn rb-btn rb-passive-link') >= 0)) &&
+ this.d.arr[list].indexOf(this.material[mKey]) < 0) {
+ this.modalText.list = mKey;
+ this.modalText.suggestion = this.d.arr[list] // find possible entry from list
+ .map(e => ({v: e, s: strCompare.sorensenDice(e, this.material[mKey])}))
+ .sort((a, b) => b.s - a.s)[0].v;
+ this.modal.open(modal).then(result => {
+ if (result) { // use suggestion
+ this.material[mKey] = this.modalText.suggestion;
+ }
+ });
+ }
+ }
+
+}
diff --git a/src/app/materials/materials.component.html b/src/app/materials/materials.component.html
new file mode 100644
index 0000000..d4ee65e
--- /dev/null
+++ b/src/app/materials/materials.component.html
@@ -0,0 +1,86 @@
+
+
+
+
+
+ validated
+
+
+ new
+
+
+ deleted
+
+
+
+
+
+
+
+
+ all
+ |
+ Name |
+ Supplier |
+ Group |
+ {{key.label}} |
+ Numbers |
+ |
+
+
+
+
+
+ |
+ {{material.name}} |
+ {{material.supplier}} |
+ {{material.group}} |
+ {{material.properties[key.key] | exists}} |
+ {{material.numbers}} |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+ of {{pages}}
+
+
+
+
+
+
+
+ Do you really want to restore this sample?
+
+
diff --git a/src/app/materials/materials.component.scss b/src/app/materials/materials.component.scss
new file mode 100644
index 0000000..021af3b
--- /dev/null
+++ b/src/app/materials/materials.component.scss
@@ -0,0 +1,53 @@
+.paging {
+ height: 50px;
+ float: left;
+
+ rb-form-input {
+ max-width: 65px;
+ }
+
+ > * {
+ float: left;
+ }
+
+ > button {
+ margin-top: 18px;
+ }
+
+ > span {
+ margin-top: 20px;
+ margin-left: 5px;
+ }
+}
+
+.status-selection {
+ overflow: hidden;
+ margin-bottom: 10px;
+ float: left;
+ margin-right: 15px;
+
+ label {
+ display: block;
+ font-weight: 700;
+ font-size: 10px;
+ }
+
+ rb-form-checkbox {
+ float: left;
+ margin-right: 10px;
+ margin-top: -10px;
+ }
+}
+
+.header-addnew {
+ margin-bottom: 40px;
+
+ & > * {
+ display: inline;
+ margin-bottom: 10px;
+ }
+
+ rb-icon-button {
+ float: right;
+ }
+}
diff --git a/src/app/materials/materials.component.spec.ts b/src/app/materials/materials.component.spec.ts
new file mode 100644
index 0000000..d89957d
--- /dev/null
+++ b/src/app/materials/materials.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MaterialsComponent } from './materials.component';
+
+describe('MaterialsComponent', () => {
+ let component: MaterialsComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ MaterialsComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MaterialsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/materials/materials.component.ts b/src/app/materials/materials.component.ts
new file mode 100644
index 0000000..c6e692b
--- /dev/null
+++ b/src/app/materials/materials.component.ts
@@ -0,0 +1,92 @@
+import { Component, OnInit } from '@angular/core';
+import {DataService} from '../services/data.service';
+import {MaterialModel} from '../models/material.model';
+import {ApiService} from '../services/api.service';
+import {ModalService} from '@inst-iot/bosch-angular-ui-components';
+
+
+@Component({
+ selector: 'app-materials',
+ templateUrl: './materials.component.html',
+ styleUrls: ['./materials.component.scss']
+})
+export class MaterialsComponent implements OnInit {
+
+ materials: MaterialModel[] = [];
+ templateKeys: {key: string, label: string}[] = [];
+ materialStatus = {validated: true, new: true, deleted: false};
+ sampleSelect = false;
+
+ page = 1;
+ pages = 0;
+ pageSize = 25;
+
+ constructor(
+ private api: ApiService,
+ public d: DataService,
+ private modal: ModalService
+ ) { }
+
+ ngOnInit(): void {
+ this.loadMaterials();
+ this.d.load('materialTemplates', () => {
+ this.d.arr.materialTemplates.forEach(template => {
+ template.parameters.forEach(parameter => {
+ this.templateKeys.push({key: parameter.name, label: `${this.ucFirst(template.name)} ${parameter.name}`});
+ });
+ });
+ this.templateKeys = this.templateKeys.filter((e, i, a) => !a.slice(0, i).find(el => el.key === e.key));
+ console.log(this.templateKeys);
+ });
+ }
+
+ loadMaterials() {
+ this.api.get('/materials?' +
+ Object.entries(this.materialStatus).filter(e => e[1]).map(e => 'status[]=' + e[0]).join('&'), data => {
+ this.materials = data.map(e => new MaterialModel().deserialize(e));
+ this.pages = Math.ceil(this.materials.length / this.pageSize);
+ this.page = 1;
+ });
+ }
+
+ validate() {
+ if (this.sampleSelect) {
+ this.materials.forEach(sample => {
+ if (sample.selected) {
+ this.api.put('/material/validate/' + sample._id);
+ }
+ });
+ this.loadMaterials();
+ this.sampleSelect = false;
+ }
+ else {
+ this.sampleSelect = true;
+ }
+ }
+
+ selectAll(event) {
+ this.materials.forEach(material => {
+ if (material.status !== 'deleted') {
+ material.selected = event.target.checked;
+ }
+ else {
+ material.selected = false;
+ }
+ });
+ }
+
+ restoreMaterial(id, modal) {
+ this.modal.open(modal).then(res => {
+ if (res) {
+ this.api.put('/sample/restore/' + id, {}, ignore => {
+ this.materials.find(e => e._id === id).status = 'new';
+ });
+ }
+ });
+ }
+
+ ucFirst(string) {
+ return string[0].toUpperCase() + string.slice(1);
+ }
+
+}
diff --git a/src/app/model-templates/model-templates.component.ts b/src/app/model-templates/model-templates.component.ts
index 256fd72..17e2fdb 100644
--- a/src/app/model-templates/model-templates.component.ts
+++ b/src/app/model-templates/model-templates.component.ts
@@ -4,6 +4,7 @@ import {ModelItemModel} from '../models/model-item.model';
import {ApiService} from '../services/api.service';
import {AutocompleteService} from '../services/autocomplete.service';
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
+import omit from 'lodash/omit';
@Component({
selector: 'app-model-templates',
@@ -44,7 +45,7 @@ export class ModelTemplatesComponent implements OnInit {
if (this.oldModelGroup !== '' && this.modelGroup !== this.oldModelGroup) { // group was changed, delete model in old group
this.delete(null, this.oldModelGroup, this.oldModelName);
}
- this.api.post('/model/' + this.modelGroup, this.model, () => {
+ this.api.post('/model/' + this.modelGroup, omit(this.model, '_id'), () => {
this.newModel = false;
this.loadGroups();
this.modelGroup = '';
diff --git a/src/app/models/material.model.ts b/src/app/models/material.model.ts
index d69cc7f..38c6479 100644
--- a/src/app/models/material.model.ts
+++ b/src/app/models/material.model.ts
@@ -9,6 +9,8 @@ export class MaterialModel extends BaseModel {
group = '';
properties: {material_template: string, [prop: string]: string} = {material_template: null};
numbers: string[] = [''];
+ selected = false;
+ status = '';
sendFormat() {
return pick(this, ['name', 'supplier', 'group', 'numbers', 'properties']);
diff --git a/src/app/prediction/prediction.component.html b/src/app/prediction/prediction.component.html
index 46a4359..b8bd37b 100644
--- a/src/app/prediction/prediction.component.html
+++ b/src/app/prediction/prediction.component.html
@@ -20,7 +20,7 @@
Average result: {{result.mean}}#
- Details
+ Details
{{spectrumNames[i]}}: {{prediction}}#
diff --git a/src/app/prediction/prediction.component.ts b/src/app/prediction/prediction.component.ts
index d6cb0c2..755b039 100644
--- a/src/app/prediction/prediction.component.ts
+++ b/src/app/prediction/prediction.component.ts
@@ -99,6 +99,8 @@ export class PredictionComponent implements OnInit {
loadPrediction() {
this.loading = true;
+ console.log(this.activeGroup);
+ console.log(this.activeModelIndex);
this.api.post(this.activeGroup.models[this.activeModelIndex].url, this.flattenedSpectra, data => {
this.result = {
predictions: Object.entries(omit(data, ['mean', 'std', 'label']))
@@ -114,7 +116,9 @@ export class PredictionComponent implements OnInit {
}
groupChange(index) {
+ console.log(index);
this.activeGroup = this.d.arr.modelGroups[index];
+ this.activeModelIndex = 0;
this.result = undefined;
}
diff --git a/src/app/samples/samples.component.html b/src/app/samples/samples.component.html
index 52d0898..3dc102c 100644
--- a/src/app/samples/samples.component.html
+++ b/src/app/samples/samples.component.html
@@ -1,4 +1,3 @@
-