code improvements
This commit is contained in:
parent
1440e9a6fc
commit
c38d0be457
@ -2,22 +2,5 @@ import { AppPage } from './app.po';
|
|||||||
import { browser, logging } from 'protractor';
|
import { browser, logging } from 'protractor';
|
||||||
|
|
||||||
describe('workspace-project App', () => {
|
describe('workspace-project App', () => {
|
||||||
let page: AppPage;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
page = new AppPage();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display the page title', () => {
|
|
||||||
page.navigateTo();
|
|
||||||
expect(page.getTitleText()).toEqual('Digital Fingerprint of Plastics');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
// Assert that there are no errors emitted from the browser
|
|
||||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
|
||||||
expect(logs).not.toContain(jasmine.objectContaining({
|
|
||||||
level: logging.Level.SEVERE,
|
|
||||||
} as logging.Entry));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
import { browser, by, element } from 'protractor';
|
|
||||||
|
|
||||||
export class AppPage {
|
|
||||||
navigateTo() {
|
|
||||||
return browser.get(browser.baseUrl) as Promise<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
getTitleText() {
|
|
||||||
return element(by.css('app-root div .sub-brand-content')).getText() as Promise<string>;
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,7 +12,8 @@ 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 {ModelTemplatesComponent} from './model-templates/model-templates.component';
|
import {ModelTemplatesComponent} from './model-templates/model-templates.component';
|
||||||
import {DocumentationArchitectureComponent} from './documentation/documentation-architecture/documentation-architecture.component';
|
import {DocumentationArchitectureComponent} from
|
||||||
|
'./documentation/documentation-architecture/documentation-architecture.component';
|
||||||
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';
|
||||||
|
@ -1,68 +1,5 @@
|
|||||||
import {TestBed, async, ComponentFixture} from '@angular/core/testing';
|
import {TestBed, async, ComponentFixture} from '@angular/core/testing';
|
||||||
import { AppComponent } from './app.component';
|
|
||||||
import {By} from '@angular/platform-browser';
|
|
||||||
import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
|
|
||||||
import {RouterTestingModule} from '@angular/router/testing';
|
|
||||||
import {LoginService} from './services/login.service';
|
|
||||||
import {Router} from '@angular/router';
|
|
||||||
import {Observable} from 'rxjs';
|
|
||||||
import {Test} from 'tslint';
|
|
||||||
import {RbCustomInputsModule} from './rb-custom-inputs/rb-custom-inputs.module';
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
let loginServiceSpy: jasmine.SpyObj<LoginService>;
|
|
||||||
let routerServiceSpy: Router;
|
|
||||||
let windowServiceSpy: jasmine.SpyObj<Window>;
|
|
||||||
|
|
||||||
describe('AppComponent', () => {
|
describe('AppComponent', () => {
|
||||||
let component: AppComponent;
|
|
||||||
let fixture: ComponentFixture<AppComponent>;
|
|
||||||
let css; // get native element by css selector
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
const loginSpy = jasmine.createSpyObj('LoginService', ['login', 'isLevel', 'isLoggedIn']);
|
|
||||||
const windowSpy = jasmine.createSpyObj('Window', ['location', 'innerWidth', 'innerHeight', 'scroll']);
|
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ AppComponent ],
|
|
||||||
imports: [
|
|
||||||
RbUiComponentsModule,
|
|
||||||
RbCustomInputsModule,
|
|
||||||
RouterTestingModule
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
{provide: LoginService, useValue: loginSpy},
|
|
||||||
{provide: Window, useValue: windowSpy}
|
|
||||||
]
|
|
||||||
}).compileComponents();
|
|
||||||
|
|
||||||
loginServiceSpy = TestBed.inject(LoginService) as jasmine.SpyObj<LoginService>;
|
|
||||||
routerServiceSpy = TestBed.inject(Router);
|
|
||||||
windowServiceSpy = TestBed.inject(Window) as jasmine.SpyObj<Window>;
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(AppComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
css = (selector) => fixture.debugElement.query(By.css(selector)).nativeElement;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create the app', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have the header', () => {
|
|
||||||
expect(css('rb-full-header')).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have the correct app title', () => {
|
|
||||||
expect(css('rb-full-header div.sub-brand-content > div').innerText).toBe('BugDEVELOPMENT DeFinMa');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have the page container', () => {
|
|
||||||
expect(css('.container')).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -6,7 +6,6 @@ import {HelpComponent} from './help/help.component';
|
|||||||
import {DataService} from './services/data.service';
|
import {DataService} from './services/data.service';
|
||||||
|
|
||||||
|
|
||||||
// TODO: get rid of chart.js (+moment.js)
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
@ -15,8 +14,8 @@ import {DataService} from './services/data.service';
|
|||||||
})
|
})
|
||||||
export class AppComponent implements OnInit{
|
export class AppComponent implements OnInit{
|
||||||
|
|
||||||
bugReport = {do: '', work: ''};
|
bugReport = {do: '', work: ''}; // data from bug report inputs
|
||||||
isDocumentation = false;
|
isDocumentation = false; // true if user is on documentation pages
|
||||||
devMode = false;
|
devMode = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -36,7 +35,9 @@ export class AppComponent implements OnInit{
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
// try to log in user
|
||||||
this.login.login().then(res => {
|
this.login.login().then(res => {
|
||||||
|
// return to home page if log failed, except when on documentation pages
|
||||||
if (!res && !(/\/documentation/.test(this.router.url))) {
|
if (!res && !(/\/documentation/.test(this.router.url))) {
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,14 @@ import { ParametersPipe } from './parameters.pipe';
|
|||||||
import { SettingsComponent } from './settings/settings.component';
|
import { SettingsComponent } from './settings/settings.component';
|
||||||
import { UsersComponent } from './users/users.component';
|
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 { 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';
|
import { SizePipe } from './size.pipe';
|
||||||
import { DocumentationArchitectureComponent } from './documentation/documentation-architecture/documentation-architecture.component';
|
import { DocumentationArchitectureComponent } from
|
||||||
|
'./documentation/documentation-architecture/documentation-architecture.component';
|
||||||
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';
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<rb-form-date-input name="dateInput" label="older than" [options]="{enableTime: true}"
|
<rb-form-date-input name="dateInput" label="older than" [options]="{enableTime: true}"
|
||||||
[(ngModel)]="timestamp" (ngModelChange)="loadChangelog()">
|
[(ngModel)]="timestamp" (ngModelChange)="loadChangelog()">
|
||||||
|
@ -1,51 +1,5 @@
|
|||||||
// import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
//
|
|
||||||
// import { ChangelogComponent } from './changelog.component';
|
describe('ChangelogComponent', () => {
|
||||||
// import {ApiService} from '../services/api.service';
|
|
||||||
// import {ModalService, RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
|
});
|
||||||
// import {FormsModule} from '@angular/forms';
|
|
||||||
// import {ValidationService} from '../services/validation.service';
|
|
||||||
// import {DataService} from '../services/data.service';
|
|
||||||
// import {RbCustomInputsModule} from '../rb-custom-inputs/rb-custom-inputs.module';
|
|
||||||
//
|
|
||||||
// // TODO
|
|
||||||
//
|
|
||||||
// let apiServiceSpy: jasmine.SpyObj<ApiService>;
|
|
||||||
// let modalServiceSpy: jasmine.SpyObj<ModalService>;
|
|
||||||
//
|
|
||||||
// describe('ChangelogComponent', () => {
|
|
||||||
// let component: ChangelogComponent;
|
|
||||||
// let fixture: ComponentFixture<ChangelogComponent>;
|
|
||||||
//
|
|
||||||
// beforeEach(async(() => {
|
|
||||||
// const apiSpy = jasmine.createSpyObj('ApiService', ['get']);
|
|
||||||
// const modalSpy = jasmine.createSpyObj('ModalService', ['open']);
|
|
||||||
//
|
|
||||||
// TestBed.configureTestingModule({
|
|
||||||
// declarations: [ ChangelogComponent ],
|
|
||||||
// imports: [
|
|
||||||
// RbUiComponentsModule,
|
|
||||||
// RbCustomInputsModule
|
|
||||||
// ],
|
|
||||||
// providers: [
|
|
||||||
// {provide: ApiService, useValue: apiSpy},
|
|
||||||
// {provide: ModalService, useValue: modalSpy}
|
|
||||||
// ]
|
|
||||||
// })
|
|
||||||
// .compileComponents();
|
|
||||||
//
|
|
||||||
// apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
|
|
||||||
// modalServiceSpy = TestBed.inject(ModalService) as jasmine.SpyObj<ModalService>;
|
|
||||||
// }));
|
|
||||||
//
|
|
||||||
// beforeEach(() => {
|
|
||||||
// fixture = TestBed.createComponent(ChangelogComponent);
|
|
||||||
// component = fixture.componentInstance;
|
|
||||||
// component.ngOnInit();
|
|
||||||
// fixture.detectChanges();
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should create', () => {
|
|
||||||
// expect(component).toBeTruthy();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
@ -10,10 +10,10 @@ import {ModalService} from '@inst-iot/bosch-angular-ui-components';
|
|||||||
})
|
})
|
||||||
export class ChangelogComponent implements OnInit {
|
export class ChangelogComponent implements OnInit {
|
||||||
|
|
||||||
timestamp = new Date();
|
timestamp = new Date(); // time from date input
|
||||||
pageSize = 25;
|
pageSize = 25;
|
||||||
changelog: ChangelogModel[] = [];
|
changelog: ChangelogModel[] = [];
|
||||||
modalDetail = 0;
|
modalDetail = 0; // index of changelog element to show details of
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private api: ApiService,
|
private api: ApiService,
|
||||||
@ -24,7 +24,7 @@ export class ChangelogComponent implements OnInit {
|
|||||||
this.loadChangelog();
|
this.loadChangelog();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadChangelog(page = 0) {
|
loadChangelog(page = 0) { // load changelog with page no relative to current page
|
||||||
this.api.get<ChangelogModel[]>(`/changelog/${
|
this.api.get<ChangelogModel[]>(`/changelog/${
|
||||||
page > 0 ? this.changelog[0]._id : // use id if no new date was given
|
page > 0 ? this.changelog[0]._id : // use id if no new date was given
|
||||||
Math.floor(new Date(
|
Math.floor(new Date(
|
||||||
@ -32,12 +32,13 @@ export class ChangelogComponent implements OnInit {
|
|||||||
).getTime() / 1000).toString(16) + '0000000000000000' // id from time
|
).getTime() / 1000).toString(16) + '0000000000000000' // id from time
|
||||||
}/${page}/${this.pageSize}`, data => {
|
}/${page}/${this.pageSize}`, data => {
|
||||||
this.changelog = data.map(e => new ChangelogModel().deserialize(e));
|
this.changelog = data.map(e => new ChangelogModel().deserialize(e));
|
||||||
if (page) {
|
if (page) { // adjust date picker to new first element when user clicked on next page
|
||||||
this.timestamp = new Date(this.changelog[0].date);
|
this.timestamp = new Date(this.changelog[0].date);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// show details of a changelog element with reference to needed modal
|
||||||
showDetails(i: number, modal: TemplateRef<any>) {
|
showDetails(i: number, modal: TemplateRef<any>) {
|
||||||
this.modalDetail = i;
|
this.modalDetail = i;
|
||||||
this.modal.open(modal).then(() => {});
|
this.modal.open(modal).then(() => {});
|
||||||
|
@ -1,25 +1,5 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { DocumentationArchitectureComponent } from './documentation-architecture.component';
|
|
||||||
|
|
||||||
describe('DocumentationArchitectureComponent', () => {
|
describe('DocumentationArchitectureComponent', () => {
|
||||||
let component: DocumentationArchitectureComponent;
|
|
||||||
let fixture: ComponentFixture<DocumentationArchitectureComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ DocumentationArchitectureComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(DocumentationArchitectureComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<p>
|
<p>
|
||||||
The used database instance is a MongoDB instance running on the BIC, storing all application data. The admin database
|
The used database instance is a MongoDB instance running on the BIC, storing all application data. The admin database
|
||||||
management page can be accessed
|
management page can be accessed
|
||||||
@ -399,6 +398,11 @@ Every time:
|
|||||||
<td>The key used to find the required help text</td>
|
<td>The key used to find the required help text</td>
|
||||||
<td>'/documentation/database'</td>
|
<td>'/documentation/database'</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>level</td>
|
||||||
|
<td>The minimum level required to read this help</td>
|
||||||
|
<td>'write'</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>text</td>
|
<td>text</td>
|
||||||
<td>The actual help text</td>
|
<td>The actual help text</td>
|
||||||
|
@ -1,41 +1,5 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { DocumentationDatabaseComponent } from './documentation-database.component';
|
|
||||||
import {Component, Input} from '@angular/core';
|
|
||||||
import {RbCustomInputsModule} from '../../rb-custom-inputs/rb-custom-inputs.module';
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
@Component({selector: 'app-img-magnifier', template: ''})
|
|
||||||
class ImgMagnifierStubComponent {
|
|
||||||
@Input() magnifierSize: {width: number, height: number};
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('DocumentationDatabaseComponent', () => {
|
describe('DocumentationDatabaseComponent', () => {
|
||||||
let component: DocumentationDatabaseComponent;
|
|
||||||
let fixture: ComponentFixture<DocumentationDatabaseComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [
|
|
||||||
DocumentationDatabaseComponent,
|
|
||||||
ImgMagnifierStubComponent
|
|
||||||
],
|
|
||||||
imports: [
|
|
||||||
RbCustomInputsModule
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(DocumentationDatabaseComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
component.ngOnInit();
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -1,25 +1,5 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { DocumentationModelsComponent } from './documentation-models.component';
|
|
||||||
|
|
||||||
describe('DocumentationModelsComponent', () => {
|
describe('DocumentationModelsComponent', () => {
|
||||||
let component: DocumentationModelsComponent;
|
|
||||||
let fixture: ComponentFixture<DocumentationModelsComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ DocumentationModelsComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(DocumentationModelsComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a [href]="api.hostName + '/static/intro-presentation/index.html'">
|
<a [href]="api.hostName + '/static/intro-presentation/index.html'">
|
||||||
View the presentation explaining the main functions
|
View the presentation explaining the main functions
|
||||||
|
@ -1,42 +1,5 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { DocumentationComponent } from './documentation.component';
|
|
||||||
import {RbCustomInputsModule} from '../rb-custom-inputs/rb-custom-inputs.module';
|
|
||||||
import {ApiService} from '../services/api.service';
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
let apiServiceSpy: jasmine.SpyObj<ApiService>;
|
|
||||||
|
|
||||||
describe('DocumentationComponent', () => {
|
describe('DocumentationComponent', () => {
|
||||||
let component: DocumentationComponent;
|
|
||||||
let fixture: ComponentFixture<DocumentationComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
const apiSpy = jasmine.createSpyObj('ApiService', ['post', 'put']);
|
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ DocumentationComponent ],
|
|
||||||
imports: [
|
|
||||||
RbCustomInputsModule
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
{provide: ApiService, useValue: apiSpy}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(DocumentationComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
component.ngOnInit();
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -1,48 +1,5 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ErrorComponent } from './error.component';
|
|
||||||
import {ModalService, RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
|
|
||||||
import {By} from '@angular/platform-browser';
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
describe('ErrorComponent', () => {
|
describe('ErrorComponent', () => {
|
||||||
let component: ErrorComponent;
|
|
||||||
let fixture: ComponentFixture<ErrorComponent>;
|
|
||||||
let css; // get native element by css selector
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ ErrorComponent ],
|
|
||||||
imports: [
|
|
||||||
RbUiComponentsModule,
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
ModalService
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(ErrorComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
component.ngOnInit();
|
|
||||||
fixture.detectChanges();
|
|
||||||
css = (selector) => fixture.debugElement.query(By.css(selector)).nativeElement;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show the alert', () => {
|
|
||||||
expect(css('rb-alert')).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have the right message', () => {
|
|
||||||
component.message = 'test';
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(css('.dialog-text').innerText).toBe('test');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -7,8 +7,8 @@ import { Component, OnInit } from '@angular/core';
|
|||||||
})
|
})
|
||||||
export class ErrorComponent implements OnInit {
|
export class ErrorComponent implements OnInit {
|
||||||
|
|
||||||
message = '';
|
message = ''; // main error message
|
||||||
details: string[] = [];
|
details: string[] = []; // array of error detail paragraphs
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { ExistsPipe } from './exists.pipe';
|
import { ExistsPipe } from './exists.pipe';
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
describe('ExistsPipe', () => {
|
describe('ExistsPipe', () => {
|
||||||
it('create an instance', () => {
|
it('create an instance', () => {
|
||||||
|
@ -7,7 +7,6 @@ import { Pipe, PipeTransform } from '@angular/core';
|
|||||||
export class ExistsPipe implements PipeTransform {
|
export class ExistsPipe implements PipeTransform {
|
||||||
|
|
||||||
transform(value: unknown, key?): unknown {
|
transform(value: unknown, key?): unknown {
|
||||||
// console.log(new Date().getTime());
|
|
||||||
return value || value === 0 ? (key ? value[key] : value) : '';
|
return value || value === 0 ? (key ? value[key] : value) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-template #normalView>
|
<ng-template #normalView>
|
||||||
<p *ngIf="content.text; else defaultContent">
|
<p *ngIf="content.text; else defaultContent" class="content-text">
|
||||||
{{content.text}}
|
{{content.text}}
|
||||||
</p>
|
</p>
|
||||||
<ng-template #defaultContent>
|
<ng-template #defaultContent>
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
.delete-btn {
|
.delete-btn {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content-text {
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
@ -1,25 +1,5 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { HelpComponent } from './help.component';
|
|
||||||
|
|
||||||
describe('HelpComponent', () => {
|
describe('HelpComponent', () => {
|
||||||
let component: HelpComponent;
|
|
||||||
let fixture: ComponentFixture<HelpComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ HelpComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(HelpComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -12,9 +12,9 @@ import {LoginService} from '../services/login.service';
|
|||||||
})
|
})
|
||||||
export class HelpComponent implements OnInit {
|
export class HelpComponent implements OnInit {
|
||||||
|
|
||||||
content: HelpModel = new HelpModel().deserialize({text: null, level: 'none'});
|
content: HelpModel = new HelpModel().deserialize({text: null, level: 'none'}); // help content
|
||||||
edit = false;
|
edit = false; // set true to change to edit mode
|
||||||
private route = '';
|
private route = ''; // URIComponent encoded route which serves as a key to fetch the help document
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
@ -24,8 +24,8 @@ export class HelpComponent implements OnInit {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
// remove ids from path
|
||||||
this.route = encodeURIComponent(this.router.url.replace(/\/[0-9a-f]{24}/, ''));
|
this.route = encodeURIComponent(this.router.url.replace(/\/[0-9a-f]{24}/, ''));
|
||||||
// remove ids from path and get help content
|
|
||||||
this.api.get<HelpModel>('/help/' + this.route, (data, err) => {
|
this.api.get<HelpModel>('/help/' + this.route, (data, err) => {
|
||||||
if (!err) { // content was found
|
if (!err) { // content was found
|
||||||
this.content = new HelpModel().deserialize(data);
|
this.content = new HelpModel().deserialize(data);
|
||||||
@ -33,7 +33,6 @@ export class HelpComponent implements OnInit {
|
|||||||
else {
|
else {
|
||||||
this.content.text = '';
|
this.content.text = '';
|
||||||
}
|
}
|
||||||
console.log(this.content);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,39 +1,5 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { HomeComponent } from './home.component';
|
|
||||||
import {Component} from '@angular/core';
|
|
||||||
import {By} from '@angular/platform-browser';
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
@Component({selector: 'app-login', template: ''})
|
|
||||||
class LoginStubComponent {}
|
|
||||||
|
|
||||||
describe('HomeComponent', () => {
|
describe('HomeComponent', () => {
|
||||||
let component: HomeComponent;
|
|
||||||
let fixture: ComponentFixture<HomeComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [
|
|
||||||
HomeComponent,
|
|
||||||
LoginStubComponent
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(HomeComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
component.ngOnInit();
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should load the login component', () => {
|
|
||||||
expect(fixture.debugElement.query(By.css('app-login'))).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -1,39 +1,5 @@
|
|||||||
// import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
//
|
|
||||||
// import { ImgMagnifierComponent } from './img-magnifier.component';
|
describe('ImgMagnifierComponent', () => {
|
||||||
//
|
|
||||||
// // TODO
|
});
|
||||||
//
|
|
||||||
// let windowServiceSpy: jasmine.SpyObj<Window>;
|
|
||||||
//
|
|
||||||
// describe('ImgMagnifierComponent', () => {
|
|
||||||
// let component: ImgMagnifierComponent;
|
|
||||||
// let fixture: ComponentFixture<ImgMagnifierComponent>;
|
|
||||||
//
|
|
||||||
// beforeEach(async(() => {
|
|
||||||
// const windowSpy = jasmine.createSpyObj('Window', ['pageXOffset', 'pageYOffset']);
|
|
||||||
//
|
|
||||||
// TestBed.configureTestingModule({
|
|
||||||
// declarations: [ ImgMagnifierComponent ],
|
|
||||||
// imports: [
|
|
||||||
// ],
|
|
||||||
// providers: [
|
|
||||||
// {provide: Window, useValue: windowSpy}
|
|
||||||
// ]
|
|
||||||
// })
|
|
||||||
// .compileComponents();
|
|
||||||
//
|
|
||||||
// windowServiceSpy = TestBed.inject(Window) as jasmine.SpyObj<Window>;
|
|
||||||
// }));
|
|
||||||
//
|
|
||||||
// beforeEach(() => {
|
|
||||||
// fixture = TestBed.createComponent(ImgMagnifierComponent);
|
|
||||||
// component = fixture.componentInstance;
|
|
||||||
// component.ngOnInit();
|
|
||||||
// fixture.detectChanges();
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should create', () => {
|
|
||||||
// expect(component).toBeTruthy();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
@ -7,13 +7,13 @@ import {AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild} from '@a
|
|||||||
})
|
})
|
||||||
export class ImgMagnifierComponent implements OnInit, AfterViewInit {
|
export class ImgMagnifierComponent implements OnInit, AfterViewInit {
|
||||||
|
|
||||||
@Input() src: string;
|
@Input() src: string; // image source
|
||||||
@Input() zoom: number;
|
@Input() zoom: number; // zoom level
|
||||||
@Input() magnifierSize: {width: number, height: number};
|
@Input() magnifierSize: {width: number, height: number}; // size of the magnifier
|
||||||
@ViewChild('mainImg') mainImg: ElementRef;
|
@ViewChild('mainImg') mainImg: ElementRef;
|
||||||
|
|
||||||
backgroundSize;
|
backgroundSize;
|
||||||
magnifierPos = {x: 0, y: 0};
|
magnifierPos = {x: 0, y: 0}; // position of the magnifier
|
||||||
showMagnifier = false;
|
showMagnifier = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -29,7 +29,7 @@ export class ImgMagnifierComponent implements OnInit, AfterViewInit {
|
|||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
calcPos(event) {
|
calcPos(event) { // calculate the current magnifier position
|
||||||
const img = this.mainImg.nativeElement.getBoundingClientRect();
|
const img = this.mainImg.nativeElement.getBoundingClientRect();
|
||||||
this.magnifierPos.x = Math.min(
|
this.magnifierPos.x = Math.min(
|
||||||
img.width - this.magnifierSize.width,
|
img.width - this.magnifierSize.width,
|
||||||
@ -42,7 +42,7 @@ export class ImgMagnifierComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
calcBackgroundSize() {
|
calcBackgroundSize() {
|
||||||
this.backgroundSize = this.mainImg ? (this.mainImg.nativeElement.width * this.zoom - this.magnifierSize.width) + 'px ' +
|
this.backgroundSize = this.mainImg ? (this.mainImg.nativeElement.width * this.zoom - this.magnifierSize.width) +
|
||||||
(this.mainImg.nativeElement.height * this.zoom - this.magnifierSize.height) + 'px ' : '0 0';
|
'px ' + (this.mainImg.nativeElement.height * this.zoom - this.magnifierSize.height) + 'px ' : '0 0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,141 +1,5 @@
|
|||||||
import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
|
import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
|
||||||
import { LoginComponent } from './login.component';
|
|
||||||
import {LoginService} from '../services/login.service';
|
|
||||||
import {ValidationService} from '../services/validation.service';
|
|
||||||
import {FormsModule} from '@angular/forms';
|
|
||||||
import {By} from '@angular/platform-browser';
|
|
||||||
import {ValidateDirective} from '../validate.directive';
|
|
||||||
import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
|
|
||||||
import {ApiService} from '../services/api.service';
|
|
||||||
import {RouterTestingModule} from '@angular/router/testing';
|
|
||||||
import {Router} from '@angular/router';
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
let validationServiceSpy: jasmine.SpyObj<ValidationService>;
|
|
||||||
let loginServiceSpy: jasmine.SpyObj<LoginService>;
|
|
||||||
let apiServiceSpy: jasmine.SpyObj<ApiService>;
|
|
||||||
let routerServiceSpy: Router;
|
|
||||||
|
|
||||||
describe('LoginComponent', () => {
|
describe('LoginComponent', () => {
|
||||||
let component: LoginComponent;
|
|
||||||
let fixture: ComponentFixture<LoginComponent>;
|
|
||||||
let css; // get native element by css selector
|
|
||||||
let cssd; // get debug element by css selector
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
const validationSpy = jasmine.createSpyObj('ValidationService', ['username', 'password']);
|
|
||||||
const loginSpy = jasmine.createSpyObj('LoginService', ['login']);
|
|
||||||
const apiSpy = jasmine.createSpyObj('ApiService', ['post', 'put']);
|
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ LoginComponent, ValidateDirective ],
|
|
||||||
imports: [
|
|
||||||
RbUiComponentsModule,
|
|
||||||
FormsModule,
|
|
||||||
RouterTestingModule
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
{provide: ValidationService, useValue: validationSpy},
|
|
||||||
{provide: LoginService, useValue: loginSpy},
|
|
||||||
{provide: ApiService, useValue: apiSpy}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
validationServiceSpy = TestBed.inject(ValidationService) as jasmine.SpyObj<ValidationService>;
|
|
||||||
loginServiceSpy = TestBed.inject(LoginService) as jasmine.SpyObj<LoginService>;
|
|
||||||
apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
|
|
||||||
routerServiceSpy = TestBed.inject(Router);
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(LoginComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
component.ngOnInit();
|
|
||||||
fixture.detectChanges();
|
|
||||||
cssd = (selector) => fixture.debugElement.query(By.css(selector));
|
|
||||||
css = (selector) => fixture.debugElement.query(By.css(selector)).nativeElement;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
validationServiceSpy.username.and.returnValue({ok: true, error: ''});
|
|
||||||
validationServiceSpy.password.and.returnValue({ok: true, error: ''});
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
// it('should have the `Please log in` heading', () => {
|
|
||||||
// expect(css('h2').innerText).toBe('Please log in');
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should have empty credential inputs', () => {
|
|
||||||
// expect(css('rb-form-input[label=username] input').value).toBe('');
|
|
||||||
// expect(css('rb-form-input[label=password] input').value).toBe('');
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should have an empty message at the beginning', () => {
|
|
||||||
// expect(css('.message').innerText).toBe('');
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should have a login button', async () => {
|
|
||||||
// validationServiceSpy.username.and.returnValue({ok: true, error: ''});
|
|
||||||
// validationServiceSpy.password.and.returnValue({ok: true, error: ''});
|
|
||||||
// await fixture.whenStable();
|
|
||||||
// fixture.detectChanges();
|
|
||||||
// expect(css('.login-button')).toBeTruthy();
|
|
||||||
// expect(css('.login-button').disabled).toBeTruthy();
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should reject a wrong username', async () => {
|
|
||||||
// validationServiceSpy.username.and.returnValue({ok: false, error: 'username must only contain a-z0-9-_.'});
|
|
||||||
// component.username = 'ab#';
|
|
||||||
// fixture.detectChanges();
|
|
||||||
// await fixture.whenRenderingDone();
|
|
||||||
// expect(component.loginForm.controls.username.valid).toBeFalsy();
|
|
||||||
// expect(validationServiceSpy.username).toHaveBeenCalledWith('ab#');
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should reject a wrong password', async () => {
|
|
||||||
// validationServiceSpy.password.and.returnValue({ok: false, error: 'password must only contain a-zA-Z0-9!"#%&\'()*+,-./:;<=>?@[]^_`{|}~'});
|
|
||||||
// component.password = 'abc';
|
|
||||||
//
|
|
||||||
// fixture.detectChanges();
|
|
||||||
// await fixture.whenRenderingDone();
|
|
||||||
// expect(component.loginForm.controls.password.valid).toBeFalsy();
|
|
||||||
// expect(validationServiceSpy.password).toHaveBeenCalledWith('abc');
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should enable the login button with valid credentials', async () => {
|
|
||||||
// validationServiceSpy.username.and.returnValue({ok: true, error: ''});
|
|
||||||
// validationServiceSpy.password.and.returnValue({ok: true, error: ''});
|
|
||||||
// loginServiceSpy.login.and.returnValue(new Promise(r => r(true)));
|
|
||||||
//
|
|
||||||
// fixture.detectChanges();
|
|
||||||
// await fixture.whenRenderingDone();
|
|
||||||
//
|
|
||||||
// cssd('.login-button').triggerEventHandler('click', null);
|
|
||||||
// expect(css('.login-button').disabled).toBeFalsy();
|
|
||||||
// expect(loginServiceSpy.login.calls.count()).toBe(1);
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should call the LoginService with valid credentials', () => {
|
|
||||||
// validationServiceSpy.username.and.returnValue({ok: true, error: ''});
|
|
||||||
// validationServiceSpy.password.and.returnValue({ok: true, error: ''});
|
|
||||||
// loginServiceSpy.login.and.returnValue(new Promise(r => r(true)));
|
|
||||||
//
|
|
||||||
// cssd('.login-button').triggerEventHandler('click', null);
|
|
||||||
// expect(loginServiceSpy.login.calls.count()).toBe(1);
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should display an error if the LoginService could not authenticate', fakeAsync(() => {
|
|
||||||
// validationServiceSpy.username.and.returnValue({ok: true, error: ''});
|
|
||||||
// validationServiceSpy.password.and.returnValue({ok: true, error: ''});
|
|
||||||
// loginServiceSpy.login.and.returnValue(new Promise(r => r(false)));
|
|
||||||
//
|
|
||||||
// cssd('.login-button').triggerEventHandler('click', null);
|
|
||||||
// expect(loginServiceSpy.login.calls.count()).toBe(1);
|
|
||||||
// tick();
|
|
||||||
// fixture.detectChanges();
|
|
||||||
// expect(css('.message').innerText).toBe('Wrong credentials!');
|
|
||||||
// }));
|
|
||||||
});
|
});
|
||||||
|
@ -16,7 +16,7 @@ export class LoginComponent implements OnInit {
|
|||||||
password = '';
|
password = '';
|
||||||
email = '';
|
email = '';
|
||||||
message = ''; // message below login fields
|
message = ''; // message below login fields
|
||||||
passreset = false;
|
passreset = false; // to toggle between normal login and password reset form
|
||||||
|
|
||||||
@ViewChild('loginForm') loginForm;
|
@ViewChild('loginForm') loginForm;
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ export class LoginComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
userLogin() {
|
userLogin() {
|
||||||
if (this.passreset) {
|
if (this.passreset) { // reset password
|
||||||
this.api.post('/user/passreset', {name: this.username, email: this.email}, (data, err) => {
|
this.api.post('/user/passreset', {name: this.username, email: this.email}, (data, err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
this.message = 'Could not find a valid user';
|
this.message = 'Could not find a valid user';
|
||||||
@ -49,7 +49,7 @@ export class LoginComponent implements OnInit {
|
|||||||
if (this.login.isLevel.read) {
|
if (this.login.isLevel.read) {
|
||||||
this.router.navigate(['/samples']);
|
this.router.navigate(['/samples']);
|
||||||
}
|
}
|
||||||
else {
|
else { // navigate prediction users to prediction as they cannot access samples
|
||||||
this.router.navigate(['/prediction']);
|
this.router.navigate(['/prediction']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,5 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { MaterialComponent } from './material.component';
|
|
||||||
|
|
||||||
describe('MaterialComponent', () => {
|
describe('MaterialComponent', () => {
|
||||||
let component: MaterialComponent;
|
|
||||||
let fixture: ComponentFixture<MaterialComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ MaterialComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(MaterialComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -19,12 +19,12 @@ export class MaterialComponent implements OnInit, AfterContentChecked {
|
|||||||
|
|
||||||
@ViewChild('materialForm') materialForm: NgForm;
|
@ViewChild('materialForm') materialForm: NgForm;
|
||||||
|
|
||||||
material: MaterialModel;
|
material: MaterialModel; // material to edit
|
||||||
materialNames: string[] = [];
|
materialNames: string[] = []; // all other material names for unique validation
|
||||||
|
|
||||||
modalText = {list: '', suggestion: ''};
|
modalText = {list: '', suggestion: ''}; // modal for group and supplier correction
|
||||||
loading = 0;
|
loading = 0; // number of loading instances
|
||||||
checkFormAfterInit = true;
|
checkFormAfterInit = true; // revalidate all fields on the next AfterContentChecked
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private api: ApiService,
|
private api: ApiService,
|
||||||
@ -42,6 +42,7 @@ export class MaterialComponent implements OnInit, AfterContentChecked {
|
|||||||
this.material = new MaterialModel().deserialize(data);
|
this.material = new MaterialModel().deserialize(data);
|
||||||
this.loading--;
|
this.loading--;
|
||||||
this.d.load('materials', () => {
|
this.d.load('materials', () => {
|
||||||
|
// filter out name of the edited material as it can stay the same
|
||||||
this.materialNames = this.d.arr.materials.map(e => e.name).filter(e => e !== this.material.name);
|
this.materialNames = this.d.arr.materials.map(e => e.name).filter(e => e !== this.material.name);
|
||||||
this.loading--;
|
this.loading--;
|
||||||
});
|
});
|
||||||
@ -58,12 +59,14 @@ export class MaterialComponent implements OnInit, AfterContentChecked {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngAfterContentChecked() {
|
ngAfterContentChecked() {
|
||||||
|
// attach validators
|
||||||
if (this.materialForm && this.material.properties.material_template) { // material template is set
|
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.d.id.materialTemplates[this.material.properties.material_template].parameters.forEach((parameter, i) => {
|
||||||
this.attachValidator(this.materialForm, 'materialParameter' + i, parameter.range);
|
this.attachValidator(this.materialForm, 'materialParameter' + i, parameter.range);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// revalidate
|
||||||
if (this.checkFormAfterInit && this.materialForm !== undefined && this.materialForm.form.get('propertiesSelect')) {
|
if (this.checkFormAfterInit && this.materialForm !== undefined && this.materialForm.form.get('propertiesSelect')) {
|
||||||
this.checkFormAfterInit = false;
|
this.checkFormAfterInit = false;
|
||||||
Object.keys(this.materialForm.form.controls).forEach(field => {
|
Object.keys(this.materialForm.form.controls).forEach(field => {
|
||||||
@ -97,7 +100,7 @@ export class MaterialComponent implements OnInit, AfterContentChecked {
|
|||||||
|
|
||||||
materialSave() {
|
materialSave() {
|
||||||
this.api.put('/material/' + this.material._id, this.material.sendFormat(), () => {
|
this.api.put('/material/' + this.material._id, this.material.sendFormat(), () => {
|
||||||
delete this.d.arr.materials;
|
delete this.d.arr.materials; // reload materials
|
||||||
this.d.load('materials');
|
this.d.load('materials');
|
||||||
this.router.navigate(['/materials']);
|
this.router.navigate(['/materials']);
|
||||||
});
|
});
|
||||||
@ -107,7 +110,7 @@ export class MaterialComponent implements OnInit, AfterContentChecked {
|
|||||||
this.modal.open(modal).then(result => {
|
this.modal.open(modal).then(result => {
|
||||||
if (result) {
|
if (result) {
|
||||||
this.api.delete('/material/' + this.material._id, (ignore, error) => {
|
this.api.delete('/material/' + this.material._id, (ignore, error) => {
|
||||||
if (error) {
|
if (error) { // material cannot be deleted as it is still referenced by active samples
|
||||||
const modalRef = this.modal.openComponent(ErrorComponent);
|
const modalRef = this.modal.openComponent(ErrorComponent);
|
||||||
modalRef.instance.message = 'Cannot delete material as it is still in use!';
|
modalRef.instance.message = 'Cannot delete material as it is still in use!';
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,5 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { MaterialsComponent } from './materials.component';
|
|
||||||
|
|
||||||
describe('MaterialsComponent', () => {
|
describe('MaterialsComponent', () => {
|
||||||
let component: MaterialsComponent;
|
|
||||||
let fixture: ComponentFixture<MaterialsComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ MaterialsComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(MaterialsComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -12,13 +12,13 @@ import {ModalService} from '@inst-iot/bosch-angular-ui-components';
|
|||||||
})
|
})
|
||||||
export class MaterialsComponent implements OnInit {
|
export class MaterialsComponent implements OnInit {
|
||||||
|
|
||||||
materials: MaterialModel[] = [];
|
materials: MaterialModel[] = []; // all materials
|
||||||
templateKeys: {key: string, label: string}[] = [];
|
templateKeys: {key: string, label: string}[] = []; // material template keys
|
||||||
materialStatus = {validated: true, new: true, deleted: false};
|
materialStatus = {validated: true, new: true, deleted: false}; // material statuses to show
|
||||||
materialSearch = '';
|
materialSearch = ''; // material name search string
|
||||||
sampleSelect = false;
|
sampleSelect = false; // set to true to show checkboxes for validation
|
||||||
|
|
||||||
page = 1;
|
page = 1; // page settings
|
||||||
pages = 0;
|
pages = 0;
|
||||||
pageSize = 25;
|
pageSize = 25;
|
||||||
|
|
||||||
@ -36,8 +36,8 @@ export class MaterialsComponent implements OnInit {
|
|||||||
this.templateKeys.push({key: parameter.name, label: `${this.ucFirst(template.name)} ${parameter.name}`});
|
this.templateKeys.push({key: parameter.name, label: `${this.ucFirst(template.name)} ${parameter.name}`});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
// filter out duplicates
|
||||||
this.templateKeys = this.templateKeys.filter((e, i, a) => !a.slice(0, i).find(el => el.key === e.key));
|
this.templateKeys = this.templateKeys.filter((e, i, a) => !a.slice(0, i).find(el => el.key === e.key));
|
||||||
console.log(this.templateKeys);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ export class MaterialsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validate() {
|
validate() {
|
||||||
if (this.sampleSelect) {
|
if (this.sampleSelect) { // selection was done do actual validation
|
||||||
this.materials.forEach(sample => {
|
this.materials.forEach(sample => {
|
||||||
if (sample.selected) {
|
if (sample.selected) {
|
||||||
this.api.put('/material/validate/' + sample._id);
|
this.api.put('/material/validate/' + sample._id);
|
||||||
@ -60,12 +60,12 @@ export class MaterialsComponent implements OnInit {
|
|||||||
this.loadMaterials();
|
this.loadMaterials();
|
||||||
this.sampleSelect = false;
|
this.sampleSelect = false;
|
||||||
}
|
}
|
||||||
else {
|
else { // activate validation mode
|
||||||
this.sampleSelect = true;
|
this.sampleSelect = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selectAll(event) {
|
selectAll(event) { // toggle selection for all items except deleted ones
|
||||||
this.materials.forEach(material => {
|
this.materials.forEach(material => {
|
||||||
if (material.status !== 'deleted') {
|
if (material.status !== 'deleted') {
|
||||||
material.selected = event.target.checked;
|
material.selected = event.target.checked;
|
||||||
@ -86,11 +86,11 @@ export class MaterialsComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ucFirst(string) {
|
ucFirst(string) { // convert first character of string to uppercase
|
||||||
return string[0].toUpperCase() + string.slice(1);
|
return string[0].toUpperCase() + string.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
materialFilter(ms) {
|
materialFilter(ms) { // filter function for material names
|
||||||
return e => e.name.indexOf(ms) >= 0;
|
return e => e.name.indexOf(ms) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<rb-icon-button icon="add" mode="primary" (click)="newModel = !newModel; oldModelGroup = ''" class="space-below">
|
<rb-icon-button icon="add" mode="primary" (click)="newModel = !newModel; oldModelGroup = ''" class="space-below">
|
||||||
New model
|
New model
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
|
@ -1,25 +1,5 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ModelTemplatesComponent } from './model-templates.component';
|
|
||||||
|
|
||||||
describe('ModelTemplatesComponent', () => {
|
describe('ModelTemplatesComponent', () => {
|
||||||
let component: ModelTemplatesComponent;
|
|
||||||
let fixture: ComponentFixture<ModelTemplatesComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ ModelTemplatesComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(ModelTemplatesComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -13,12 +13,12 @@ import omit from 'lodash/omit';
|
|||||||
})
|
})
|
||||||
export class ModelTemplatesComponent implements OnInit {
|
export class ModelTemplatesComponent implements OnInit {
|
||||||
|
|
||||||
newModel = false;
|
newModel = false; // display new model dialog
|
||||||
modelGroup = '';
|
modelGroup = ''; // group of the edited model
|
||||||
oldModelGroup = '';
|
oldModelGroup = ''; // group of the edited model before editing started
|
||||||
oldModelName = '';
|
oldModelName = ''; // name of the edited model before editing started
|
||||||
model = new ModelItemModel().models[0];
|
model = new ModelItemModel().models[0]; // edited model
|
||||||
groups = [];
|
groups = []; // all model group names
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private api: ApiService,
|
private api: ApiService,
|
||||||
@ -40,13 +40,12 @@ export class ModelTemplatesComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
saveModel() {
|
saveModel() {
|
||||||
console.log(this.modelGroup);
|
// group was changed, delete model in old group
|
||||||
console.log(this.oldModelGroup);
|
if (this.oldModelGroup !== '' && this.modelGroup !== this.oldModelGroup) {
|
||||||
if (this.oldModelGroup !== '' && this.modelGroup !== this.oldModelGroup) { // group was changed, delete model in old group
|
|
||||||
this.delete(null, this.oldModelName, this.oldModelGroup);
|
this.delete(null, this.oldModelName, this.oldModelGroup);
|
||||||
}
|
}
|
||||||
this.api.post('/model/' + this.modelGroup, omit(this.model, '_id'), () => {
|
this.api.post('/model/' + this.modelGroup, omit(this.model, '_id'), () => {
|
||||||
this.newModel = false;
|
this.newModel = false; // reset model edit parameters
|
||||||
this.loadGroups();
|
this.loadGroups();
|
||||||
this.modelGroup = '';
|
this.modelGroup = '';
|
||||||
this.oldModelGroup = '';
|
this.oldModelGroup = '';
|
||||||
@ -57,7 +56,7 @@ export class ModelTemplatesComponent implements OnInit {
|
|||||||
|
|
||||||
delete(modal, name, group = null) {
|
delete(modal, name, group = null) {
|
||||||
new Promise(resolve => {
|
new Promise(resolve => {
|
||||||
if (modal) {
|
if (modal) { // if modal was given, wait for result
|
||||||
this.modal.open(modal).then(result => {
|
this.modal.open(modal).then(result => {
|
||||||
resolve(result);
|
resolve(result);
|
||||||
});
|
});
|
||||||
@ -67,12 +66,12 @@ export class ModelTemplatesComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
if (res) {
|
if (res) {
|
||||||
if (group) { // delete group
|
if (group) { // delete model group if given
|
||||||
this.api.delete(`/model/${group}/${name}`, () => {
|
this.api.delete(`/model/${group}/${name}`, () => {
|
||||||
this.loadGroups();
|
this.loadGroups();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else { // delete file
|
else { // delete model file
|
||||||
this.api.delete(`/model/file/${name}`, () => {
|
this.api.delete(`/model/file/${name}`, () => {
|
||||||
this.d.arr.modelFiles.splice(this.d.arr.modelFiles.findIndex(e => e.name === name), 1);
|
this.d.arr.modelFiles.splice(this.d.arr.modelFiles.findIndex(e => e.name === name), 1);
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { ObjectPipe } from './object.pipe';
|
import { ObjectPipe } from './object.pipe';
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
describe('ObjectPipe', () => {
|
describe('ObjectPipe', () => {
|
||||||
it('create an instance', () => {
|
it('create an instance', () => {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { ParametersPipe } from './parameters.pipe';
|
import { ParametersPipe } from './parameters.pipe';
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
describe('ParametersPipe', () => {
|
describe('ParametersPipe', () => {
|
||||||
it('create an instance', () => {
|
it('create an instance', () => {
|
||||||
|
@ -6,7 +6,8 @@ import { Pipe, PipeTransform } from '@angular/core';
|
|||||||
export class ParametersPipe implements PipeTransform {
|
export class ParametersPipe implements PipeTransform {
|
||||||
|
|
||||||
transform(value: {name: string, range: object}[]): string {
|
transform(value: {name: string, range: object}[]): string {
|
||||||
return `{${value.map(e => `${e.name}: <${JSON.stringify(e.range).replace('{}', 'any').replace(/["{}]/g, '')}>`).join(', ')}}`;
|
return `{${value.map(e => `${e.name}: <${JSON.stringify(e.range).replace('{}', 'any')
|
||||||
|
.replace(/["{}]/g, '')}>`).join(', ')}}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<rb-tab-panel (tabChanged)="groupChange($event)">
|
<rb-tab-panel (tabChanged)="groupChange($event)">
|
||||||
<ng-container *ngFor="let group of d.arr.modelGroups; index as i">
|
<ng-container *ngFor="let group of d.arr.modelGroups; index as i">
|
||||||
<div *rbTabPanelItem="group.group; id: i"></div>
|
<div *rbTabPanelItem="group.group; id: i"></div>
|
||||||
|
@ -1,47 +1,5 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { PredictionComponent } from './prediction.component';
|
|
||||||
import {ApiService} from '../services/api.service';
|
|
||||||
import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
|
|
||||||
import {FormsModule} from '@angular/forms';
|
|
||||||
import {ValidationService} from '../services/validation.service';
|
|
||||||
import {DataService} from '../services/data.service';
|
|
||||||
import {ChartsModule} from 'ng2-charts';
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
let apiServiceSpy: jasmine.SpyObj<ApiService>;
|
|
||||||
|
|
||||||
describe('PredictionComponent', () => {
|
describe('PredictionComponent', () => {
|
||||||
let component: PredictionComponent;
|
|
||||||
let fixture: ComponentFixture<PredictionComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
const apiSpy = jasmine.createSpyObj('ApiService', ['post']);
|
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ PredictionComponent ],
|
|
||||||
imports: [
|
|
||||||
RbUiComponentsModule,
|
|
||||||
ChartsModule
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
{provide: ApiService, useValue: apiSpy}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(PredictionComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
component.ngOnInit();
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -29,11 +29,12 @@ import {ModelItemModel} from '../models/model-item.model';
|
|||||||
})
|
})
|
||||||
export class PredictionComponent implements OnInit {
|
export class PredictionComponent implements OnInit {
|
||||||
|
|
||||||
result: {predictions: string[], mean: string};
|
result: {predictions: string[], mean: string}; // prediction result from python container
|
||||||
loading = false;
|
loading = false;
|
||||||
activeGroup: ModelItemModel = new ModelItemModel();
|
activeGroup: ModelItemModel = new ModelItemModel();
|
||||||
activeModelIndex = 0;
|
activeModelIndex = 0;
|
||||||
multipleSamples = false; // 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;
|
||||||
spectrumNames: string[] = [];
|
spectrumNames: string[] = [];
|
||||||
spectrum: string[][] = [[]];
|
spectrum: string[][] = [[]];
|
||||||
flattenedSpectra = [];
|
flattenedSpectra = [];
|
||||||
@ -50,7 +51,7 @@ export class PredictionComponent implements OnInit {
|
|||||||
readonly chartOptions: ChartOptions = {
|
readonly chartOptions: ChartOptions = {
|
||||||
scales: {
|
scales: {
|
||||||
xAxes: [{ticks: {min: 400, max: 4000, stepSize: 400, reverse: true}}],
|
xAxes: [{ticks: {min: 400, max: 4000, stepSize: 400, reverse: true}}],
|
||||||
yAxes: [{ticks: {min: 0, max: 1}}]
|
yAxes: [{ticks: {}}]
|
||||||
},
|
},
|
||||||
responsive: true,
|
responsive: true,
|
||||||
tooltips: {enabled: false},
|
tooltips: {enabled: false},
|
||||||
@ -82,13 +83,16 @@ export class PredictionComponent implements OnInit {
|
|||||||
if (files.hasOwnProperty(i)) {
|
if (files.hasOwnProperty(i)) {
|
||||||
const fileReader = new FileReader();
|
const fileReader = new FileReader();
|
||||||
fileReader.onload = () => {
|
fileReader.onload = () => {
|
||||||
|
// parse to database spectrum representation
|
||||||
this.spectrum = fileReader.result.toString().split('\r\n').map(e => e.split(',').map(el => parseFloat(el)))
|
this.spectrum = fileReader.result.toString().split('\r\n').map(e => e.split(',').map(el => parseFloat(el)))
|
||||||
.filter(el => el.length === 2) as any;
|
.filter(el => el.length === 2) as any;
|
||||||
|
// flatten to format needed for prediction
|
||||||
this.flattenedSpectra[i] = {labels: this.spectrum.map(e => e[0]), values: this.spectrum.map(e => e[1])};
|
this.flattenedSpectra[i] = {labels: this.spectrum.map(e => e[0]), values: this.spectrum.map(e => e[1])};
|
||||||
|
// add to chart
|
||||||
this.chart[i] = cloneDeep(this.chartInit);
|
this.chart[i] = cloneDeep(this.chartInit);
|
||||||
this.chart[i].data = this.spectrum.map(e => ({x: parseFloat(e[0]), y: parseFloat(e[1])}));
|
this.chart[i].data = this.spectrum.map(e => ({x: parseFloat(e[0]), y: parseFloat(e[1])}));
|
||||||
load --;
|
load --;
|
||||||
if (load <= 0) {
|
if (load <= 0) { // all loaded
|
||||||
this.loadPrediction();
|
this.loadPrediction();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -99,24 +103,21 @@ export class PredictionComponent implements OnInit {
|
|||||||
|
|
||||||
loadPrediction() {
|
loadPrediction() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
console.log(this.activeGroup);
|
|
||||||
console.log(this.activeModelIndex);
|
|
||||||
this.api.post<any>(this.activeGroup.models[this.activeModelIndex].url, this.flattenedSpectra, data => {
|
this.api.post<any>(this.activeGroup.models[this.activeModelIndex].url, this.flattenedSpectra, data => {
|
||||||
this.result = {
|
this.result = { // parse result into prediction and mean string
|
||||||
predictions: Object.entries(omit(data, ['mean', 'std', 'label']))
|
predictions: Object.entries(omit(data, ['mean', 'std', 'label']))
|
||||||
.map((p: any) => p[1].map(e => `${p[0]}: ${e} ${data.label[p[0]]}`))
|
.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])),
|
.reduce((s, e) => s.map((el, i) => this.clip(el) + ', ' + e[i])),
|
||||||
mean: Object.keys(data.mean).map(e =>
|
mean: Object.keys(data.mean).map(e =>
|
||||||
this.clip(`${e}: ${data.mean[e]} ${data.label[e]}`) + (data.std[e] !== '' ? ` (standard deviation: ${data.std[e]})` : '')
|
this.clip(`${e}: ${data.mean[e]} ${data.label[e]}`) +
|
||||||
|
(data.std[e] !== '' ? ` (standard deviation: ${data.std[e]})` : '')
|
||||||
).join(', ')
|
).join(', ')
|
||||||
};
|
};
|
||||||
console.log(this.result);
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
groupChange(index) {
|
groupChange(index) { // group was changed
|
||||||
console.log(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;
|
||||||
|
@ -1,18 +1,5 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ArrayInputHelperService } from './array-input-helper.service';
|
|
||||||
|
|
||||||
// TOdo
|
|
||||||
|
|
||||||
describe('ArrayInputHelperService', () => {
|
describe('ArrayInputHelperService', () => {
|
||||||
let service: ArrayInputHelperService;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
TestBed.configureTestingModule({});
|
|
||||||
service = TestBed.inject(ArrayInputHelperService);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be created', () => {
|
|
||||||
expect(service).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -10,7 +10,7 @@ export class ArrayInputHelperService {
|
|||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
values(id: string) {
|
values(id: string) { // observable which returns new values as they come for subscribed id
|
||||||
return new Observable<{index: number, value: any}>(observer => {
|
return new Observable<{index: number, value: any}>(observer => {
|
||||||
this.com.subscribe(data => {
|
this.com.subscribe(data => {
|
||||||
if (data.id === id) {
|
if (data.id === id) {
|
||||||
@ -20,7 +20,7 @@ export class ArrayInputHelperService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
newValue(id: string, index: number, value: any) {
|
newValue(id: string, index: number, value: any) { // set new value
|
||||||
this.com.next({id, index, value});
|
this.com.next({id, index, value});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,5 @@
|
|||||||
// import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
//
|
|
||||||
// import { RbArrayInputComponent } from './rb-array-input.component';
|
describe('RbArrayInputComponent', () => {
|
||||||
//
|
|
||||||
// describe('RbArrayInputComponent', () => {
|
});
|
||||||
// let component: RbArrayInputComponent;
|
|
||||||
// let fixture: ComponentFixture<RbArrayInputComponent>;
|
|
||||||
//
|
|
||||||
// beforeEach(async(() => {
|
|
||||||
// TestBed.configureTestingModule({
|
|
||||||
// declarations: [ RbArrayInputComponent ]
|
|
||||||
// })
|
|
||||||
// .compileComponents();
|
|
||||||
// }));
|
|
||||||
//
|
|
||||||
// beforeEach(() => {
|
|
||||||
// fixture = TestBed.createComponent(RbArrayInputComponent);
|
|
||||||
// component = fixture.componentInstance;
|
|
||||||
// component.ngOnInit();
|
|
||||||
// fixture.detectChanges();
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should create', () => {
|
|
||||||
// expect(component).toBeTruthy();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
@ -37,7 +37,7 @@ export class RbArrayInputListenerDirective {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
@HostListener('ngModelChange', ['$event'])
|
@HostListener('ngModelChange', ['$event'])
|
||||||
onChange(event) {
|
onChange(event) { // emit new value
|
||||||
this.helperService.newValue(this.rbArrayInputListener, this.index, event);
|
this.helperService.newValue(this.rbArrayInputListener, this.index, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,7 +52,7 @@ export class RbArrayInputListenerDirective {
|
|||||||
})
|
})
|
||||||
export class RbArrayInputComponent implements ControlValueAccessor, OnInit, AfterViewInit {
|
export class RbArrayInputComponent implements ControlValueAccessor, OnInit, AfterViewInit {
|
||||||
|
|
||||||
pushTemplate: any = '';
|
pushTemplate: any = ''; // array element template
|
||||||
@Input('pushTemplate') set _pushTemplate(value) {
|
@Input('pushTemplate') set _pushTemplate(value) {
|
||||||
this.pushTemplate = value;
|
this.pushTemplate = value;
|
||||||
if (this.values.length) {
|
if (this.values.length) {
|
||||||
@ -98,7 +98,8 @@ export class RbArrayInputComponent implements ControlValueAccessor, OnInit, Afte
|
|||||||
if (this.pushTemplate !== null) {
|
if (this.pushTemplate !== null) {
|
||||||
if (this.pushPath) {
|
if (this.pushPath) {
|
||||||
// remove last element if last two are empty
|
// remove last element if last two are empty
|
||||||
if (this.values[this.values.length - 1][this.pushPath] === '' && this.values[this.values.length - 2][this.pushPath] === '') {
|
if (this.values[this.values.length - 1][this.pushPath] === '' &&
|
||||||
|
this.values[this.values.length - 2][this.pushPath] === '') {
|
||||||
this.values.pop();
|
this.values.pop();
|
||||||
}
|
}
|
||||||
// add element if last all are filled
|
// add element if last all are filled
|
||||||
@ -131,6 +132,7 @@ export class RbArrayInputComponent implements ControlValueAccessor, OnInit, Afte
|
|||||||
writeValue(obj: any) { // add empty value on init
|
writeValue(obj: any) { // add empty value on init
|
||||||
if (obj) {
|
if (obj) {
|
||||||
if (this.pushTemplate !== null) {
|
if (this.pushTemplate !== null) {
|
||||||
|
// filter out empty values
|
||||||
if (this.pushPath) {
|
if (this.pushPath) {
|
||||||
this.values = [...obj.filter(e => e[this.pushPath] !== ''), cloneDeep(this.pushTemplate)];
|
this.values = [...obj.filter(e => e[this.pushPath] !== ''), cloneDeep(this.pushTemplate)];
|
||||||
}
|
}
|
||||||
@ -150,22 +152,6 @@ export class RbArrayInputComponent implements ControlValueAccessor, OnInit, Afte
|
|||||||
this.values = [''];
|
this.values = [''];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// this.values = obj ? obj : [];
|
|
||||||
// console.log('-----');
|
|
||||||
// console.log(obj);
|
|
||||||
// console.log(this.pushPath);
|
|
||||||
// if (this.values && this.values.length) {
|
|
||||||
// this.values = obj.filter(e => this.pushPath ? e[this.pushPath] !== '' : e !== '');
|
|
||||||
// }
|
|
||||||
// console.log(this.values);
|
|
||||||
// // console.log(obj.filter(e => this.pushPath ? e[this.pushPath] !== '' : e !== ''));
|
|
||||||
// // this.values = obj ? obj.filter(e => this.pushPath ? e[this.pushPath] !== '' : e !== '') : [];
|
|
||||||
// if (this.values.length === 0 || this.values[0] !== '') {
|
|
||||||
// // add empty last field if pushTemplate is specified
|
|
||||||
// if (this.pushTemplate !== null) {
|
|
||||||
// this.values.push(cloneDeep(this.pushTemplate));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOnChange(fn: any) {
|
registerOnChange(fn: any) {
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { RbTableComponent } from './rb-table/rb-table.component';
|
import { RbTableComponent } from './rb-table/rb-table.component';
|
||||||
import {RbArrayInputComponent, RbArrayInputListenerDirective, RbArrayInputItemDirective} from './rb-array-input/rb-array-input.component';
|
import {RbArrayInputComponent, RbArrayInputListenerDirective, RbArrayInputItemDirective} from
|
||||||
|
'./rb-array-input/rb-array-input.component';
|
||||||
import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
|
import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
|
||||||
import {FormsModule} from '@angular/forms';
|
import {FormsModule} from '@angular/forms';
|
||||||
import { RbIconButtonComponent } from './rb-icon-button/rb-icon-button.component';
|
import { RbIconButtonComponent } from './rb-icon-button/rb-icon-button.component';
|
||||||
|
@ -1,29 +1,5 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { RbIconButtonComponent } from './rb-icon-button.component';
|
|
||||||
import {RbCustomInputsModule} from '../rb-custom-inputs.module';
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
describe('RbIconButtonComponent', () => {
|
describe('RbIconButtonComponent', () => {
|
||||||
let component: RbIconButtonComponent;
|
|
||||||
let fixture: ComponentFixture<RbIconButtonComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ RbIconButtonComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(RbIconButtonComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
component.ngOnInit();
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -34,6 +34,5 @@ table.ellipsis {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
//min-width: 100px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,5 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { RbTableComponent } from './rb-table.component';
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
describe('RbTableComponent', () => {
|
describe('RbTableComponent', () => {
|
||||||
let component: RbTableComponent;
|
|
||||||
let fixture: ComponentFixture<RbTableComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ RbTableComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(RbTableComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
component.ngOnInit();
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -1,76 +1,5 @@
|
|||||||
// import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
//
|
|
||||||
// import { SampleComponent } from './sample.component';
|
describe('SampleComponent', () => {
|
||||||
// import {ApiService} from '../services/api.service';
|
|
||||||
// import {ValidationService} from '../services/validation.service';
|
});
|
||||||
// import {DataService} from '../services/data.service';
|
|
||||||
// import {ModalService, RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
|
|
||||||
// import {FormsModule} from '@angular/forms';
|
|
||||||
// import {ActivatedRoute, Router} from '@angular/router';
|
|
||||||
// import {AutocompleteService} from '../services/autocomplete.service';
|
|
||||||
// import {RbCustomInputsModule} from '../rb-custom-inputs/rb-custom-inputs.module';
|
|
||||||
//
|
|
||||||
// // TODO
|
|
||||||
//
|
|
||||||
// let routerServiceSpy: jasmine.SpyObj<Router>;
|
|
||||||
// let activatedRouteServiceSpy: jasmine.SpyObj<ActivatedRoute>;
|
|
||||||
// let apiServiceSpy: jasmine.SpyObj<ApiService>;
|
|
||||||
// let validationServiceSpy: jasmine.SpyObj<ValidationService>;
|
|
||||||
// let autocompleteServiceSpy: jasmine.SpyObj<AutocompleteService>;
|
|
||||||
// let modalServiceSpy: jasmine.SpyObj<ModalService>;
|
|
||||||
// let dataServiceSpy: jasmine.SpyObj<DataService>;
|
|
||||||
//
|
|
||||||
// describe('SampleComponent', () => {
|
|
||||||
// let component: SampleComponent;
|
|
||||||
// let fixture: ComponentFixture<SampleComponent>;
|
|
||||||
//
|
|
||||||
// beforeEach(async(() => {
|
|
||||||
// const routerSpy = jasmine.createSpyObj('Router', ['navigate']);
|
|
||||||
// const activatedRouteSpy = jasmine.createSpyObj('ActivatedRoute', ['snapshot']);
|
|
||||||
// const apiSpy = jasmine.createSpyObj('ApiService', ['get', 'post', 'put', 'delete']);
|
|
||||||
// const validationSpy = jasmine.createSpyObj('ValidationService', ['generate']);
|
|
||||||
// const autocompleteSpy = jasmine.createSpyObj('AutocompleteService', ['bind']);
|
|
||||||
// const modalSpy = jasmine.createSpyObj('ModalService', ['open']);
|
|
||||||
// const dataSpy = jasmine.createSpyObj('DataService', ['load', 'idReload']);
|
|
||||||
//
|
|
||||||
// TestBed.configureTestingModule({
|
|
||||||
// declarations: [ SampleComponent ],
|
|
||||||
// imports: [
|
|
||||||
// RbUiComponentsModule,
|
|
||||||
// RbCustomInputsModule,
|
|
||||||
// FormsModule
|
|
||||||
// ],
|
|
||||||
// providers: [
|
|
||||||
// {provide: Router, useValue: routerSpy},
|
|
||||||
// {provide: ActivatedRoute, useValue: {snapshot: {paramMap: {get: (id) => '12345'}}}},
|
|
||||||
// {provide: ApiService, useValue: apiSpy},
|
|
||||||
// {provide: ValidationService, useValue: validationSpy},
|
|
||||||
// {provide: AutocompleteService, useValue: autocompleteSpy},
|
|
||||||
// {provide: ModalService, useValue: modalSpy},
|
|
||||||
// {provide: DataService, useValue: dataSpy}
|
|
||||||
// ]
|
|
||||||
// })
|
|
||||||
// .compileComponents();
|
|
||||||
//
|
|
||||||
// routerServiceSpy = TestBed.inject(Router) as jasmine.SpyObj<Router>;
|
|
||||||
// activatedRouteServiceSpy = TestBed.inject(ActivatedRoute) as jasmine.SpyObj<ActivatedRoute>;
|
|
||||||
// apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
|
|
||||||
// validationServiceSpy = TestBed.inject(ValidationService) as jasmine.SpyObj<ValidationService>;
|
|
||||||
// autocompleteServiceSpy = TestBed.inject(AutocompleteService) as jasmine.SpyObj<AutocompleteService>;
|
|
||||||
// modalServiceSpy = TestBed.inject(ModalService) as jasmine.SpyObj<ModalService>;
|
|
||||||
// dataServiceSpy = TestBed.inject(DataService) as jasmine.SpyObj<DataService>;
|
|
||||||
//
|
|
||||||
// // activatedRouteServiceSpy.snapshot.and.returnValue({paramMap: {get: () => '12345'}});
|
|
||||||
// }));
|
|
||||||
//
|
|
||||||
// beforeEach(() => {
|
|
||||||
// fixture = TestBed.createComponent(SampleComponent);
|
|
||||||
// component = fixture.componentInstance;
|
|
||||||
// component.ngOnInit();
|
|
||||||
// fixture.detectChanges();
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should create', () => {
|
|
||||||
// expect(component).toBeTruthy();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
@ -25,8 +25,6 @@ import {ModalService} from '@inst-iot/bosch-angular-ui-components';
|
|||||||
import {DataService} from '../services/data.service';
|
import {DataService} from '../services/data.service';
|
||||||
import {LoginService} from '../services/login.service';
|
import {LoginService} from '../services/login.service';
|
||||||
|
|
||||||
// TODO: additional property value not validated on edit
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-sample',
|
selector: 'app-sample',
|
||||||
templateUrl: './sample.component.html',
|
templateUrl: './sample.component.html',
|
||||||
@ -82,7 +80,8 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
modalText = {list: '', suggestion: ''};
|
modalText = {list: '', suggestion: ''};
|
||||||
cmSampleIndex = '0';
|
cmSampleIndex = '0';
|
||||||
measurementDeleteList = []; // buffer with measurements to delete, if the user confirms and saves the cm changes
|
measurementDeleteList = []; // buffer with measurements to delete, if the user confirms and saves the cm changes
|
||||||
measurementRestoreData: MeasurementModel[] = []; // deleted measurements if user is allowed and measurements are available
|
// deleted measurements if user is allowed and measurements are available
|
||||||
|
measurementRestoreData: MeasurementModel[] = [];
|
||||||
|
|
||||||
charts = [[]]; // chart data for spectra
|
charts = [[]]; // chart data for spectra
|
||||||
readonly chartInit = [{
|
readonly chartInit = [{
|
||||||
@ -118,7 +117,6 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
console.log(this.baseSample);
|
|
||||||
this.mode = this.router.url === '/samples/new' ? 'new' : '';
|
this.mode = this.router.url === '/samples/new' ? 'new' : '';
|
||||||
this.loading = 7;
|
this.loading = 7;
|
||||||
this.d.load('materials', () => {
|
this.d.load('materials', () => {
|
||||||
@ -150,7 +148,8 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
});
|
});
|
||||||
this.d.load('materialTemplates', () => {
|
this.d.load('materialTemplates', () => {
|
||||||
if (!this.material.properties.material_template) {
|
if (!this.material.properties.material_template) {
|
||||||
this.material.properties.material_template = this.d.latest.materialTemplates.find(e => e.name === 'plastic')._id;
|
this.material.properties.material_template =
|
||||||
|
this.d.latest.materialTemplates.find(e => e.name === 'plastic')._id;
|
||||||
}
|
}
|
||||||
this.loading--;
|
this.loading--;
|
||||||
});
|
});
|
||||||
@ -167,9 +166,9 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
if (this.login.isLevel.dev) { // load measurement restore data
|
if (this.login.isLevel.dev) { // load measurement restore data
|
||||||
this.api.get<MeasurementModel[]>('/measurement/sample/' + sampleIds[0], (data, ignore) => {
|
this.api.get<MeasurementModel[]>('/measurement/sample/' + sampleIds[0], (data, ignore) => {
|
||||||
if (data) {
|
if (data) {
|
||||||
this.measurementRestoreData = data.filter(e => e.status === 'deleted').map(e => new MeasurementModel().deserialize(e));
|
this.measurementRestoreData =
|
||||||
|
data.filter(e => e.status === 'deleted').map(e => new MeasurementModel().deserialize(e));
|
||||||
}
|
}
|
||||||
console.log(this.measurementRestoreData);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,7 +181,8 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
this.samples = [new SampleModel().deserialize(sData)];
|
this.samples = [new SampleModel().deserialize(sData)];
|
||||||
this.baseSample.deserialize(sData);
|
this.baseSample.deserialize(sData);
|
||||||
this.material = new MaterialModel().deserialize(sData.material); // read material
|
this.material = new MaterialModel().deserialize(sData.material); // read material
|
||||||
this.customFields = this.baseSample.notes.custom_fields && this.baseSample.notes.custom_fields !== {} ? // read custom fields
|
// read custom fields
|
||||||
|
this.customFields = this.baseSample.notes.custom_fields && this.baseSample.notes.custom_fields !== {} ?
|
||||||
Object.keys(this.baseSample.notes.custom_fields).map(e => [e, this.baseSample.notes.custom_fields[e]]) : [];
|
Object.keys(this.baseSample.notes.custom_fields).map(e => [e, this.baseSample.notes.custom_fields[e]]) : [];
|
||||||
if (this.baseSample.notes.sample_references.length) { // read sample references
|
if (this.baseSample.notes.sample_references.length) { // read sample references
|
||||||
this.sampleReferences = [];
|
this.sampleReferences = [];
|
||||||
@ -214,11 +214,10 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
}
|
}
|
||||||
this.checkFormAfterInit = true;
|
this.checkFormAfterInit = true;
|
||||||
this.loading--;
|
this.loading--;
|
||||||
sampleIds.slice(1).forEach(sampleId => {
|
sampleIds.slice(1).forEach(sampleId => { // load further samples for batch edit
|
||||||
this.api.get<SampleModel>('/sample/' + sampleId, data => {
|
this.api.get<SampleModel>('/sample/' + sampleId, data => {
|
||||||
this.samples.push(new SampleModel().deserialize(data));
|
this.samples.push(new SampleModel().deserialize(data));
|
||||||
['type', 'color', 'batch', 'notes'].forEach((key) => {
|
['type', 'color', 'batch', 'notes'].forEach((key) => {
|
||||||
console.log(isEqual(data[key], this.baseSample[key]));
|
|
||||||
if (!isEqual(data[key], this.baseSample[key])) {
|
if (!isEqual(data[key], this.baseSample[key])) {
|
||||||
this.baseSample[key] = undefined;
|
this.baseSample[key] = undefined;
|
||||||
}
|
}
|
||||||
@ -228,7 +227,6 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
}
|
}
|
||||||
this.loading--;
|
this.loading--;
|
||||||
this.checkFormAfterInit = true;
|
this.checkFormAfterInit = true;
|
||||||
console.log(this.baseSample.material.name);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -239,6 +237,7 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngAfterContentChecked() {
|
ngAfterContentChecked() {
|
||||||
|
// attach validators
|
||||||
if (this.samples.length) { // conditions are displayed
|
if (this.samples.length) { // conditions are displayed
|
||||||
this.samples.forEach((gSample, gIndex) => {
|
this.samples.forEach((gSample, gIndex) => {
|
||||||
if (this.d.id.conditionTemplates[gSample.condition.condition_template]) {
|
if (this.d.id.conditionTemplates[gSample.condition.condition_template]) {
|
||||||
@ -260,6 +259,7 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// revalidate inputs
|
||||||
if (this.checkFormAfterInit) {
|
if (this.checkFormAfterInit) {
|
||||||
if (this.view.base) { // validate sampleForm
|
if (this.view.base) { // validate sampleForm
|
||||||
if (this.sampleForm !== undefined && this.sampleForm.form.get('cf-key0')) {
|
if (this.sampleForm !== undefined && this.sampleForm.form.get('cf-key0')) {
|
||||||
@ -277,13 +277,13 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
(this.d.id.conditionTemplates[this.samples[0].condition.condition_template].parameters.length - 1)) as any;
|
(this.d.id.conditionTemplates[this.samples[0].condition.condition_template].parameters.length - 1)) as any;
|
||||||
}
|
}
|
||||||
if (this.samples[0].measurements.length) { // if there are measurements, last measurement field exists
|
if (this.samples[0].measurements.length) { // if there are measurements, last measurement field exists
|
||||||
formReady = formReady && this.cmForm.form.get('measurementParameter-0-' + (this.samples[0].measurements.length - 1) +
|
formReady = formReady &&
|
||||||
|
this.cmForm.form.get('measurementParameter-0-' + (this.samples[0].measurements.length - 1) +
|
||||||
'-' + (this.d.id.measurementTemplates[this.samples[0].measurements[this.samples[0].measurements.length - 1]
|
'-' + (this.d.id.measurementTemplates[this.samples[0].measurements[this.samples[0].measurements.length - 1]
|
||||||
.measurement_template].parameters.length - 1)) as any;
|
.measurement_template].parameters.length - 1)) as any;
|
||||||
}
|
}
|
||||||
if (formReady) { // fields are ready, do validation
|
if (formReady) { // fields are ready, do validation
|
||||||
this.checkFormAfterInit = false;
|
this.checkFormAfterInit = false;
|
||||||
console.log('init');
|
|
||||||
Object.keys(this.cmForm.form.controls).forEach(field => {
|
Object.keys(this.cmForm.form.controls).forEach(field => {
|
||||||
this.cmForm.form.get(field).updateValueAndValidity();
|
this.cmForm.form.get(field).updateValueAndValidity();
|
||||||
});
|
});
|
||||||
@ -346,7 +346,7 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.baseSample.notes.sample_references = this.sampleReferences
|
this.baseSample.notes.sample_references = this.sampleReferences
|
||||||
.filter(e => e[0] && e[1] && e[2])
|
.filter(e => e[0] && e[1] && e[2]) // filter empty values
|
||||||
.map(e => ({sample_id: e[2], relation: e[1]}));
|
.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
|
if (this.samples.length === 0) { // only save new sample for the first time in mode new, otherwise save changes
|
||||||
@ -363,7 +363,6 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.samples.forEach((sample, i) => {
|
this.samples.forEach((sample, i) => {
|
||||||
console.log(sample._id);
|
|
||||||
this.api.put<SampleModel>('/sample/' + sample._id, this.baseSample.sendFormat(false), data => {
|
this.api.put<SampleModel>('/sample/' + sample._id, this.baseSample.sendFormat(false), data => {
|
||||||
merge(this.samples[i], omit(data, ['condition']));
|
merge(this.samples[i], omit(data, ['condition']));
|
||||||
this.samples[i].material = this.d.arr.materials.find(e => e._id === this.samples[0].material_id);
|
this.samples[i].material = this.d.arr.materials.find(e => e._id === this.samples[0].material_id);
|
||||||
@ -381,7 +380,10 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
if (sample.condition.condition_template) { // condition was set
|
if (sample.condition.condition_template) { // condition was set
|
||||||
this.api.put('/sample/' + sample._id,
|
this.api.put('/sample/' + sample._id,
|
||||||
{condition: pick(sample.condition,
|
{condition: pick(sample.condition,
|
||||||
['condition_template', ...this.d.id.conditionTemplates[sample.condition.condition_template].parameters.map(e => e.name)]
|
[
|
||||||
|
'condition_template',
|
||||||
|
...this.d.id.conditionTemplates[sample.condition.condition_template].parameters.map(e => e.name)
|
||||||
|
]
|
||||||
)}
|
)}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -439,7 +441,8 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
else { // no matching material found
|
else { // no matching material found
|
||||||
if (this.baseSample.material_id !== null) { // reset previous match
|
if (this.baseSample.material_id !== null) { // reset previous match
|
||||||
this.material = new MaterialModel();
|
this.material = new MaterialModel();
|
||||||
this.material.properties.material_template = this.d.latest.materialTemplates.find(e => e.name === 'plastic')._id;
|
this.material.properties.material_template =
|
||||||
|
this.d.latest.materialTemplates.find(e => e.name === 'plastic')._id;
|
||||||
}
|
}
|
||||||
this.baseSample.material_id = null;
|
this.baseSample.material_id = null;
|
||||||
}
|
}
|
||||||
@ -450,8 +453,8 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
setNewMaterial(value = null) {
|
setNewMaterial(value = null) {
|
||||||
if (value === null) { // toggle dialog
|
if (value === null) { // toggle dialog
|
||||||
this.newMaterial = !this.baseSample.material_id;
|
this.newMaterial = !this.baseSample.material_id;
|
||||||
}
|
} // set to false only if material already exists
|
||||||
else if (value || (!value && this.baseSample.material_id !== null )) { // set to false only if material already exists
|
else if (value || (!value && this.baseSample.material_id !== null )) {
|
||||||
this.newMaterial = value;
|
this.newMaterial = value;
|
||||||
}
|
}
|
||||||
if (this.newMaterial) { // set validators if dialog is open
|
if (this.newMaterial) { // set validators if dialog is open
|
||||||
@ -476,7 +479,7 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove the measurement at the specified index
|
// remove the measurement at the specified index
|
||||||
removeMeasurement(gIndex, mIndex) { // TODO: do not delete directly but only after confirmation
|
removeMeasurement(gIndex, mIndex) {
|
||||||
if (this.samples[gIndex].measurements[mIndex]._id !== null) {
|
if (this.samples[gIndex].measurements[mIndex]._id !== null) {
|
||||||
this.measurementDeleteList.push(this.samples[gIndex].measurements[mIndex]._id);
|
this.measurementDeleteList.push(this.samples[gIndex].measurements[mIndex]._id);
|
||||||
}
|
}
|
||||||
@ -490,7 +493,7 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
this.samples[gIndex].measurements[mIndex].values = {};
|
this.samples[gIndex].measurements[mIndex].values = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
fileToArray(files, gIndex, mIndex, parameter) {
|
fileToArray(files, gIndex, mIndex, parameter) { // process spectrum file input
|
||||||
for (const i in files) {
|
for (const i in files) {
|
||||||
if (files.hasOwnProperty(i)) {
|
if (files.hasOwnProperty(i)) {
|
||||||
const fileReader = new FileReader();
|
const fileReader = new FileReader();
|
||||||
@ -500,6 +503,7 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
this.addMeasurement(gIndex);
|
this.addMeasurement(gIndex);
|
||||||
index = this.samples[gIndex].measurements.length - 1;
|
index = this.samples[gIndex].measurements.length - 1;
|
||||||
}
|
}
|
||||||
|
// autofill further parameters
|
||||||
this.samples[gIndex].measurements[index].values.device =
|
this.samples[gIndex].measurements[index].values.device =
|
||||||
this.samples[gIndex].measurements[mIndex].values.device;
|
this.samples[gIndex].measurements[mIndex].values.device;
|
||||||
this.samples[gIndex].measurements[index].values.filename = files[i].name;
|
this.samples[gIndex].measurements[index].values.filename = files[i].name;
|
||||||
@ -578,12 +582,13 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
this.sampleReferenceIdFind(value);
|
this.sampleReferenceIdFind(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleReferenceList(value) {
|
sampleReferenceList(value) { // get list of sample reference number suggestions
|
||||||
return new Observable(observer => {
|
return new Observable(observer => {
|
||||||
if (value !== '') {
|
if (value !== '') {
|
||||||
this.api.get<{ _id: string, number: string }[]>(
|
this.api.get<{ _id: string, number: string }[]>(
|
||||||
'/samples?status[]=validated&status[]=new&page-size=25&sort=number-asc&fields[]=number&fields[]=_id&' +
|
'/samples?status[]=validated&status[]=new&page-size=25&sort=number-asc&fields[]=number&fields[]=_id&' +
|
||||||
'filters[]=%7B%22mode%22%3A%22stringin%22%2C%22field%22%3A%22number%22%2C%22values%22%3A%5B%22' + value + '%22%5D%7D', data => {
|
'filters[]=%7B%22mode%22%3A%22stringin%22%2C%22field%22%3A%22number%22%2C%22values%22%3A%5B%22' + value +
|
||||||
|
'%22%5D%7D', data => {
|
||||||
this.sampleReferenceAutocomplete[this.currentSRIndex] = data.map(e => e.number);
|
this.sampleReferenceAutocomplete[this.currentSRIndex] = data.map(e => e.number);
|
||||||
this.sampleReferenceFinds = data;
|
this.sampleReferenceFinds = data;
|
||||||
observer.next(data.map(e => e.number));
|
observer.next(data.map(e => e.number));
|
||||||
@ -598,7 +603,7 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleReferenceIdFind(value) {
|
sampleReferenceIdFind(value) { // sample reference id from number
|
||||||
const idFind = this.sampleReferenceFinds.find(e => e.number === value);
|
const idFind = this.sampleReferenceFinds.find(e => e.number === value);
|
||||||
if (idFind) {
|
if (idFind) {
|
||||||
this.sampleReferences[this.currentSRIndex][2] = idFind._id;
|
this.sampleReferences[this.currentSRIndex][2] = idFind._id;
|
||||||
@ -626,11 +631,3 @@ export class SampleComponent implements OnInit, AfterContentChecked {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 1. ngAfterViewInit wird ja jedes mal nach einem ngOnChanges aufgerufen, also zB wenn sich dein ngFor aufbaut. Du könntest also in der
|
|
||||||
// Methode prüfen, ob die Daten schon da sind und dann dementsprechend handeln. Das wäre die Eleganteste Variante
|
|
||||||
// 2. Der state "dirty" soll eigentlich anzeigen, wenn ein Form-Field vom User geändert wurde; damit missbrauchst du es hier etwas
|
|
||||||
// 3. Die Dirty-Variante: Pack in deine ngFor ein {{ onFirstLoad(data) }} rein, das einfach ausgeführt wird. müsstest dann natürlich
|
|
||||||
// abfangen, dass das nicht nach jedem view-cycle neu getriggert wird. Schön ist das nicht, aber besser als mit Timeouts^^
|
|
||||||
|
@ -106,18 +106,6 @@ rb-table {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sort-active-asc {
|
|
||||||
color: $color-bosch-dark-blue;
|
|
||||||
background: linear-gradient(to bottom, #FFF 17%, $color-bosch-light-blue-w50 17%);;
|
|
||||||
border-radius: 0 0 8px 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sort-active-desc {
|
|
||||||
color: $color-bosch-dark-blue;
|
|
||||||
background: linear-gradient(to top, #FFF 17%, $color-bosch-light-blue-w50 17%);;
|
|
||||||
border-radius: 8px 8px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filters:after {
|
.filters:after {
|
||||||
content:"";
|
content:"";
|
||||||
clear:both;
|
clear:both;
|
||||||
@ -202,11 +190,6 @@ textarea.linkmodal {
|
|||||||
|
|
||||||
.sample-details-table {
|
.sample-details-table {
|
||||||
|
|
||||||
::ng-deep .table-wrapper {
|
|
||||||
max-height: 80vh;
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
td {
|
||||||
max-width: none;
|
max-width: none;
|
||||||
}
|
}
|
||||||
|
@ -1,77 +1,5 @@
|
|||||||
// import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
//
|
|
||||||
// import { SamplesComponent } from './samples.component';
|
describe('SamplesComponent', () => {
|
||||||
// import {ApiService} from '../services/api.service';
|
|
||||||
// import {AutocompleteService} from '../services/autocomplete.service';
|
});
|
||||||
// import {DataService} from '../services/data.service';
|
|
||||||
// import {LoginService} from '../services/login.service';
|
|
||||||
// import {LocalStorageService} from 'angular-2-local-storage';
|
|
||||||
// import {ModalService, RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
|
|
||||||
// import {ValidationService} from '../services/validation.service';
|
|
||||||
// import {RbCustomInputsModule} from '../rb-custom-inputs/rb-custom-inputs.module';
|
|
||||||
// import {FormsModule} from '@angular/forms';
|
|
||||||
//
|
|
||||||
// // TODO
|
|
||||||
//
|
|
||||||
// let apiServiceSpy: jasmine.SpyObj<ApiService>;
|
|
||||||
// let autocompleteServiceSpy: jasmine.SpyObj<AutocompleteService>;
|
|
||||||
// let modalServiceSpy: jasmine.SpyObj<ModalService>;
|
|
||||||
// let dataServiceSpy: jasmine.SpyObj<DataService>;
|
|
||||||
// let loginServiceSpy: jasmine.SpyObj<LoginService>;
|
|
||||||
// let localStorageServiceSpy: jasmine.SpyObj<LocalStorageService>;
|
|
||||||
// let windowServiceSpy: jasmine.SpyObj<Window>;
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// describe('SamplesComponent', () => {
|
|
||||||
// let component: SamplesComponent;
|
|
||||||
// let fixture: ComponentFixture<SamplesComponent>;
|
|
||||||
//
|
|
||||||
// beforeEach(async(() => {
|
|
||||||
// const apiSpy = jasmine.createSpyObj('ApiService', ['post', 'put']);
|
|
||||||
// const autocompleteSpy = jasmine.createSpyObj('AutocompleteService', ['bind']);
|
|
||||||
// const loginSpy = jasmine.createSpyObj('LoginService', ['login', 'isLevel']);
|
|
||||||
// const modalSpy = jasmine.createSpyObj('ModalService', ['open']);
|
|
||||||
// const dataSpy = jasmine.createSpyObj('DataService', ['load', 'idReload']);
|
|
||||||
// const localStorageSpy = jasmine.createSpyObj('LocalStorageService', ['set', 'remove']);
|
|
||||||
// const windowSpy = jasmine.createSpyObj('Window', ['location']);
|
|
||||||
//
|
|
||||||
// TestBed.configureTestingModule({
|
|
||||||
// declarations: [ SamplesComponent ],
|
|
||||||
// imports: [
|
|
||||||
// RbUiComponentsModule,
|
|
||||||
// RbCustomInputsModule,
|
|
||||||
// FormsModule
|
|
||||||
// ],
|
|
||||||
// providers: [
|
|
||||||
// {provide: ApiService, useValue: apiSpy},
|
|
||||||
// {provide: AutocompleteService, useValue: autocompleteSpy},
|
|
||||||
// {provide: ModalService, useValue: modalSpy},
|
|
||||||
// {provide: DataService, useValue: dataSpy},
|
|
||||||
// {provide: LoginService, useValue: loginSpy},
|
|
||||||
// {provide: LocalStorageService, useValue: localStorageSpy},
|
|
||||||
// {provide: Window, useValue: windowSpy}
|
|
||||||
// ]
|
|
||||||
// })
|
|
||||||
// .compileComponents();
|
|
||||||
//
|
|
||||||
// apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
|
|
||||||
// autocompleteServiceSpy = TestBed.inject(AutocompleteService) as jasmine.SpyObj<AutocompleteService>;
|
|
||||||
// modalServiceSpy = TestBed.inject(ModalService) as jasmine.SpyObj<ModalService>;
|
|
||||||
// dataServiceSpy = TestBed.inject(DataService) as jasmine.SpyObj<DataService>;
|
|
||||||
// loginServiceSpy = TestBed.inject(LoginService) as jasmine.SpyObj<LoginService>;
|
|
||||||
// localStorageServiceSpy = TestBed.inject(LocalStorageService) as jasmine.SpyObj<LocalStorageService>;
|
|
||||||
// windowServiceSpy = TestBed.inject(Window) as jasmine.SpyObj<Window>;
|
|
||||||
// }));
|
|
||||||
//
|
|
||||||
// beforeEach(() => {
|
|
||||||
// fixture = TestBed.createComponent(SamplesComponent);
|
|
||||||
// component = fixture.componentInstance;
|
|
||||||
// component.ngOnInit();
|
|
||||||
// fixture.detectChanges();
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should create', () => {
|
|
||||||
// expect(component).toBeTruthy();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
@ -11,11 +11,6 @@ import {DataService} from '../services/data.service';
|
|||||||
import {LocalStorageService} from 'angular-2-local-storage';
|
import {LocalStorageService} from 'angular-2-local-storage';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
|
|
||||||
// TODO: turn off sort field
|
|
||||||
// TODO reset sort when field is excluded
|
|
||||||
// TODO: Eh DPT
|
|
||||||
// TODO: filter button
|
|
||||||
// TODO: check if connect-src to model works
|
|
||||||
|
|
||||||
|
|
||||||
interface LoadSamplesOptions {
|
interface LoadSamplesOptions {
|
||||||
@ -41,10 +36,10 @@ export class SamplesComponent implements OnInit {
|
|||||||
@ViewChild('pageSizeSelection') pageSizeSelection: ElementRef<HTMLElement>;
|
@ViewChild('pageSizeSelection') pageSizeSelection: ElementRef<HTMLElement>;
|
||||||
@ViewChild('linkarea') linkarea: ElementRef<HTMLTextAreaElement>;
|
@ViewChild('linkarea') linkarea: ElementRef<HTMLTextAreaElement>;
|
||||||
|
|
||||||
downloadSpectra = false; // TODO: streamline these options after csv option handling is clear
|
downloadSpectra = false; // download options
|
||||||
downloadCondition = false;
|
downloadCondition = false;
|
||||||
downloadFlatten = true;
|
downloadFlatten = true;
|
||||||
samples: SampleModel[] = [];
|
samples: SampleModel[] = []; // all samples to display
|
||||||
totalSamples = 0; // total number of samples
|
totalSamples = 0; // total number of samples
|
||||||
csvUrl = ''; // store url separate so it only has to be generated when clicking the download button
|
csvUrl = ''; // store url separate so it only has to be generated when clicking the download button
|
||||||
filters = {
|
filters = {
|
||||||
@ -68,8 +63,8 @@ export class SamplesComponent implements OnInit {
|
|||||||
{field: 'added', label: 'Added', active: false, autocomplete: [], mode: 'eq', values: ['']}
|
{field: 'added', label: 'Added', active: false, autocomplete: [], mode: 'eq', values: ['']}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
page = 1;
|
page = 1; // current page
|
||||||
pages = 1;
|
pages = 1; // total number of pages
|
||||||
loadSamplesQueue = []; // arguments of queued up loadSamples() calls
|
loadSamplesQueue = []; // arguments of queued up loadSamples() calls
|
||||||
keys: KeyInterface[] = [
|
keys: KeyInterface[] = [
|
||||||
{id: 'number', label: 'Number', active: true, sortable: true},
|
{id: 'number', label: 'Number', active: true, sortable: true},
|
||||||
@ -85,12 +80,12 @@ export class SamplesComponent implements OnInit {
|
|||||||
{id: 'status', label: 'Status', active: false, sortable: true},
|
{id: 'status', label: 'Status', active: false, sortable: true},
|
||||||
{id: 'added', label: 'Added', active: true, sortable: true}
|
{id: 'added', label: 'Added', active: true, sortable: true}
|
||||||
];
|
];
|
||||||
isActiveKey: {[key: string]: boolean} = {};
|
isActiveKey: {[key: string]: boolean} = {}; // object to check if key is currently active
|
||||||
activeKeys: KeyInterface[] = [];
|
activeKeys: KeyInterface[] = []; // list of active keys
|
||||||
activeTemplateKeys = {material: [], condition: [], measurements: []};
|
activeTemplateKeys = {material: [], condition: [], measurements: []};
|
||||||
sampleDetailsSample: any = null;
|
sampleDetailsSample: any = null; // sample for the sample details dialog
|
||||||
sampleSelect = 0; // modes: 0 - no selection, 1 - sample edit selection, 2 - validation selection
|
sampleSelect = 0; // modes: 0 - no selection, 1 - sample edit selection, 2 - validation selection
|
||||||
loading = 0;
|
loading = 0; // number of loading instances
|
||||||
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -170,7 +165,8 @@ export class SamplesComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadSamples(options: LoadSamplesOptions = {}, event = null) { // set toPage to null to reload first page, queues calls
|
// set toPage to null to reload first page, queues calls
|
||||||
|
loadSamples(options: LoadSamplesOptions = {}, event = null) {
|
||||||
if (event) { // adjust active keys
|
if (event) { // adjust active keys
|
||||||
this.keys.forEach(key => {
|
this.keys.forEach(key => {
|
||||||
if (event.hasOwnProperty(key.id)) {
|
if (event.hasOwnProperty(key.id)) {
|
||||||
@ -194,7 +190,7 @@ export class SamplesComponent implements OnInit {
|
|||||||
this.loading ++;
|
this.loading ++;
|
||||||
this.api.get(this.sampleUrl({paging: true, pagingOptions: options}), (sData, err, headers) => {
|
this.api.get(this.sampleUrl({paging: true, pagingOptions: options}), (sData, err, headers) => {
|
||||||
this.loading --;
|
this.loading --;
|
||||||
if (err) {
|
if (err) { // remove stored options on error to avoid loop
|
||||||
this.storage.remove('samplesPreferences');
|
this.storage.remove('samplesPreferences');
|
||||||
this.api.requestError(err);
|
this.api.requestError(err);
|
||||||
}
|
}
|
||||||
@ -223,7 +219,8 @@ export class SamplesComponent implements OnInit {
|
|||||||
export?: boolean,
|
export?: boolean,
|
||||||
host?: boolean
|
host?: boolean
|
||||||
}) { // return url to fetch samples
|
}) { // return url to fetch samples
|
||||||
const additionalTableKeys = ['material_id', '_id', 'user_id']; // keys which should always be added if export = false
|
// keys which should always be added if export = false
|
||||||
|
const additionalTableKeys = ['material_id', '_id', 'user_id'];
|
||||||
const query: string[] = [];
|
const query: string[] = [];
|
||||||
query.push(...Object.keys(this.filters.status).filter(e => this.filters.status[e]).map(e => 'status[]=' + e));
|
query.push(...Object.keys(this.filters.status).filter(e => this.filters.status[e]).map(e => 'status[]=' + e));
|
||||||
if (options.paging) {
|
if (options.paging) {
|
||||||
@ -241,7 +238,7 @@ export class SamplesComponent implements OnInit {
|
|||||||
query.push('page-size=' + this.filters.pageSize);
|
query.push('page-size=' + this.filters.pageSize);
|
||||||
}
|
}
|
||||||
query.push('sort=' + this.filters.sort);
|
query.push('sort=' + this.filters.sort);
|
||||||
if (options.export) {
|
if (options.export) { // append API key on export
|
||||||
query.push('key=' + this.d.d.userKey.key);
|
query.push('key=' + this.d.d.userKey.key);
|
||||||
}
|
}
|
||||||
this.keys.forEach(key => {
|
this.keys.forEach(key => {
|
||||||
@ -255,9 +252,11 @@ export class SamplesComponent implements OnInit {
|
|||||||
.map(e => {
|
.map(e => {
|
||||||
e.values = e.values.filter(el => el !== ''); // do not include empty values
|
e.values = e.values.filter(el => el !== ''); // do not include empty values
|
||||||
if (e.field === 'added') { // correct timezone
|
if (e.field === 'added') { // correct timezone
|
||||||
e.values = e.values.map(el => new Date(new Date(el).getTime() - new Date(el).getTimezoneOffset() * 60000).toISOString());
|
e.values = e.values.map(
|
||||||
|
el => new Date(new Date(el).getTime() - new Date(el).getTimezoneOffset() * 60000).toISOString()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (e.mode === 'null') {
|
if (e.mode === 'null') { // handle null mode
|
||||||
e.mode = 'in';
|
e.mode = 'in';
|
||||||
e.values = [null, ''];
|
e.values = [null, ''];
|
||||||
}
|
}
|
||||||
@ -270,7 +269,8 @@ export class SamplesComponent implements OnInit {
|
|||||||
query.push('filters[]=' + encodeURIComponent( JSON.stringify({mode: 'eq', field: 'condition', values: [{}]})));
|
query.push('filters[]=' + encodeURIComponent( JSON.stringify({mode: 'eq', field: 'condition', values: [{}]})));
|
||||||
}
|
}
|
||||||
if (this.filters.no.measurements) {
|
if (this.filters.no.measurements) {
|
||||||
query.push('filters[]=' + encodeURIComponent( JSON.stringify( {mode: 'eq', field: 'measurements', values: [null]})));
|
query.push('filters[]=' +
|
||||||
|
encodeURIComponent( JSON.stringify( {mode: 'eq', field: 'measurements', values: [null]})));
|
||||||
}
|
}
|
||||||
if (!options.export) {
|
if (!options.export) {
|
||||||
additionalTableKeys.forEach(key => {
|
additionalTableKeys.forEach(key => {
|
||||||
@ -279,7 +279,7 @@ export class SamplesComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else { // export options
|
||||||
if (options.csv) {
|
if (options.csv) {
|
||||||
query.push('output=csv');
|
query.push('output=csv');
|
||||||
}
|
}
|
||||||
@ -338,7 +338,6 @@ export class SamplesComponent implements OnInit {
|
|||||||
this.updateActiveKeys();
|
this.updateActiveKeys();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: avoid reloading
|
|
||||||
resetPreferences() {
|
resetPreferences() {
|
||||||
this.storage.remove('samplesPreferences');
|
this.storage.remove('samplesPreferences');
|
||||||
this.window.location.reload();
|
this.window.location.reload();
|
||||||
@ -346,7 +345,6 @@ export class SamplesComponent implements OnInit {
|
|||||||
|
|
||||||
updateFilterFields(field) {
|
updateFilterFields(field) {
|
||||||
const filter = this.filters.filters.find(e => e.field === field);
|
const filter = this.filters.filters.find(e => e.field === field);
|
||||||
console.log(filter);
|
|
||||||
filter.active = !(filter.values.length === 1 && filter.values[0] === '');
|
filter.active = !(filter.values.length === 1 && filter.values[0] === '');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,20 +378,24 @@ export class SamplesComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleDetails(id: string, modal: TemplateRef<any>) {
|
sampleDetails(id: string, modal: TemplateRef<any>) { // show sample details
|
||||||
this.sampleDetailsSample = null;
|
this.sampleDetailsSample = null;
|
||||||
this.api.get<SampleModel>('/sample/' + id, data => {
|
this.api.get<SampleModel>('/sample/' + id, data => {
|
||||||
this.sampleDetailsSample = new SampleModel().deserialize(data);
|
this.sampleDetailsSample = new SampleModel().deserialize(data);
|
||||||
if (data.notes.custom_fields) { // convert custom_fields for more optimized display
|
if (data.notes.custom_fields) { // convert custom_fields for more optimized display
|
||||||
this.sampleDetailsSample.notes.custom_fields_entries = Object.entries(this.sampleDetailsSample.notes.custom_fields);
|
this.sampleDetailsSample.notes.custom_fields_entries =
|
||||||
|
Object.entries(this.sampleDetailsSample.notes.custom_fields);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.sampleDetailsSample.custom_fields_entries = [];
|
this.sampleDetailsSample.custom_fields_entries = [];
|
||||||
}
|
}
|
||||||
if (Object.keys(data.condition).length) {
|
if (Object.keys(data.condition).length) { // convert condition
|
||||||
this.sampleDetailsSample.condition_entries = Object.entries(omit(this.sampleDetailsSample.condition, ['condition_template']))
|
this.sampleDetailsSample.condition_entries =
|
||||||
|
Object.entries(omit(this.sampleDetailsSample.condition, ['condition_template']))
|
||||||
.map(e => {
|
.map(e => {
|
||||||
e[0] = `${this.ucFirst(this.d.id.conditionTemplates[this.sampleDetailsSample.condition.condition_template].name)} ${e[0]}`;
|
e[0] = `${this.ucFirst(
|
||||||
|
this.d.id.conditionTemplates[this.sampleDetailsSample.condition.condition_template].name
|
||||||
|
)} ${e[0]}`;
|
||||||
return e;
|
return e;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -401,9 +403,11 @@ export class SamplesComponent implements OnInit {
|
|||||||
this.sampleDetailsSample.condition_entries = [];
|
this.sampleDetailsSample.condition_entries = [];
|
||||||
}
|
}
|
||||||
this.sampleDetailsSample.measurement_entries = [];
|
this.sampleDetailsSample.measurement_entries = [];
|
||||||
this.sampleDetailsSample.measurements.forEach(measurement => { // convert measurements for more optimized display without dpt
|
// convert measurements for more optimized display without dpt
|
||||||
|
this.sampleDetailsSample.measurements.forEach(measurement => {
|
||||||
const name = this.d.id.measurementTemplates[measurement.measurement_template].name;
|
const name = this.d.id.measurementTemplates[measurement.measurement_template].name;
|
||||||
this.sampleDetailsSample.measurement_entries.push(...Object.entries(measurement.values).filter(e => e[0] !== 'dpt')
|
this.sampleDetailsSample.measurement_entries
|
||||||
|
.push(...Object.entries(measurement.values).filter(e => e[0] !== 'dpt')
|
||||||
.map(e => ({name: this.ucFirst(name) + ' ' + e[0], value: e[1]})));
|
.map(e => ({name: this.ucFirst(name) + ' ' + e[0], value: e[1]})));
|
||||||
});
|
});
|
||||||
new Promise(resolve => {
|
new Promise(resolve => {
|
||||||
@ -429,7 +433,7 @@ export class SamplesComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validate() {
|
validate() {
|
||||||
if (this.sampleSelect) {
|
if (this.sampleSelect) { // do actual validation
|
||||||
this.samples.forEach(sample => {
|
this.samples.forEach(sample => {
|
||||||
if (sample.selected) {
|
if (sample.selected) {
|
||||||
this.api.put('/sample/validate/' + sample._id);
|
this.api.put('/sample/validate/' + sample._id);
|
||||||
@ -438,12 +442,12 @@ export class SamplesComponent implements OnInit {
|
|||||||
this.loadSamples();
|
this.loadSamples();
|
||||||
this.sampleSelect = 0;
|
this.sampleSelect = 0;
|
||||||
}
|
}
|
||||||
else {
|
else { // get into validation mode
|
||||||
this.sampleSelect = 2;
|
this.sampleSelect = 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
batchEdit() {
|
batchEdit() { // redirect to batch edit
|
||||||
if (this.sampleSelect) {
|
if (this.sampleSelect) {
|
||||||
this.router.navigate(['/samples/edit/' + this.samples.filter(e => e.selected).map(e => e._id).join(',')]);
|
this.router.navigate(['/samples/edit/' + this.samples.filter(e => e.selected).map(e => e._id).join(',')]);
|
||||||
this.sampleSelect = 0;
|
this.sampleSelect = 0;
|
||||||
@ -464,7 +468,7 @@ export class SamplesComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
selectAll(event) {
|
selectAll(event) { // toggle select all except deleted samples
|
||||||
this.samples.forEach(sample => {
|
this.samples.forEach(sample => {
|
||||||
if (sample.status !== 'deleted') {
|
if (sample.status !== 'deleted') {
|
||||||
sample.selected = event.target.checked;
|
sample.selected = event.target.checked;
|
||||||
@ -485,13 +489,13 @@ export class SamplesComponent implements OnInit {
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
clipboard() {
|
clipboard() { // copy contents to clipboard
|
||||||
this.linkarea.nativeElement.select();
|
this.linkarea.nativeElement.select();
|
||||||
this.linkarea.nativeElement.setSelectionRange(0, 99999);
|
this.linkarea.nativeElement.setSelectionRange(0, 99999);
|
||||||
document.execCommand('copy');
|
document.execCommand('copy');
|
||||||
}
|
}
|
||||||
|
|
||||||
ucFirst(string) {
|
ucFirst(string) { // convert first character of string to uppercase
|
||||||
return string[0].toUpperCase() + string.slice(1);
|
return string[0].toUpperCase() + string.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,143 +1,5 @@
|
|||||||
import {async, TestBed} from '@angular/core/testing';
|
import {async, TestBed} from '@angular/core/testing';
|
||||||
import { ApiService } from './api.service';
|
|
||||||
import {HttpClient} from '@angular/common/http';
|
|
||||||
import {LocalStorageService} from 'angular-2-local-storage';
|
|
||||||
import {Observable} from 'rxjs';
|
|
||||||
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
|
|
||||||
|
|
||||||
let apiService: ApiService;
|
|
||||||
let httpClientSpy: jasmine.SpyObj<HttpClient>;
|
|
||||||
let localStorageServiceSpy: jasmine.SpyObj<LocalStorageService>;
|
|
||||||
let modalServiceSpy: jasmine.SpyObj<ModalService>;
|
|
||||||
let windowServiceSpy: jasmine.SpyObj<Window>;
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
// TODO: test options
|
|
||||||
|
|
||||||
describe('ApiService', () => {
|
describe('ApiService', () => {
|
||||||
beforeEach(() => {
|
|
||||||
const httpSpy = jasmine.createSpyObj('HttpClient', ['get', 'post', 'put', 'delete']);
|
|
||||||
const localStorageSpy = jasmine.createSpyObj('LocalStorageService', ['get']);
|
|
||||||
const modalSpy = jasmine.createSpyObj('ModalService', ['openComponent']);
|
|
||||||
const windowSpy = jasmine.createSpyObj('Window', ['location']);
|
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
providers: [
|
|
||||||
ApiService,
|
|
||||||
{provide: HttpClient, useValue: httpSpy},
|
|
||||||
{provide: LocalStorageService, useValue: localStorageSpy},
|
|
||||||
{provide: ModalService, useValue: modalSpy},
|
|
||||||
{provide: Window, useValue: windowSpy}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
apiService = TestBed.inject(ApiService);
|
|
||||||
httpClientSpy = TestBed.inject(HttpClient) as jasmine.SpyObj<HttpClient>;
|
|
||||||
localStorageServiceSpy = TestBed.inject(LocalStorageService) as jasmine.SpyObj<LocalStorageService>;
|
|
||||||
modalServiceSpy = TestBed.inject(ModalService) as jasmine.SpyObj<ModalService>;
|
|
||||||
windowServiceSpy = TestBed.inject(Window) as jasmine.SpyObj<Window>;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be created', () => {
|
|
||||||
expect(apiService).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('shows an error message when the request fails', () => {
|
|
||||||
const getReturn = new Observable(observer => {
|
|
||||||
observer.error('error');
|
|
||||||
});
|
|
||||||
httpClientSpy.get.and.returnValue(getReturn);
|
|
||||||
localStorageServiceSpy.get.and.returnValue(undefined);
|
|
||||||
modalServiceSpy.openComponent.and.returnValue({instance: {message: ''}} as any);
|
|
||||||
|
|
||||||
apiService.get('/testurl');
|
|
||||||
expect(httpClientSpy.get).toHaveBeenCalledWith('/api/testurl', jasmine.any(Object));
|
|
||||||
expect(modalServiceSpy.openComponent.calls.count()).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns the error message if the callback function had an error parameter', () => {
|
|
||||||
const getReturn = new Observable(observer => {
|
|
||||||
observer.error('error');
|
|
||||||
});
|
|
||||||
httpClientSpy.get.and.returnValue(getReturn);
|
|
||||||
localStorageServiceSpy.get.and.returnValue(undefined);
|
|
||||||
modalServiceSpy.openComponent.and.returnValue({instance: {message: ''}} as any);
|
|
||||||
|
|
||||||
apiService.get('/testurl', (data, error) => {
|
|
||||||
expect(modalServiceSpy.openComponent.calls.count()).toBe(0);
|
|
||||||
expect(error).toBe('error');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should do get requests without auth if not available', async(() => {
|
|
||||||
const getReturn = new Observable(observer => {
|
|
||||||
observer.next({body: 'data', headers: {keys: () => []}});
|
|
||||||
});
|
|
||||||
httpClientSpy.get.and.returnValue(getReturn);
|
|
||||||
localStorageServiceSpy.get.and.returnValue(undefined);
|
|
||||||
|
|
||||||
apiService.get('/testurl', res => {
|
|
||||||
expect(res).toBe('data');
|
|
||||||
expect(httpClientSpy.get).toHaveBeenCalledWith('/api/testurl', jasmine.any(Object));
|
|
||||||
expect(localStorageServiceSpy.get).toHaveBeenCalledWith('basicAuth');
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should do get requests with basic auth if available', async(() => {
|
|
||||||
const getReturn = new Observable(observer => {
|
|
||||||
observer.next('data');
|
|
||||||
});
|
|
||||||
httpClientSpy.get.and.returnValue(getReturn);
|
|
||||||
localStorageServiceSpy.get.and.returnValue('basicAuth');
|
|
||||||
|
|
||||||
apiService.get('/testurl', res => {
|
|
||||||
expect(res).toBe('data');
|
|
||||||
expect(httpClientSpy.get).toHaveBeenCalledWith('/api/testurl', jasmine.any(Object));
|
|
||||||
expect(localStorageServiceSpy.get).toHaveBeenCalledWith('basicAuth');
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should do post requests', async(() => {
|
|
||||||
const resReturn = new Observable(observer => {
|
|
||||||
observer.next('data');
|
|
||||||
});
|
|
||||||
httpClientSpy.post.and.returnValue(resReturn);
|
|
||||||
localStorageServiceSpy.get.and.returnValue('basicAuth');
|
|
||||||
|
|
||||||
apiService.post('/testurl', 'reqData', res => {
|
|
||||||
expect(res).toBe('data');
|
|
||||||
expect(httpClientSpy.post).toHaveBeenCalledWith('/api/testurl', 'reqData', jasmine.any(Object));
|
|
||||||
expect(localStorageServiceSpy.get).toHaveBeenCalledWith('basicAuth');
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should do put requests', async(() => {
|
|
||||||
const resReturn = new Observable(observer => {
|
|
||||||
observer.next('data');
|
|
||||||
});
|
|
||||||
httpClientSpy.put.and.returnValue(resReturn);
|
|
||||||
localStorageServiceSpy.get.and.returnValue('basicAuth');
|
|
||||||
|
|
||||||
apiService.put('/testurl', 'reqData', res => {
|
|
||||||
expect(res).toBe('data');
|
|
||||||
expect(httpClientSpy.put).toHaveBeenCalledWith('/api/testurl', 'reqData', jasmine.any(Object));
|
|
||||||
expect(localStorageServiceSpy.get).toHaveBeenCalledWith('basicAuth');
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should do delete requests', async(() => {
|
|
||||||
const resReturn = new Observable(observer => {
|
|
||||||
observer.next('data');
|
|
||||||
});
|
|
||||||
httpClientSpy.delete.and.returnValue(resReturn);
|
|
||||||
localStorageServiceSpy.get.and.returnValue('basicAuth');
|
|
||||||
|
|
||||||
apiService.delete('/testurl', res => {
|
|
||||||
expect(res).toBe('data');
|
|
||||||
expect(httpClientSpy.delete).toHaveBeenCalledWith('/api/testurl', jasmine.any(Object));
|
|
||||||
expect(localStorageServiceSpy.get).toHaveBeenCalledWith('basicAuth');
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
// TODO: test return headers
|
|
||||||
});
|
});
|
||||||
|
@ -5,7 +5,6 @@ import {Observable} from 'rxjs';
|
|||||||
import {ErrorComponent} from '../error/error.component';
|
import {ErrorComponent} from '../error/error.component';
|
||||||
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
|
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
|
||||||
|
|
||||||
// TODO: find solution when client wants to visit subpage but is not logged in to redirect to login without showing request failed errors
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -25,6 +24,7 @@ export class ApiService {
|
|||||||
return this.host;
|
return this.host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// main HTTP methods
|
||||||
get<T>(url, f: (data?: T, err?, headers?) => void = () => {}) {
|
get<T>(url, f: (data?: T, err?, headers?) => void = () => {}) {
|
||||||
this.requestErrorHandler<T>(this.http.get(this.url(url), this.options()), f);
|
this.requestErrorHandler<T>(this.http.get(this.url(url), this.options()), f);
|
||||||
}
|
}
|
||||||
@ -41,20 +41,25 @@ export class ApiService {
|
|||||||
this.requestErrorHandler<T>(this.http.delete(this.url(url), this.options()), f);
|
this.requestErrorHandler<T>(this.http.delete(this.url(url), this.options()), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// execute request and handle errors
|
||||||
private requestErrorHandler<T>(observable: Observable<any>, f: (data?: T, err?, headers?) => void) {
|
private requestErrorHandler<T>(observable: Observable<any>, f: (data?: T, err?, headers?) => void) {
|
||||||
observable.subscribe(data => {
|
observable.subscribe(data => { // successful request
|
||||||
f(data.body, undefined, data.headers.keys().reduce((s, e) => {s[e.toLowerCase()] = data.headers.get(e); return s; }, {}));
|
f(
|
||||||
}, err => {
|
data.body,
|
||||||
if (f.length > 1) {
|
undefined,
|
||||||
|
data.headers.keys().reduce((s, e) => {s[e.toLowerCase()] = data.headers.get(e); return s; }, {})
|
||||||
|
);
|
||||||
|
}, err => { // error
|
||||||
|
if (f.length > 1) { // pass on error
|
||||||
f(undefined, err, undefined);
|
f(undefined, err, undefined);
|
||||||
}
|
}
|
||||||
else {
|
else { // handle directly
|
||||||
this.requestError(err);
|
this.requestError(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
requestError(err) {
|
requestError(err) { // network error dialog
|
||||||
const modalRef = this.modalService.openComponent(ErrorComponent);
|
const modalRef = this.modalService.openComponent(ErrorComponent);
|
||||||
modalRef.instance.message = 'Network request failed!';
|
modalRef.instance.message = 'Network request failed!';
|
||||||
const details = [err.error.status];
|
const details = [err.error.status];
|
||||||
@ -67,7 +72,7 @@ export class ApiService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private url(url) {
|
private url(url) { // detect if host was given, otherwise use default host
|
||||||
if (/http[s]?:\/\//.test(url)) {
|
if (/http[s]?:\/\//.test(url)) {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
@ -76,10 +81,12 @@ export class ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generate request options
|
||||||
private options(): {headers: HttpHeaders, observe: 'body'} {
|
private options(): {headers: HttpHeaders, observe: 'body'} {
|
||||||
return {headers: this.authOptions(), observe: 'response' as 'body'};
|
return {headers: this.authOptions(), observe: 'response' as 'body'};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generate Basic Auth
|
||||||
private authOptions(): HttpHeaders {
|
private authOptions(): HttpHeaders {
|
||||||
const auth = this.storage.get('basicAuth');
|
const auth = this.storage.get('basicAuth');
|
||||||
if (auth) {
|
if (auth) {
|
||||||
|
@ -1,37 +1,5 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { AutocompleteService } from './autocomplete.service';
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
let autocompleteService: AutocompleteService;
|
|
||||||
|
|
||||||
describe('AutocompleteService', () => {
|
describe('AutocompleteService', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
providers: [AutocompleteService]
|
|
||||||
});
|
|
||||||
autocompleteService = TestBed.inject(AutocompleteService);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be created', () => {
|
|
||||||
expect(autocompleteService).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should should return a bind function', () => {
|
|
||||||
expect(autocompleteService.bind('a', ['b'])).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return search results', () => {
|
|
||||||
autocompleteService.search(['aa', 'ab', 'bb'], 'a').subscribe(res => {
|
|
||||||
expect(res).toEqual(['aa', 'ab']);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return an empty array if no result was found', () => {
|
|
||||||
autocompleteService.search(['aa', 'ab', 'bb'], 'c').subscribe(res => {
|
|
||||||
expect(res).toEqual([]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -1,33 +1,5 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { DataService } from './data.service';
|
|
||||||
import {ApiService} from './api.service';
|
|
||||||
import {HttpClient} from '@angular/common/http';
|
|
||||||
import {LocalStorageService} from 'angular-2-local-storage';
|
|
||||||
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
let apiServiceSpy: jasmine.SpyObj<ApiService>;
|
|
||||||
|
|
||||||
|
|
||||||
describe('DataService', () => {
|
describe('DataService', () => {
|
||||||
let service: DataService;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
const apiSpy = jasmine.createSpyObj('ApiService', ['post', 'put']);
|
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
providers: [
|
|
||||||
{provide: ApiService, useValue: apiSpy}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
service = TestBed.inject(DataService);
|
|
||||||
|
|
||||||
apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be created', () => {
|
|
||||||
expect(service).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -16,7 +16,7 @@ export class DataService {
|
|||||||
private api: ApiService
|
private api: ApiService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
private collectionMap = {
|
private collectionMap = { // list of available collections
|
||||||
materials: {path: '/materials?status[]=validated&status[]=new', model: MaterialModel, type: 'idArray'},
|
materials: {path: '/materials?status[]=validated&status[]=new', model: MaterialModel, type: 'idArray'},
|
||||||
materialSuppliers: {path: '/material/suppliers', model: null, type: 'idArray'},
|
materialSuppliers: {path: '/material/suppliers', model: null, type: 'idArray'},
|
||||||
materialGroups: {path: '/material/groups', model: null, type: 'idArray'},
|
materialGroups: {path: '/material/groups', model: null, type: 'idArray'},
|
||||||
@ -36,7 +36,7 @@ export class DataService {
|
|||||||
id: {[key: string]: {[id: string]: any}} = {}; // data in format _id: data
|
id: {[key: string]: {[id: string]: any}} = {}; // data in format _id: data
|
||||||
d: {[key: string]: any} = {}; // data not in array format
|
d: {[key: string]: any} = {}; // data not in array format
|
||||||
|
|
||||||
contact = {name: 'CR/APS1-Lingenfelser', mail: 'dominic.lingenfelser@bosch.com'};
|
contact = {name: 'CR/APS1-Lingenfelser', mail: 'dominic.lingenfelser@bosch.com'}; // global contact data
|
||||||
|
|
||||||
load(collection, f = () => {}) { // load data
|
load(collection, f = () => {}) { // load data
|
||||||
if (this.arr[collection]) { // data already loaded
|
if (this.arr[collection]) { // data already loaded
|
||||||
@ -46,7 +46,11 @@ export class DataService {
|
|||||||
this.api.get<any>(this.collectionMap[collection].path, data => {
|
this.api.get<any>(this.collectionMap[collection].path, data => {
|
||||||
if (this.collectionMap[collection].type !== 'string') { // array data
|
if (this.collectionMap[collection].type !== 'string') { // array data
|
||||||
this.arr[collection] = data
|
this.arr[collection] = data
|
||||||
.map(e => this.collectionMap[collection].model ? new this.collectionMap[collection].model().deserialize(e) : e);
|
.map(
|
||||||
|
e => this.collectionMap[collection].model ?
|
||||||
|
new this.collectionMap[collection].model().deserialize(e) : e
|
||||||
|
);
|
||||||
|
// load ids
|
||||||
if (this.collectionMap[collection].type === 'idArray' || this.collectionMap[collection].type === 'template') {
|
if (this.collectionMap[collection].type === 'idArray' || this.collectionMap[collection].type === 'template') {
|
||||||
this.idReload(collection);
|
this.idReload(collection);
|
||||||
}
|
}
|
||||||
@ -59,9 +63,10 @@ export class DataService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generate id object
|
||||||
idReload(collection) {
|
idReload(collection) {
|
||||||
this.id[collection] = this.arr[collection].reduce((s, e) => {s[e._id] = e; return s; }, {});
|
this.id[collection] = this.arr[collection].reduce((s, e) => {s[e._id] = e; return s; }, {});
|
||||||
if (this.collectionMap[collection].type === 'template') {
|
if (this.collectionMap[collection].type === 'template') { // generate array with latest templates
|
||||||
const tmpTemplates = {};
|
const tmpTemplates = {};
|
||||||
this.arr[collection].forEach(template => {
|
this.arr[collection].forEach(template => {
|
||||||
if (tmpTemplates[template.first_id]) { // already found another version
|
if (tmpTemplates[template.first_id]) { // already found another version
|
||||||
|
@ -1,95 +1,5 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { LoginService } from './login.service';
|
|
||||||
import {LocalStorageService} from 'angular-2-local-storage';
|
|
||||||
import {ApiService} from './api.service';
|
|
||||||
import {Router} from '@angular/router';
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
let loginService: LoginService;
|
|
||||||
let apiServiceSpy: jasmine.SpyObj<ApiService>;
|
|
||||||
let localStorageServiceSpy: jasmine.SpyObj<LocalStorageService>;
|
|
||||||
let routerServiceSpy: jasmine.SpyObj<Router>;
|
|
||||||
|
|
||||||
describe('LoginService', () => {
|
describe('LoginService', () => {
|
||||||
beforeEach(() => {
|
|
||||||
const apiSpy = jasmine.createSpyObj('ApiService', ['get']);
|
|
||||||
const localStorageSpy = jasmine.createSpyObj('LocalStorageService', ['set', 'remove']);
|
|
||||||
const routerSpy = jasmine.createSpyObj('Router', ['navigate']);
|
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
providers: [
|
|
||||||
LoginService,
|
|
||||||
{provide: ApiService, useValue: apiSpy},
|
|
||||||
{provide: LocalStorageService, useValue: localStorageSpy},
|
|
||||||
{provide: Router, useValue: routerSpy}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
loginService = TestBed.inject(LoginService);
|
|
||||||
apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
|
|
||||||
localStorageServiceSpy = TestBed.inject(LocalStorageService) as jasmine.SpyObj<LocalStorageService>;
|
|
||||||
routerServiceSpy = TestBed.inject(Router) as jasmine.SpyObj<Router>;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be created', () => {
|
|
||||||
expect(loginService).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
// describe('login', () => {
|
|
||||||
// it('should store the basic auth', () => {
|
|
||||||
// localStorageServiceSpy.set.and.returnValue(true);
|
|
||||||
// apiServiceSpy.get.and.callFake(() => {});
|
|
||||||
// loginService.login('username', 'password');
|
|
||||||
// expect(localStorageServiceSpy.set).toHaveBeenCalledWith('basicAuth', 'dXNlcm5hbWU6cGFzc3dvcmQ=');
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should remove the basic auth if login fails', () => {
|
|
||||||
// localStorageServiceSpy.set.and.returnValue(true);
|
|
||||||
// localStorageServiceSpy.remove.and.returnValue(true);
|
|
||||||
// apiServiceSpy.get.and.callFake((a, b) => {b(undefined, 'error'); });
|
|
||||||
// loginService.login('username', 'password');
|
|
||||||
// expect(localStorageServiceSpy.remove.calls.count()).toBe(1);
|
|
||||||
// expect(localStorageServiceSpy.remove).toHaveBeenCalledWith('basicAuth');
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should resolve true when login succeeds', async () => {
|
|
||||||
// localStorageServiceSpy.set.and.returnValue(true);
|
|
||||||
// apiServiceSpy.get.and.callFake((a, b) => {b({status: 'Authorization successful', method: 'basic'} as any, undefined); });
|
|
||||||
// expect(await loginService.login('username', 'password')).toBeTruthy();
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should resolve false when a wrong result comes in', async () => {
|
|
||||||
// localStorageServiceSpy.set.and.returnValue(true);
|
|
||||||
// apiServiceSpy.get.and.callFake((a, b) => {b({status: 'xxx', method: 'basic'} as any, undefined); });
|
|
||||||
// expect(await loginService.login('username', 'password')).toBeFalsy();
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should resolve false on an error', async () => {
|
|
||||||
// localStorageServiceSpy.set.and.returnValue(true);
|
|
||||||
// apiServiceSpy.get.and.callFake((a, b) => {b(undefined, 'error'); });
|
|
||||||
// expect(await loginService.login('username', 'password')).toBeFalsy();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// describe('canActivate', () => {
|
|
||||||
// it('should return false at first', done => {
|
|
||||||
// apiServiceSpy.get.and.callFake((a, b) => {b(undefined, 'error'); });
|
|
||||||
// loginService.canActivate(null, null).subscribe(res => {
|
|
||||||
// expect(res).toBeFalsy();
|
|
||||||
// done();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('returns true if login was successful', async done => {
|
|
||||||
// localStorageServiceSpy.set.and.returnValue(true);
|
|
||||||
// apiServiceSpy.get.and.callFake((a, b) => {b({status: 'Authorization successful', method: 'basic'} as any, undefined); });
|
|
||||||
// await loginService.login('username', 'password');
|
|
||||||
// loginService.canActivate(null, null).subscribe(res => {
|
|
||||||
// expect(res).toBeTruthy();
|
|
||||||
// done();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
});
|
});
|
||||||
|
@ -10,19 +10,20 @@ import {DataService} from './data.service';
|
|||||||
})
|
})
|
||||||
export class LoginService implements CanActivate {
|
export class LoginService implements CanActivate {
|
||||||
|
|
||||||
private pathPermissions = [
|
private pathPermissions = [ // minimum level needed for the specified paths
|
||||||
{path: 'materials', permission: 'dev'},
|
{path: 'materials', permission: 'dev'},
|
||||||
{path: 'templates', permission: 'dev'},
|
{path: 'templates', permission: 'dev'},
|
||||||
{path: 'changelog', permission: 'dev'},
|
{path: 'changelog', permission: 'dev'},
|
||||||
{path: 'users', permission: 'admin'}
|
{path: 'users', permission: 'admin'}
|
||||||
];
|
];
|
||||||
readonly levels = [
|
readonly levels = [ // all user levels in ascending permissions order
|
||||||
'predict',
|
'predict',
|
||||||
'read',
|
'read',
|
||||||
'write',
|
'write',
|
||||||
'dev',
|
'dev',
|
||||||
'admin'
|
'admin'
|
||||||
];
|
];
|
||||||
|
// returns true or false depending on whether the user fulfills the minimum level
|
||||||
isLevel: {[level: string]: boolean} = {};
|
isLevel: {[level: string]: boolean} = {};
|
||||||
hasPrediction = false; // true if user has prediction models specified
|
hasPrediction = false; // true if user has prediction models specified
|
||||||
userId = '';
|
userId = '';
|
||||||
@ -99,6 +100,7 @@ export class LoginService implements CanActivate {
|
|||||||
this.hasPrediction = false;
|
this.hasPrediction = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// canActivate for Angular routing
|
||||||
canActivate(route: ActivatedRouteSnapshot = null, state: RouterStateSnapshot = null): Observable<boolean> {
|
canActivate(route: ActivatedRouteSnapshot = null, state: RouterStateSnapshot = null): Observable<boolean> {
|
||||||
return new Observable<boolean>(observer => {
|
return new Observable<boolean>(observer => {
|
||||||
new Promise(resolve => {
|
new Promise(resolve => {
|
||||||
@ -112,7 +114,8 @@ export class LoginService implements CanActivate {
|
|||||||
}
|
}
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
const pathPermission = this.pathPermissions.find(e => e.path.indexOf(route.url[0].path) >= 0);
|
const pathPermission = this.pathPermissions.find(e => e.path.indexOf(route.url[0].path) >= 0);
|
||||||
const ok = res && (!pathPermission || this.isLevel[pathPermission.permission]); // check if level is permitted for path
|
// check if level is permitted for path
|
||||||
|
const ok = res && (!pathPermission || this.isLevel[pathPermission.permission]);
|
||||||
observer.next(ok);
|
observer.next(ok);
|
||||||
observer.complete();
|
observer.complete();
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
|
@ -1,116 +1,5 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { ValidationService } from './validation.service';
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
let validationService: ValidationService;
|
|
||||||
|
|
||||||
describe('ValidationService', () => {
|
describe('ValidationService', () => {
|
||||||
beforeEach(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
providers: [ValidationService]
|
|
||||||
});
|
|
||||||
|
|
||||||
validationService = TestBed.inject(ValidationService);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be created', () => {
|
|
||||||
expect(validationService).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
// it('should return true on a correct username', () => {
|
|
||||||
// expect(validationService.username('abc')).toEqual({ok: true, error: ''});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return an error on an incorrect username', () => {
|
|
||||||
// expect(validationService.username('abc#')).toEqual({ok: false, error: 'username must only contain a-z0-9-_.'});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return true on a correct password', () => {
|
|
||||||
// expect(validationService.password('Abc123!#')).toEqual({ok: true, error: ''});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return an error on a password too short', () => {
|
|
||||||
// expect(validationService.password('Abc123')).toEqual({ok: false, error: 'password must have at least 8 characters'});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return an error on a password without a lowercase letter', () => {
|
|
||||||
// expect(validationService.password('ABC123!#')).toEqual({ok: false, error: 'password must have at least one lowercase character'});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return an error on a password without an uppercase letter', () => {
|
|
||||||
// expect(validationService.password('abc123!#')).toEqual({ok: false, error: 'password must have at least one uppercase character'});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return an error on a password without a number', () => {
|
|
||||||
// expect(validationService.password('Abcabc!#')).toEqual({ok: false, error: 'password must have at least one number'});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return an error on a password without a special character', () => {
|
|
||||||
// expect(validationService.password('Abc12345')).toEqual({ok: false, error: 'password must have at least one of the following characters !"#%&\'()*+,-.\\/:;<=>?@[]^_`{|}~'});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return an error on a password with a character not allowed', () => {
|
|
||||||
// expect(validationService.password('Abc123!€')).toEqual({ok: false, error: 'password must only contain a-zA-Z0-9!"#%&\'()*+,-./:;<=>?@[]^_`{|}~'});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return true on a correct string', () => {
|
|
||||||
// expect(validationService.string('Abc')).toEqual({ok: true, error: ''});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return an error on a string too long', () => {
|
|
||||||
// expect(validationService.string('abcabcabcbabcbabcabcabacbabcabcabcbabcbabcabcabacbabcabcabcbabcbabcabcabacbabcabcabcbabcbabcabcabacbabcabcabcbabcbabcabcabacbacab')).toEqual({ok: false, error: 'must contain max 128 characters'});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return true on a string in the list', () => {
|
|
||||||
// expect(validationService.stringOf('Abc', ['Abc', 'Def'])).toEqual({ok: true, error: ''});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return an error on a string not in the list', () => {
|
|
||||||
// expect(validationService.stringOf('abc', ['Abc', 'Def'])).toEqual({ok: false, error: 'must be one of Abc, Def'});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return true on a string of correct length', () => {
|
|
||||||
// expect(validationService.stringLength('Abc', 5)).toEqual({ok: true, error: ''});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return an error on a string longer than specified', () => {
|
|
||||||
// expect(validationService.stringLength('Abc', 2)).toEqual({ok: false, error: 'must contain max 2 characters'});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return true on a number in the range', () => {
|
|
||||||
// expect(validationService.minMax(2, -2, 2)).toEqual({ok: true, error: ''});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return an error on a number below the range', () => {
|
|
||||||
// expect(validationService.minMax(0, 1, 3)).toEqual({ok: false, error: 'must be between 1 and 3'});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return an error on a number above the range', () => {
|
|
||||||
// expect(validationService.minMax(3.1, 1, 3)).toEqual({ok: false, error: 'must be between 1 and 3'});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return true on a number above min', () => {
|
|
||||||
// expect(validationService.min(2, -2)).toEqual({ok: true, error: ''});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return an error on a number below min', () => {
|
|
||||||
// expect(validationService.min(0, 1)).toEqual({ok: false, error: 'must not be below 1'});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return true on a number below max', () => {
|
|
||||||
// expect(validationService.max(2, 2)).toEqual({ok: true, error: ''});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return an error on a number above max', () => {
|
|
||||||
// expect(validationService.max(2, 1)).toEqual({ok: false, error: 'must not be above 1'});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return true on a string not in the list', () => {
|
|
||||||
// expect(validationService.unique('Abc', ['Def', 'Ghi'])).toEqual({ok: true, error: ''});
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should return an error on a string from the list', () => {
|
|
||||||
// expect(validationService.unique('Abc', ['Abc', 'Def'])).toEqual({ok: false, error: 'values must be unique'});
|
|
||||||
// });
|
|
||||||
});
|
});
|
||||||
|
@ -63,7 +63,7 @@ export class ValidationService {
|
|||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
stringOf(data, list) {
|
stringOf(data, list) { // string must be in list
|
||||||
const {ignore, error} = Joi.string().allow('').valid(...list.map(e => e.toString())).validate(data);
|
const {ignore, error} = Joi.string().allow('').valid(...list.map(e => e.toString())).validate(data);
|
||||||
if (error) {
|
if (error) {
|
||||||
return {ok: false, error: 'must be one of ' + list.join(', ')};
|
return {ok: false, error: 'must be one of ' + list.join(', ')};
|
||||||
@ -71,7 +71,7 @@ export class ValidationService {
|
|||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
stringNin(data, list) {
|
stringNin(data, list) { // string must not be in list
|
||||||
const {ignore, error} = Joi.string().invalid(...list).validate(data);
|
const {ignore, error} = Joi.string().invalid(...list).validate(data);
|
||||||
if (error) {
|
if (error) {
|
||||||
return {ok: false, error: 'value not allowed'};
|
return {ok: false, error: 'value not allowed'};
|
||||||
@ -79,7 +79,7 @@ export class ValidationService {
|
|||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
stringLength(data, length) {
|
stringLength(data, length) { // string with maximum length
|
||||||
const {ignore, error} = Joi.string().max(length).allow('').validate(data);
|
const {ignore, error} = Joi.string().max(length).allow('').validate(data);
|
||||||
if (error) {
|
if (error) {
|
||||||
return {ok: false, error: 'must contain max ' + length + ' characters'};
|
return {ok: false, error: 'must contain max ' + length + ' characters'};
|
||||||
@ -87,7 +87,7 @@ export class ValidationService {
|
|||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
minMax(data, min, max) {
|
minMax(data, min, max) { // number between min and max
|
||||||
const {ignore, error} = Joi.number().allow('').min(min).max(max).validate(data);
|
const {ignore, error} = Joi.number().allow('').min(min).max(max).validate(data);
|
||||||
if (error) {
|
if (error) {
|
||||||
return {ok: false, error: `must be between ${min} and ${max}`};
|
return {ok: false, error: `must be between ${min} and ${max}`};
|
||||||
@ -95,7 +95,7 @@ export class ValidationService {
|
|||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
min(data, min) {
|
min(data, min) { // number above min
|
||||||
const {ignore, error} = Joi.number().allow('').min(min).validate(data);
|
const {ignore, error} = Joi.number().allow('').min(min).validate(data);
|
||||||
if (error) {
|
if (error) {
|
||||||
return {ok: false, error: `must not be below ${min}`};
|
return {ok: false, error: `must not be below ${min}`};
|
||||||
@ -103,7 +103,7 @@ export class ValidationService {
|
|||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
max(data, max) {
|
max(data, max) { // number below max
|
||||||
const {ignore, error} = Joi.number().allow('').max(max).validate(data);
|
const {ignore, error} = Joi.number().allow('').max(max).validate(data);
|
||||||
if (error) {
|
if (error) {
|
||||||
return {ok: false, error: `must not be above ${max}`};
|
return {ok: false, error: `must not be above ${max}`};
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<form #userForm="ngForm">
|
<form #userForm="ngForm">
|
||||||
<rb-form-input name="name" label="user name" appValidate="username" required [(ngModel)]="user.name"
|
<rb-form-input name="name" label="user name" appValidate="username" required [(ngModel)]="user.name"
|
||||||
#nameInput="ngModel">
|
#nameInput="ngModel">
|
||||||
|
@ -1,62 +1,5 @@
|
|||||||
// import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
//
|
|
||||||
// import { SettingsComponent } from './settings.component';
|
describe('SettingsComponent', () => {
|
||||||
// import {ApiService} from '../services/api.service';
|
|
||||||
// import {LoginService} from '../services/login.service';
|
});
|
||||||
// import {Router} from '@angular/router';
|
|
||||||
// import {RbCustomInputsModule} from '../rb-custom-inputs/rb-custom-inputs.module';
|
|
||||||
// import {FormsModule} from '@angular/forms';
|
|
||||||
// import {ValidationService} from '../services/validation.service';
|
|
||||||
// import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
|
|
||||||
//
|
|
||||||
// // TODO
|
|
||||||
//
|
|
||||||
// let routerServiceSpy: jasmine.SpyObj<Router>;
|
|
||||||
// let apiServiceSpy: jasmine.SpyObj<ApiService>;
|
|
||||||
// let loginServiceSpy: jasmine.SpyObj<LoginService>;
|
|
||||||
// let validationServiceSpy: jasmine.SpyObj<ValidationService>;
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// describe('SettingsComponent', () => {
|
|
||||||
// let component: SettingsComponent;
|
|
||||||
// let fixture: ComponentFixture<SettingsComponent>;
|
|
||||||
//
|
|
||||||
// beforeEach(async(() => {
|
|
||||||
// const routerSpy = jasmine.createSpyObj('Router', ['navigate']);
|
|
||||||
// const apiSpy = jasmine.createSpyObj('ApiService', ['get', 'post', 'put']);
|
|
||||||
// const loginSpy = jasmine.createSpyObj('LoginService', ['login', 'canActivate']);
|
|
||||||
// const validationSpy = jasmine.createSpyObj('ValidationService', ['generate']);
|
|
||||||
//
|
|
||||||
// TestBed.configureTestingModule({
|
|
||||||
// declarations: [ SettingsComponent ],
|
|
||||||
// imports: [
|
|
||||||
// RbUiComponentsModule,
|
|
||||||
// RbCustomInputsModule,
|
|
||||||
// FormsModule
|
|
||||||
// ],
|
|
||||||
// providers: [
|
|
||||||
// {provide: Router, useValue: routerSpy},
|
|
||||||
// {provide: ApiService, useValue: apiSpy},
|
|
||||||
// {provide: LoginService, useValue: loginSpy},
|
|
||||||
// {provide: ValidationService, useValue: validationSpy},
|
|
||||||
// ]
|
|
||||||
// })
|
|
||||||
// .compileComponents();
|
|
||||||
//
|
|
||||||
// routerServiceSpy = TestBed.inject(Router) as jasmine.SpyObj<Router>;
|
|
||||||
// apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
|
|
||||||
// loginServiceSpy = TestBed.inject(LoginService) as jasmine.SpyObj<LoginService>;
|
|
||||||
// validationServiceSpy = TestBed.inject(ValidationService) as jasmine.SpyObj<ValidationService>;
|
|
||||||
// }));
|
|
||||||
//
|
|
||||||
// beforeEach(() => {
|
|
||||||
// fixture = TestBed.createComponent(SettingsComponent);
|
|
||||||
// component = fixture.componentInstance;
|
|
||||||
// component.ngOnInit();
|
|
||||||
// fixture.detectChanges();
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// it('should create', () => {
|
|
||||||
// expect(component).toBeTruthy();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
@ -12,9 +12,9 @@ import {LoginService} from '../services/login.service';
|
|||||||
})
|
})
|
||||||
export class SettingsComponent implements OnInit {
|
export class SettingsComponent implements OnInit {
|
||||||
|
|
||||||
user: UserModel = new UserModel();
|
user: UserModel = new UserModel(); // user to edit
|
||||||
password = '';
|
password = ''; // new password
|
||||||
messageUser = '';
|
messageUser = ''; // messages for user and pass part
|
||||||
messagePass = '';
|
messagePass = '';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -34,7 +34,7 @@ export class SettingsComponent implements OnInit {
|
|||||||
if (err) {
|
if (err) {
|
||||||
this.messageUser = err.error.status;
|
this.messageUser = err.error.status;
|
||||||
}
|
}
|
||||||
else {
|
else { // login with new credentials
|
||||||
this.login.login(data.name).then(res => {
|
this.login.login(data.name).then(res => {
|
||||||
if (res) {
|
if (res) {
|
||||||
this.router.navigate(['/samples']);
|
this.router.navigate(['/samples']);
|
||||||
@ -52,7 +52,7 @@ export class SettingsComponent implements OnInit {
|
|||||||
if (err) {
|
if (err) {
|
||||||
this.messagePass = err.error.status;
|
this.messagePass = err.error.status;
|
||||||
}
|
}
|
||||||
else {
|
else { // login with new credentials
|
||||||
this.login.login('', this.password).then(res => {
|
this.login.login('', this.password).then(res => {
|
||||||
if (res) {
|
if (res) {
|
||||||
this.router.navigate(['/samples']);
|
this.router.navigate(['/samples']);
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<rb-form-select name="collectionSelection" label="collection"
|
<rb-form-select name="collectionSelection" label="collection"
|
||||||
[(ngModel)]="collection" (ngModelChange)="loadTemplates()">
|
[(ngModel)]="collection" (ngModelChange)="loadTemplates()">
|
||||||
<option value="material">Materials</option>
|
<option value="material">Materials</option>
|
||||||
|
@ -1,57 +1,5 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { TemplatesComponent } from './templates.component';
|
|
||||||
import {LoginService} from '../services/login.service';
|
|
||||||
import {ValidationService} from '../services/validation.service';
|
|
||||||
import {ApiService} from '../services/api.service';
|
|
||||||
import {DataService} from '../services/data.service';
|
|
||||||
import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
|
|
||||||
import {FormsModule} from '@angular/forms';
|
|
||||||
import {RbCustomInputsModule} from '../rb-custom-inputs/rb-custom-inputs.module';
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
let apiServiceSpy: jasmine.SpyObj<ApiService>;
|
|
||||||
let validationServiceSpy: jasmine.SpyObj<ValidationService>;
|
|
||||||
let dataServiceSpy: jasmine.SpyObj<DataService>;
|
|
||||||
|
|
||||||
describe('TemplatesComponent', () => {
|
describe('TemplatesComponent', () => {
|
||||||
let component: TemplatesComponent;
|
|
||||||
let fixture: ComponentFixture<TemplatesComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
const apiSpy = jasmine.createSpyObj('ApiService', ['post', 'put']);
|
|
||||||
const validationSpy = jasmine.createSpyObj('ValidationService', ['string', 'parameterName', 'parameterRange']);
|
|
||||||
const dataSpy = jasmine.createSpyObj('DataService', ['load', 'idReload']);
|
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ TemplatesComponent ],
|
|
||||||
imports: [
|
|
||||||
RbUiComponentsModule,
|
|
||||||
RbCustomInputsModule,
|
|
||||||
FormsModule
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
{provide: ApiService, useValue: apiSpy},
|
|
||||||
{provide: ValidationService, useValue: validationSpy},
|
|
||||||
{provide: DataService, useValue: dataSpy}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
|
|
||||||
validationServiceSpy = TestBed.inject(ValidationService) as jasmine.SpyObj<ValidationService>;
|
|
||||||
dataServiceSpy = TestBed.inject(DataService) as jasmine.SpyObj<DataService>;
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(TemplatesComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
component.ngOnInit();
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -28,12 +28,18 @@ import {DataService} from '../services/data.service';
|
|||||||
})
|
})
|
||||||
export class TemplatesComponent implements OnInit {
|
export class TemplatesComponent implements OnInit {
|
||||||
|
|
||||||
collection = 'measurement';
|
collection = 'measurement'; // collection to view
|
||||||
templates: TemplateModel[] = [];
|
templates: TemplateModel[] = []; // all templates of current collection
|
||||||
templateGroups: {[first_id: string]: TemplateModel[]} = {}; // templates grouped by first_id
|
templateGroups: {[first_id: string]: TemplateModel[]} = {}; // templates grouped by first_id
|
||||||
templateEdit: {[first_id: string]: TemplateModel} = {}; // latest template of each first_id for editing
|
templateEdit: {[first_id: string]: TemplateModel} = {}; // latest template of each first_id for editing
|
||||||
groupsView: {first_id: string, name: string, version: number, expanded: boolean, edit: boolean, entries: TemplateModel[]}[] = [];
|
groupsView: { // grouped templates
|
||||||
arr = ['testA', 'testB', 'testC'];
|
first_id: string,
|
||||||
|
name: string,
|
||||||
|
version: number,
|
||||||
|
expanded: boolean,
|
||||||
|
edit: boolean,
|
||||||
|
entries: TemplateModel[]
|
||||||
|
}[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private api: ApiService,
|
private api: ApiService,
|
||||||
@ -55,7 +61,7 @@ export class TemplatesComponent implements OnInit {
|
|||||||
templateFormat() {
|
templateFormat() {
|
||||||
this.templateGroups = {};
|
this.templateGroups = {};
|
||||||
this.templateEdit = {};
|
this.templateEdit = {};
|
||||||
this.templates.forEach(template => {
|
this.templates.forEach(template => { // group templates
|
||||||
if (this.templateGroups[template.first_id]) {
|
if (this.templateGroups[template.first_id]) {
|
||||||
this.templateGroups[template.first_id].push(template);
|
this.templateGroups[template.first_id].push(template);
|
||||||
}
|
}
|
||||||
@ -94,7 +100,7 @@ export class TemplatesComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
if (valid) {
|
if (valid) {
|
||||||
const sendData = {name: template.name, parameters: template.parameters.map(e => omit(e, ['rangeString']))};
|
const sendData = {name: template.name, parameters: template.parameters.map(e => omit(e, ['rangeString']))};
|
||||||
if (first_id === 'null') {
|
if (first_id === 'null') { // new template
|
||||||
this.api.post<TemplateModel>(`/template/${this.collection}/new`, sendData, () => {
|
this.api.post<TemplateModel>(`/template/${this.collection}/new`, sendData, () => {
|
||||||
delete this.d.arr[this.collection + 'Templates'];
|
delete this.d.arr[this.collection + 'Templates'];
|
||||||
this.d.load(this.collection + 'Templates', () => {
|
this.d.load(this.collection + 'Templates', () => {
|
||||||
@ -113,16 +119,20 @@ export class TemplatesComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
console.log('not valid');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newTemplate() {
|
newTemplate() {
|
||||||
if (!this.templateEdit.null) {
|
if (!this.templateEdit.null) {
|
||||||
const template = new TemplateModel();
|
const template = new TemplateModel();
|
||||||
template.name = 'new template';
|
template.name = 'new template';
|
||||||
this.groupsView.push({first_id: 'null', name: 'new template', version: 0, expanded: true, edit: true, entries: [template]});
|
this.groupsView.push({
|
||||||
|
first_id: 'null',
|
||||||
|
name: 'new template',
|
||||||
|
version: 0,
|
||||||
|
expanded: true,
|
||||||
|
edit: true,
|
||||||
|
entries: [template]
|
||||||
|
});
|
||||||
this.templateEdit.null = new TemplateModel();
|
this.templateEdit.null = new TemplateModel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<rb-icon-button icon="add" mode="primary" (click)="addNewUser()" class="space-below">New user</rb-icon-button>
|
<rb-icon-button icon="add" mode="primary" (click)="addNewUser()" class="space-below">New user</rb-icon-button>
|
||||||
|
|
||||||
<form *ngIf="newUser" #userForm="ngForm" class="space-below">
|
<form *ngIf="newUser" #userForm="ngForm" class="space-below">
|
||||||
|
@ -1,53 +1,5 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { UsersComponent } from './users.component';
|
|
||||||
import {ApiService} from '../services/api.service';
|
|
||||||
import {LoginService} from '../services/login.service';
|
|
||||||
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
|
|
||||||
import {RbCustomInputsModule} from '../rb-custom-inputs/rb-custom-inputs.module';
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
let apiServiceSpy: jasmine.SpyObj<ApiService>;
|
|
||||||
let modalServiceSpy: jasmine.SpyObj<ModalService>;
|
|
||||||
let loginServiceSpy: jasmine.SpyObj<LoginService>;
|
|
||||||
|
|
||||||
|
|
||||||
describe('UsersComponent', () => {
|
describe('UsersComponent', () => {
|
||||||
let component: UsersComponent;
|
|
||||||
let fixture: ComponentFixture<UsersComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
const apiSpy = jasmine.createSpyObj('ApiService', ['get', 'post', 'put']);
|
|
||||||
const modalSpy = jasmine.createSpyObj('ModalService', ['open']);
|
|
||||||
const loginSpy = jasmine.createSpyObj('LoginService', ['login', 'canActivate']);
|
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ UsersComponent ],
|
|
||||||
imports: [
|
|
||||||
RbCustomInputsModule
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
{provide: ApiService, useValue: apiSpy},
|
|
||||||
{provide: ModalService, useValue: modalSpy},
|
|
||||||
{provide: LoginService, useValue: loginSpy},
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
|
|
||||||
modalServiceSpy = TestBed.inject(ModalService) as jasmine.SpyObj<ModalService>;
|
|
||||||
loginServiceSpy = TestBed.inject(LoginService) as jasmine.SpyObj<LoginService>;
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(UsersComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
component.ngOnInit();
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -13,12 +13,12 @@ import {DataService} from '../services/data.service';
|
|||||||
})
|
})
|
||||||
export class UsersComponent implements OnInit {
|
export class UsersComponent implements OnInit {
|
||||||
|
|
||||||
users: UserModel[] = [];
|
users: UserModel[] = []; // all active users
|
||||||
deletedUsers: UserModel[] = [];
|
deletedUsers: UserModel[] = []; // all deleted users
|
||||||
newUser: UserModel | null = null;
|
newUser: UserModel | null = null; // data of new user
|
||||||
newUserPass = '';
|
newUserPass = ''; // password of new user
|
||||||
modelSelect: {id: string, name: string}[] = [];
|
modelSelect: {id: string, name: string}[] = []; // list of all models for selection
|
||||||
modelIds: {[id: string]: string} = {};
|
modelIds: {[id: string]: string} = {}; // all models by id
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private api: ApiService,
|
private api: ApiService,
|
||||||
@ -41,9 +41,7 @@ export class UsersComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
saveUser(user: UserModel) {
|
saveUser(user: UserModel) {
|
||||||
console.log(user.models);
|
|
||||||
user.models = user.models.filter(e => e !== '');
|
user.models = user.models.filter(e => e !== '');
|
||||||
console.log(user.models);
|
|
||||||
this.api.put<UserModel>('/user/' + user.origName, user.sendFormat('admin'), data => {
|
this.api.put<UserModel>('/user/' + user.origName, user.sendFormat('admin'), data => {
|
||||||
user.deserialize(data);
|
user.deserialize(data);
|
||||||
user.edit = false;
|
user.edit = false;
|
||||||
@ -51,7 +49,7 @@ export class UsersComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
saveNewUser() {
|
saveNewUser() {
|
||||||
// this.newUser.models = this.newUser.models.filter(e => e !== '' || e !== undefined);
|
this.newUser.models = this.newUser.models.filter(e => e !== '');
|
||||||
this.api.post('/user/new', {...this.newUser.sendFormat('admin'), pass: this.newUserPass}, data => {
|
this.api.post('/user/new', {...this.newUser.sendFormat('admin'), pass: this.newUserPass}, data => {
|
||||||
this.newUser = null;
|
this.newUser = null;
|
||||||
this.users.push(new UserModel().deserialize(data));
|
this.users.push(new UserModel().deserialize(data));
|
||||||
|
@ -1,14 +1,5 @@
|
|||||||
// import { ValidateDirective } from './validate.directive';
|
import { ValidateDirective } from './validate.directive';
|
||||||
// import {ValidationService} from './services/validation.service';
|
|
||||||
//
|
describe('ValidateDirective', () => {
|
||||||
// // TODO
|
|
||||||
//
|
});
|
||||||
// const validationSpy = {test: () => {}};
|
|
||||||
// const validationServiceSpy: jasmine.SpyObj<any> = spyOn(validationSpy, 'test');
|
|
||||||
//
|
|
||||||
// describe('ValidateDirective', () => {
|
|
||||||
// it('should create an instance', () => {
|
|
||||||
// const directive = new ValidateDirective(validationServiceSpy);
|
|
||||||
// expect(directive).toBeTruthy();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
Loading…
Reference in New Issue
Block a user