Merge pull request #49 in DEFINMA/definma-ui from f/pdf-export to master
* commit 'efd4e3f5e90d7e6efe1f21fe197a762954d2e9d0': Add timestamps to export file names Fix export button placement, finalize PDF layout Improve PDF export fonts Add most relevant content to PDF export Fix prediction result aggregation Add pdfmake dependency
This commit is contained in:
commit
2d3782cc82
707
package-lock.json
generated
707
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -32,6 +32,7 @@
|
|||||||
"flatpickr": "^4.6.3",
|
"flatpickr": "^4.6.3",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
"ng2-charts": "^2.3.2",
|
"ng2-charts": "^2.3.2",
|
||||||
|
"pdfmake": "^0.1.70",
|
||||||
"quick-score": "0.0.8",
|
"quick-score": "0.0.8",
|
||||||
"rxjs": "~6.5.5",
|
"rxjs": "~6.5.5",
|
||||||
"str-compare": "^0.1.2",
|
"str-compare": "^0.1.2",
|
||||||
|
@ -60,12 +60,18 @@
|
|||||||
<rb-form-radio name="multiple-samples" label="Multiple samples" [(ngModel)]="multipleSamples" [value]="true">
|
<rb-form-radio name="multiple-samples" label="Multiple samples" [(ngModel)]="multipleSamples" [value]="true">
|
||||||
</rb-form-radio>
|
</rb-form-radio>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<rb-icon-button icon="forward-right" mode="primary" (click)="exportCSV()" *ngIf="spectrumNames.length">
|
|
||||||
Export to CSV
|
|
||||||
</rb-icon-button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- CSV export -->
|
||||||
|
<rb-icon-button icon="forward-right" mode="secondary" (click)="exportCSV()" *ngIf="spectrumNames.length" style="margin-right: 0.5rem">
|
||||||
|
Export to CSV
|
||||||
|
</rb-icon-button>
|
||||||
|
|
||||||
|
<!-- PDF exprot -->
|
||||||
|
<rb-icon-button icon="forward-right" mode="secondary" (click)="exportPDF()" *ngIf="spectrumNames.length">
|
||||||
|
Export to PDF
|
||||||
|
</rb-icon-button>
|
||||||
|
|
||||||
<div class="dpt-chart space-below">
|
<div class="dpt-chart space-below">
|
||||||
<canvas baseChart
|
<canvas baseChart
|
||||||
class="dpt-chart"
|
class="dpt-chart"
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-input {
|
.file-input {
|
||||||
|
@ -5,8 +5,13 @@ import { animate, style, transition, trigger } from '@angular/animations';
|
|||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
import omit from 'lodash/omit';
|
import omit from 'lodash/omit';
|
||||||
import { DataService } from '../services/data.service';
|
import { DataService } from '../services/data.service';
|
||||||
|
import { LoginService } from '../services/login.service';
|
||||||
import { ModelItemModel } from '../models/model-item.model';
|
import { ModelItemModel } from '../models/model-item.model';
|
||||||
import * as FileSaver from 'file-saver'
|
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;
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -63,7 +68,8 @@ export class PredictionComponent implements OnInit {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private api: ApiService,
|
private api: ApiService,
|
||||||
public d: DataService
|
public d: DataService,
|
||||||
|
public login: LoginService
|
||||||
) {
|
) {
|
||||||
this.chart[0] = cloneDeep(this.chartInit);
|
this.chart[0] = cloneDeep(this.chartInit);
|
||||||
}
|
}
|
||||||
@ -122,10 +128,154 @@ export class PredictionComponent implements OnInit {
|
|||||||
this.result = undefined;
|
this.result = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
exportCSV() {
|
// Aggregates spectrum names and prediction values into an associative array
|
||||||
|
prepareExport() {
|
||||||
const zip = (a, b) => a.map((k, i) => [k, b[i]]);
|
const zip = (a, b) => a.map((k, i) => [k, b[i]]);
|
||||||
const predictions = zip(this.spectrumNames, this.result.predictions.map(p => p[0].value));
|
const values = this.result.predictions
|
||||||
|
.map(prediction => prediction
|
||||||
|
.filter(field => field.category === 'Prediction')
|
||||||
|
.map(field => field.value));
|
||||||
|
return zip(this.spectrumNames, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a timestamp suitable for file naming
|
||||||
|
generateTimestamp() {
|
||||||
|
let d = new Date();
|
||||||
|
return d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate() + '--' + d.getHours() + "-" + d.getMinutes();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts the prediction results to a CSV file
|
||||||
|
exportCSV() {
|
||||||
|
const predictions = this.prepareExport();
|
||||||
const csv = predictions.map(line => line.join(";")).join("\n");
|
const csv = predictions.map(line => line.join(";")).join("\n");
|
||||||
FileSaver.saveAs(new Blob([csv], { type: 'text/csv;charset=utf-8' }), "predictions.csv");
|
FileSaver.saveAs(new Blob([csv], { type: 'text/csv;charset=utf-8' }), 'predictions-' + this.generateTimestamp() + '.csv');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts the prediction results to a PDF file
|
||||||
|
exportPDF() {
|
||||||
|
const doc = {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
text: 'DeFinMa - Decoding the Fingerprint of Materials by AI',
|
||||||
|
style: 'header'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
table: {
|
||||||
|
widths: ['auto', '*', 'auto', 'auto', 'auto'],
|
||||||
|
body: [
|
||||||
|
[
|
||||||
|
{text: new Date().toLocaleDateString(), style: 'tableHeader'},
|
||||||
|
{text: 'Customer', style: 'tableHeader'},
|
||||||
|
{text: 'Security class', style: 'tableHeader'},
|
||||||
|
{text: 'Person in charge', style: 'tableHeader'},
|
||||||
|
{text: 'Phone', style: 'tableHeader'}],
|
||||||
|
[
|
||||||
|
this.activeGroup.group,
|
||||||
|
this.login.username,
|
||||||
|
'Intern',
|
||||||
|
{
|
||||||
|
stack: [
|
||||||
|
'CR/APS1-Lotter',
|
||||||
|
'CR/APS1-Lingenfelser'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stack: [
|
||||||
|
'0711/811-49017',
|
||||||
|
'0711/811-6897'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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: [
|
||||||
|
[{text: 'Input Data / Sample Name', style: 'tableHeader'}, {text: 'Prediction*', style: 'tableHeader'}]
|
||||||
|
].concat(this.prepareExport())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Reference Data',
|
||||||
|
style: 'subheader'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: document.getElementsByTagName('canvas')[0].toDataURL('image/png'),
|
||||||
|
width: 500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
table: {
|
||||||
|
body: [[{
|
||||||
|
stack: [
|
||||||
|
{
|
||||||
|
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-Lingenfelser',
|
||||||
|
link: 'mailto:dominic.lingenfelser@bosch.com'
|
||||||
|
},
|
||||||
|
'.'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}]],
|
||||||
|
},
|
||||||
|
margin: [25, 20]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
table: {
|
||||||
|
widths: ['*', '*', 'auto'],
|
||||||
|
body: [
|
||||||
|
[{text: 'Pr\u00fcfung', style: 'tableHeader'}, {text: 'Freigabe', style: 'tableHeader'}, {text: 'Datum', style: 'tableHeader'}],
|
||||||
|
['CR/APS1-Lotter', 'CR/APS1-Lingenfelser', new Date().toLocaleDateString()]
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
footer: {
|
||||||
|
text: '\u00a9 Alle Rechte bei Robert Bosch GmbH, auch f\u00fcr den Fall von Schutzreichtsanmeldungen. Jede Verf\u00fcgungsbefugnis, wie Kopier- und Weitergaberecht, bei uns.',
|
||||||
|
fontSize: 8,
|
||||||
|
alignment: 'center'
|
||||||
|
},
|
||||||
|
styles: {
|
||||||
|
header: {
|
||||||
|
fontSize: 18,
|
||||||
|
bold: true,
|
||||||
|
margin: [0, 10]
|
||||||
|
},
|
||||||
|
subheader: {
|
||||||
|
fontSize: 15,
|
||||||
|
bold: true,
|
||||||
|
margin: [0, 8]
|
||||||
|
},
|
||||||
|
subsubheader: {
|
||||||
|
bold: true,
|
||||||
|
margin: [0, 5]
|
||||||
|
},
|
||||||
|
tableHeader: {
|
||||||
|
bold: true,
|
||||||
|
fontSize: 13,
|
||||||
|
color: 'black'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pageMargins: [50, 50, 50, 15]
|
||||||
|
};
|
||||||
|
|
||||||
|
pdfMake.createPdf(doc).download('predictions-' + this.generateTimestamp() + '.pdf');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user