definma-ui/src/app/prediction/prediction.component.ts

249 lines
8.0 KiB
TypeScript
Raw Normal View History

2020-08-12 15:15:31 +02:00
import { Component, OnInit } from '@angular/core';
2020-12-15 12:00:16 +01:00
import { ChartOptions } from 'chart.js';
import { ApiService } from '../services/api.service';
import { animate, style, transition, trigger } from '@angular/animations';
import cloneDeep from 'lodash/cloneDeep';
2020-08-28 08:14:57 +02:00
import omit from 'lodash/omit';
2020-12-15 12:00:16 +01:00
import { DataService } from '../services/data.service';
import { LoginService } from '../services/login.service';
2020-12-15 12:00:16 +01:00
import { ModelItemModel } from '../models/model-item.model';
2021-01-08 13:52:20 +01:00
import * as FileSaver from 'file-saver';
import * as pdfMake from "pdfmake/build/pdfmake";
import * as pdfFonts from 'pdfmake/build/vfs_fonts';
(<any>pdfMake).vfs = pdfFonts.pdfMake.vfs;
2020-08-12 15:15:31 +02:00
@Component({
selector: 'app-prediction',
templateUrl: './prediction.component.html',
styleUrls: ['./prediction.component.scss'],
animations: [
trigger(
'inOut', [
transition(':enter', [
style({height: 0, opacity: 0}),
animate('0.5s ease-out', style({height: '*', opacity: 1}))
]),
transition(':leave', [
style({height: '*', opacity: 1}),
animate('0.5s ease-in', style({height: 0, opacity: 0}))
])
]
)
]
})
export class PredictionComponent implements OnInit {
result: { predictions: any[], mean: any[] }; // Prediction result from python container
2020-08-12 15:15:31 +02:00
loading = false;
activeGroup: ModelItemModel = new ModelItemModel();
activeModelIndex = 0;
// If true, spectra belong to different samples, otherwise multiple spectra from the same sample are given
2020-09-03 15:51:53 +02:00
multipleSamples = false;
spectrumNames: string[] = [];
2020-08-12 15:15:31 +02:00
spectrum: string[][] = [[]];
flattenedSpectra = [];
chart = [];
readonly chartInit = {
2020-08-12 15:15:31 +02:00
data: [],
label: 'Spectrum',
showLine: true,
fill: false,
pointRadius: 0,
borderColor: '#00a8b0',
borderWidth: 2
};
2020-08-12 15:15:31 +02:00
readonly chartOptions: ChartOptions = {
scales: {
xAxes: [{ticks: {min: 400, max: 4000, stepSize: 400, reverse: true}}],
2020-09-03 15:51:53 +02:00
yAxes: [{ticks: {}}]
2020-08-12 15:15:31 +02:00
},
responsive: true,
tooltips: {enabled: false},
hover: {mode: null},
maintainAspectRatio: true,
plugins: {datalabels: {display: false}}
};
constructor(
private api: ApiService,
public d: DataService,
public login: LoginService
) {
this.chart[0] = cloneDeep(this.chartInit);
}
2020-08-12 15:15:31 +02:00
ngOnInit(): void {
this.d.load('modelGroups', () => {
this.activeGroup = this.d.arr.modelGroups[0];
});
2020-08-12 15:15:31 +02:00
}
fileToArray(files) {
this.loading = true;
this.flattenedSpectra = [];
this.chart = [];
let load = files.length;
this.spectrumNames = files.map(e => e.name);
for (const i in files) {
if (files.hasOwnProperty(i)) {
const fileReader = new FileReader();
fileReader.onload = () => {
// Parse to database spectrum representation
2020-08-26 19:25:31 +02:00
this.spectrum = fileReader.result.toString().split('\r\n').map(e => e.split(',').map(el => parseFloat(el)))
.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])};
// Add to chart
this.chart[i] = cloneDeep(this.chartInit);
this.chart[i].data = this.spectrum.map(e => ({x: parseFloat(e[0]), y: parseFloat(e[1])}));
load --;
if (load <= 0) { // All loaded
this.loadPrediction();
}
};
fileReader.readAsText(files[i]);
}
}
2020-08-12 15:15:31 +02:00
}
loadPrediction() {
this.loading = true;
2020-08-28 08:14:57 +02:00
this.api.post<any>(this.activeGroup.models[this.activeModelIndex].url, this.flattenedSpectra, data => {
let tmp = Object.entries(omit(data, ['mean', 'std', 'label'])) // Form: [[label, [{value, color}]]]
.map((entry: any) => entry[1].map(e => ({category: entry[0], label: data.label[entry[0]], value: e.value, color: e.color}))); // Form: [[{category, label, value, color}]]
2020-10-02 10:11:16 +02:00
this.result = {
predictions: tmp[0].map((ignore, columnIndex) => tmp.map(row => row[columnIndex])), // Transpose tmp
2020-10-02 10:11:16 +02:00
mean: Object.entries(data.mean)
.map((entry:any) => ({category: entry[0], label: data.label[entry[0]], value: entry[1].value, color: entry[1].color, std: data.std[entry[0]]})) // Form: [{category, label, value, color}]
2020-08-28 08:14:57 +02:00
};
this.loading = false;
});
}
groupChange(index) { // Group was changed
this.activeGroup = this.d.arr.modelGroups[index];
2020-08-31 16:14:47 +02:00
this.activeModelIndex = 0;
this.result = undefined;
}
2020-08-28 08:14:57 +02:00
2021-01-21 14:09:31 +01:00
// Aggregates spectrum names and prediction values into an associative array
prepareExport() {
const zip = (a, b) => a.map((k, i) => [k, b[i]]);
const values = this.result.predictions
.map(prediction => prediction
.filter(field => field.category === 'Prediction')
.map(field => field.value));
return zip(this.spectrumNames, values);
}
// Converts the prediction results to a CSV file
2020-12-15 12:00:16 +01:00
exportCSV() {
2021-01-21 14:09:31 +01:00
const predictions = this.prepareExport();
2020-12-15 12:00:16 +01:00
const csv = predictions.map(line => line.join(";")).join("\n");
FileSaver.saveAs(new Blob([csv], { type: 'text/csv;charset=utf-8' }), "predictions.csv");
2020-08-28 08:14:57 +02:00
}
2021-01-08 13:52:20 +01:00
2021-01-21 14:09:31 +01:00
// Converts the prediction results to a PDF file
2021-01-08 13:52:20 +01:00
exportPDF() {
const dd = {
content: [
{
text: 'DeFinMa - Decoding the Fingerprint of Materials by AI',
style: 'header'
},
{
table: {
body: [
[new Date().toLocaleDateString(), 'Security class', 'Person in charge', 'Phone'],
[this.activeGroup.group, 'Intern',
{
stack: [
'CR/APS1-Lotter',
'CR/APS1-Lingenfelser'
]
},
{
stack: [
'0711/811-49017',
'0711/811-6897'
]
}
]
]
}
},
{
bold: true,
text: 'Customer'
},
this.login.username,
{
text: 'Prediction of ' + this.activeGroup.group + ' (' + this.activeGroup.models[this.activeModelIndex].name + ')*',
style: 'subheader'
},
{
text: this.result.mean.map(e => e.category + ' ' + e.value + ' ' + e.label + ' ' + (e.std !== '' ? (' (standard deviation: ' + e.std + ')') : ''))
},
{
table: {
widths: ['*', 'auto'],
body: [
['Input Data / Sample Name', 'Prediction*']
].concat(this.prepareExport())
}
},
{
text: 'Reference Data',
style: 'subheader'
},
{
image: document.getElementsByTagName('canvas')[0].toDataURL('image/png'),
width: 500
},
{
text: '*Disclaimer: This tool is still under development and Testing',
style: 'subsubheader'
},
{
text: [
'The prediction and classification of material parameters are validated only for certain conditions.',
'These results may therefore under no circumstances be used to evaluate quality-relevant issues.',
'For more details please contact ',
{
text: 'CR/APS1-Lingenfelder',
link: 'mailto:dominic.lingenfelser@bosch.com'
},
'.'
]
},
{
table: {
body: [
['Pr\u00fcfung', 'Freigabe', 'Datum'],
['CR/APS1-Lotter', 'CR/APS1-Lingenfelser', new Date().toLocaleDateString()]
]
}
},
'\u00a9 Alle Rechte bei Robert Bosch GmbH, auch f\u00fcr den Fall von Schutzreichtsanmeldungen. Jede Verf\u00fcgungsbefugnis, wie Kopier- und Weitergaberecht, bei uns.'
],
styles: {
header: {
fontSize: 18,
bold: true
},
subheader: {
fontSize: 15,
bold: true
},
subsubheader: {
bold: true
}
}
};
pdfMake.createPdf(dd).download();
2021-01-08 13:52:20 +01:00
}
2020-08-12 15:15:31 +02:00
}