+
   
    
 
+
+
+  
# Disclaimer: This tool is still under development
+  
+    The prediction and classification of material parameters are validated only for certain conditions.
+    These results may therefore under no circumstances be used to evaluate quality-relevant issues. {{d.contact.name}} .
+  
+
diff --git a/src/app/prediction/prediction.component.scss b/src/app/prediction/prediction.component.scss
index 18b1f5b..72ec1e2 100644
--- a/src/app/prediction/prediction.component.scss
+++ b/src/app/prediction/prediction.component.scss
@@ -1,6 +1,7 @@
 .dpt-chart {
   max-width: 800px;
-  margin: 0 auto;
+  margin-left: auto;
+  margin-right: auto;
 }
 
 .file-input {
diff --git a/src/app/prediction/prediction.component.ts b/src/app/prediction/prediction.component.ts
index 0d4c3df..075c150 100644
--- a/src/app/prediction/prediction.component.ts
+++ b/src/app/prediction/prediction.component.ts
@@ -5,6 +5,7 @@ import {animate, style, transition, trigger} from '@angular/animations';
 import cloneDeep from 'lodash/cloneDeep';
 import {DataService} from '../services/data.service';
 import {ModelItemModel} from '../models/model-item.model';
+import {HttpClient} from '@angular/common/http';
 
 interface PredictionResult {
   meanPrediction: string;
@@ -86,7 +87,8 @@ export class PredictionComponent implements OnInit {
       if (files.hasOwnProperty(i)) {
         const fileReader = new FileReader();
         fileReader.onload = () => {
-          this.spectrum = fileReader.result.toString().split('\r\n').map(e => e.split(',').map(el => parseFloat(el))) as any;
+          this.spectrum = fileReader.result.toString().split('\r\n').map(e => e.split(',').map(el => parseFloat(el)))
+            .filter(el => el.length === 2) as any;
           this.flattenedSpectra[i] = {labels: this.spectrum.map(e => e[0]), values: this.spectrum.map(e => e[1])};
           this.chart[i] = cloneDeep(this.chartInit);
           this.chart[i].data = this.spectrum.map(e => ({x: parseFloat(e[0]), y: parseFloat(e[1])}));
diff --git a/src/app/sample/sample.component.html b/src/app/sample/sample.component.html
index cc94873..ad1f03c 100644
--- a/src/app/sample/sample.component.html
+++ b/src/app/sample/sample.component.html
@@ -169,7 +169,7 @@
       
Type {{baseSample.type}} color {{baseSample.color}} Batch {{baseSample.batch}} 
-      
Comment {{baseSample.notes.comment}} 
+      
Comment {{baseSample.notes | exists:'comment'}} 
         Sample reference {{reference[0]}} - {{reference[1]}} 
        
@@ -323,7 +323,8 @@
 
 
 
-   1 ? 's' : '')" cancelBtnLabel="Cancel">
+   1 ? 's' : '')"
+            cancelBtnLabel="Cancel">
     Do you really want to delete {{samples.length > 1 ? 'these samples' : 'this sample'}}?
    
   
diff --git a/src/app/sample/sample.component.ts b/src/app/sample/sample.component.ts
index cdcfa03..dadcfc1 100644
--- a/src/app/sample/sample.component.ts
+++ b/src/app/sample/sample.component.ts
@@ -329,15 +329,17 @@ export class SampleComponent implements OnInit, AfterContentChecked {
         resolve();
       }
     }).then(() => {  // save sample
-      this.baseSample.notes.custom_fields = {};
-      this.customFields.forEach(element => {
-        if (element[0] !== '') {
-          this.baseSample.notes.custom_fields[element[0]] = element[1];
-        }
-      });
-      this.baseSample.notes.sample_references = this.sampleReferences
-        .filter(e => e[0] && e[1] && e[2])
-        .map(e => ({sample_id: e[2], relation: e[1]}));
+      if (this.baseSample.notes) {
+        this.baseSample.notes.custom_fields = {};
+        this.customFields.forEach(element => {
+          if (element[0] !== '') {
+            this.baseSample.notes.custom_fields[element[0]] = element[1];
+          }
+        });
+        this.baseSample.notes.sample_references = this.sampleReferences
+          .filter(e => e[0] && e[1] && e[2])
+          .map(e => ({sample_id: e[2], relation: e[1]}));
+      }
       if (this.samples.length === 0) {  // only save new sample for the first time in mode new, otherwise save changes
         for (let i = 0; i < this.sampleCount; i ++) {
           this.api.post
('/sample/new', this.baseSample.sendFormat(), data => {
diff --git a/src/app/services/data.service.ts b/src/app/services/data.service.ts
index 4edba0f..3e76e0a 100644
--- a/src/app/services/data.service.ts
+++ b/src/app/services/data.service.ts
@@ -34,7 +34,7 @@ export class DataService {
   id: {[key: string]: {[id: string]: any}} = {};  // data in format _id: data
   d: {[key: string]: any} = {};      // data not in array format
 
-  contact = 'dominic.lingenfelser@bosch.com';
+  contact = {name: 'CR/APS1-Lingenfelser', mail: 'dominic.lingenfelser@bosch.com'};
 
   load(collection, f = () => {}) {  // load data
     if (this.arr[collection]) { // data already loaded
diff --git a/src/app/services/login.service.ts b/src/app/services/login.service.ts
index e8dd0db..ace657b 100644
--- a/src/app/services/login.service.ts
+++ b/src/app/services/login.service.ts
@@ -3,6 +3,7 @@ import {ApiService} from './api.service';
 import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
 import {LocalStorageService} from 'angular-2-local-storage';
 import {Observable} from 'rxjs';
+import {DataService} from './data.service';
 
 @Injectable({
   providedIn: 'root'
@@ -15,12 +16,14 @@ export class LoginService implements CanActivate {
     {path: 'users', permission: 'admin'}
   ];
   readonly levels = [
+    'predict',
     'read',
     'write',
     'dev',
     'admin'
   ];
   isLevel: {[level: string]: boolean} = {};
+  hasPrediction = false;  // true if user has prediction models specified
   userId = '';
 
   private loggedIn;
@@ -28,7 +31,8 @@ export class LoginService implements CanActivate {
   constructor(
     private api: ApiService,
     private storage: LocalStorageService,
-    private router: Router
+    private router: Router,
+    private d: DataService
   ) {
 
   }
@@ -60,6 +64,14 @@ export class LoginService implements CanActivate {
             this.loggedIn = true;
             this.levels.forEach(level => {
               this.isLevel[level] = this.levels.indexOf(data.level) >= this.levels.indexOf(level);
+              if (this.isLevel.dev) {  // set hasPrediction
+                this.hasPrediction = true;
+              }
+              else {
+                this.d.load('modelGroups', () => {
+                  this.hasPrediction = this.d.arr.modelGroups.length > 0;
+                });
+              }
             });
             this.userId = data.user_id;
             resolve(true);
@@ -83,6 +95,7 @@ export class LoginService implements CanActivate {
     this.levels.forEach(level => {
       this.isLevel[level] = false;
     });
+    this.hasPrediction = false;
   }
 
   canActivate(route: ActivatedRouteSnapshot = null, state: RouterStateSnapshot = null): Observable {
diff --git a/src/app/users/users.component.html b/src/app/users/users.component.html
index c4d8b83..2b7a457 100644
--- a/src/app/users/users.component.html
+++ b/src/app/users/users.component.html
@@ -28,6 +28,12 @@
       {{deviceInput.errors.failure}} 
     
   
+  
+    
+       
+   
   
     {{passAInput.errors.failure}} 
@@ -41,13 +47,15 @@
   
 
 
-
+
   
     Name 
     Email 
     Level 
     Location 
     Device 
+    Models 
+     
 
@@ -58,7 +66,13 @@
       {{user.level}} 
       {{user.location}} 
       {{user.devices}} 
-      
+        
+        {{(i > 0 ? ', ' : '') + modelIds[model]}}
+         
+       
+      
       
@@ -93,9 +107,17 @@
           
         
        
+      
+        
+          
+             
+         
+       
       
         
+                        (click)="deleteConfirm(modalDeleteConfirm, user)">
           Delete
          
         
@@ -111,3 +133,10 @@
      
   
 
+
+
+  
+    {{model.name}} 
+   
+ 
diff --git a/src/app/users/users.component.ts b/src/app/users/users.component.ts
index e7857be..27c587f 100644
--- a/src/app/users/users.component.ts
+++ b/src/app/users/users.component.ts
@@ -3,8 +3,8 @@ import {ApiService} from '../services/api.service';
 import {UserModel} from '../models/user.model';
 import {LoginService} from '../services/login.service';
 import {ModalService} from '@inst-iot/bosch-angular-ui-components';
+import {DataService} from '../services/data.service';
 
-// TODO: somehow mail change triggered
 
 @Component({
   selector: 'app-users',
@@ -16,17 +16,26 @@ export class UsersComponent implements OnInit {
   users: UserModel[] = [];
   newUser: UserModel | null = null;
   newUserPass = '';
+  modelSelect: {id: string, name: string}[] = [];
+  modelIds: {[id: string]: string} = {};
 
   constructor(
     private api: ApiService,
     public login: LoginService,
-    private modal: ModalService
+    private modal: ModalService,
+    public d: DataService
   ) { }
 
   ngOnInit(): void {
     this.api.get('/users', data => {
       this.users = data.map(e => new UserModel().deserialize(e));
     });
+    this.d.load('modelGroups', () => {
+      this.d.arr.modelGroups.forEach(group => {
+        this.modelSelect.push(...group.models.map(e => ({id: e._id, name: `${group.group} - ${e.name}`})));
+      });
+      this.modelIds = this.modelSelect.reduce((s, e) => {s[e.id] = e.name; return s; }, {});
+    });
   }
 
   saveUser(user: UserModel) {
@@ -48,14 +57,20 @@ export class UsersComponent implements OnInit {
     this.newUser = this.newUser ? null : new UserModel();
   }
 
-  deleteConfirm(modal, username) {
+  deleteConfirm(modal, user) {
     this.modal.open(modal).then(result => {
       if (result) {
-        this.api.delete('/user/' + username, () => {
-          this.users.splice(this.users.findIndex(e => e.name === username), 1);
+        this.api.delete('/user/' + user.name, () => {
+          user.status = 'deleted';
+          user.edit = false;
         });
       }
     });
   }
 
+  restoreUser(user) {
+    this.api.put('/user/restore/' + user.name, {}, () => {
+      user.status = 'new';
+    });
+  }
 }