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' ;
2020-08-20 10:42:02 +02:00
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' ;
2021-01-22 14:09:07 +01:00
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-20 10:42:02 +02:00
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 {
2021-01-18 14:26:40 +01:00
result : { predictions : any [ ] , mean : any [ ] } ; // Prediction result from python container
2020-08-12 15:15:31 +02:00
loading = false ;
2020-08-20 10:42:02 +02:00
activeGroup : ModelItemModel = new ModelItemModel ( ) ;
activeModelIndex = 0 ;
2021-01-18 14:26:40 +01:00
// 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 ;
2020-08-20 10:42:02 +02:00
spectrumNames : string [ ] = [ ] ;
2020-08-12 15:15:31 +02:00
spectrum : string [ ] [ ] = [ [ ] ] ;
2020-08-20 10:42:02 +02:00
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-20 10:42:02 +02:00
} ;
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 (
2020-08-20 10:42:02 +02:00
private api : ApiService ,
2021-01-22 14:09:07 +01:00
public d : DataService ,
public login : LoginService
2020-08-20 10:42:02 +02:00
) {
this . chart [ 0 ] = cloneDeep ( this . chartInit ) ;
}
2020-08-12 15:15:31 +02:00
ngOnInit ( ) : void {
2020-08-20 10:42:02 +02:00
this . d . load ( 'modelGroups' , ( ) = > {
this . activeGroup = this . d . arr . modelGroups [ 0 ] ;
} ) ;
2020-08-12 15:15:31 +02:00
}
fileToArray ( files ) {
2020-08-20 10:42:02 +02:00
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 = ( ) = > {
2021-01-18 14:26:40 +01:00
// 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 ;
2021-01-18 14:26:40 +01:00
// Flatten to format needed for prediction
2020-08-20 10:42:02 +02:00
this . flattenedSpectra [ i ] = { labels : this.spectrum.map ( e = > e [ 0 ] ) , values : this.spectrum.map ( e = > e [ 1 ] ) } ;
2021-01-18 14:26:40 +01:00
// Add to chart
2020-08-20 10:42:02 +02:00
this . chart [ i ] = cloneDeep ( this . chartInit ) ;
this . chart [ i ] . data = this . spectrum . map ( e = > ( { x : parseFloat ( e [ 0 ] ) , y : parseFloat ( e [ 1 ] ) } ) ) ;
load -- ;
2021-01-18 14:26:40 +01:00
if ( load <= 0 ) { // All loaded
2020-08-20 10:42:02 +02:00
this . loadPrediction ( ) ;
}
} ;
fileReader . readAsText ( files [ i ] ) ;
}
}
2020-08-12 15:15:31 +02:00
}
2020-08-20 10:42:02 +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 = > {
2021-01-18 14:26:40 +01:00
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 = {
2021-01-18 14:26:40 +01:00
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 )
2021-01-18 14:26:40 +01:00
. 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
} ;
2020-08-20 10:42:02 +02:00
this . loading = false ;
} ) ;
}
2021-01-18 14:26:40 +01:00
groupChange ( index ) { // Group was changed
2020-08-20 10:42:02 +02:00
this . activeGroup = this . d . arr . modelGroups [ index ] ;
2020-08-31 16:14:47 +02:00
this . activeModelIndex = 0 ;
2020-08-20 10:42:02 +02:00
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() {
2021-01-22 14:09:07 +01:00
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
}