import { Injectable } from "@angular/core" ; import { Papa } from "ngx-papaparse" ; import { entityListSortingAccessor } from "../../entity-components/entity-subrecord/entity-subrecord/sorting-accessor" ; import { ExportColumnConfig } from "./export-column-config" ; import { QueryService } from "../../../features/reporting/query.service" ; import moment from "moment" ; /**
 * Prepare data for export in csv format.
 */ @ Injectable ( { providedIn : "root" , } ) export class ExportService { /** CSV row separator */ static readonly SEPARATOR_ROW = "\n" ; /** CSV column/field separator */ static readonly SEPARATOR_COL = "," ; constructor ( private papa , private queryService ) { } param data returns createJson ( data ) { return JSON . stringify ( data ) ; } param data param config returns async createCsv ( data , config = this . generateExportConfigFromData ( data ) ) { const flattenedExportRows = [ ] ; for ( const dataRow of data ) { const extendedExportableRows = await this . generateExportRows ( dataRow , config ) ; flattenedExportRows . push ( ... extendedExportableRows ) ; } const readableExportRow = flattenedExportRows . map ( ( row ) => { const readableRow = { } ; Object . keys ( row ) . forEach ( ( key ) => { if ( row [ key ] instanceof Date ) { readableRow [ key ] = moment ( row [ key ] ) . toISOString ( true ) ; } else { readableRow [ key ] = entityListSortingAccessor ( row , key ) ; } } ) ; return readableRow ; } ) ; return this . papa . unparse ( { data : readableExportRow } , { quotes : true , header : true , newline : ExportService . SEPARATOR_ROW } ) ; } param data private private generateExportConfigFromData ( data ) { const uniqueKeys = new Set < string > ( ) ; data . forEach ( ( obj ) => Object . keys ( obj ) . forEach ( ( key ) => uniqueKeys . add ( key ) ) ) ; const columnConfigs = [ ] ; uniqueKeys . forEach ( ( key ) => columnConfigs . push ( { query : "." + key } ) ) ; return columnConfigs ; } param object param config returns private private async generateExportRows ( object , config ) { let exportRows = [ { } ] ; for ( const exportColumnConfig of config ) { const partialExportObjects = await this . getExportRowsForColumn ( object , exportColumnConfig ) ; exportRows = this . mergePartialExportRows ( exportRows , partialExportObjects ) ; } return exportRows ; } param object param exportColumnConfig private private async getExportRowsForColumn ( object , exportColumnConfig ) { const label = exportColumnConfig . label ?? exportColumnConfig . query . replace ( "." , "" ) ; const value = await this . getValueForQuery ( exportColumnConfig , object ) ; if ( ! exportColumnConfig . subQueries ) { const result = { } ; result [ label ] = value ; return [ result ] ; } else { const additionalRows = [ ] ; for ( const v of value ) { const addRows = await this . generateExportRows ( v , exportColumnConfig . subQueries ) ; additionalRows . push ( ... addRows ) ; } return additionalRows ; } } private async getValueForQuery ( exportColumnConfig , object ) { const value = await this . queryService . queryData ( exportColumnConfig . query , null , null , [ object ] ) ; if ( ! exportColumnConfig . subQueries && value . length === 1 ) { return value [ 0 ] ; } return value ; } param exportRows param additionalExportRows private private mergePartialExportRows ( exportRows , additionalExportRows ) { const rowsOfRows = additionalExportRows . map ( ( addRow ) => exportRows . map ( ( row ) => Object . assign ( { } , row , addRow ) ) ) ; return rowsOfRows . reduce ( ( acc , rowOfRows ) => acc . concat ( rowOfRows ) , [ ] ) ; } } interface ExportRow { [ key ] : any ; }