import { ChartDef } from '@/services/data/tuples/chart-def/chart-def-types' ; import { ChartDataSet , ChartDataSetRow , ChartDataSetRows , ChartType } from '@/services/data/tuples/chart-types' ; import { Report , ReportIndicatorArithmetic } from '@/services/data/tuples/report-types' ; import { getDimensionColumnIndexOffset } from './dimension-utils' ; import { createNumberFormat } from './number-format' ; import { buildTreeData } from './tree-data-builder' ; import { ChartOptions , ChartUtils } from './types' ; export interface Legend { name : string ; rows : ChartDataSetRows ; } export type ReportValidator = ( report : Report , def : ChartDef ) => ReportValidationSuccess | ReportValidationFailure ; export interface ReportValidationFailure { readonly pass : false ; readonly error : string ; } export interface ReportValidationSuccess { readonly pass : true ; } export const VALIDATION_SUCCESS = { pass : true } ; export const createValidationFailure = ( message ) => ( { pass : false , error : message } ) ; export const DEFAULT_INDICATOR_COUNT_VALIDATOR = ( report , def ) => { const count = report . indicators ?. length || 0 ; const { minIndicatorCount = 1 , maxIndicatorCount = 1 } = def ; if ( count > maxIndicatorCount ) { return createValidationFailure ( 'Too many indicators exits.' ) ; } if ( count < minIndicatorCount ) { return createValidationFailure ( 'Do not have enough indicator(s).' ) ; } return VALIDATION_SUCCESS ; } ; export const DEFAULT_DIMENSION_COUNT_VALIDATOR = ( report , def ) => { const count = report . dimensions ?. length || 0 ; const { minDimensionCount = 1 , maxDimensionCount = 1 } = def ; if ( count > maxDimensionCount ) { return createValidationFailure ( 'Too many dimensions exits.' ) ; } if ( count < minDimensionCount ) { return createValidationFailure ( 'Do not have enough dimension(s).' ) ; } return VALIDATION_SUCCESS ; } ; export abstract class DefaultChartUtils implements ChartUtils { private readonly _def : ChartDef ; protected constructor ( def ) { this . _def = def ; } protected get def ( ) : ChartDef { return this . _def ; } protected get indicatorCountValidator ( ) : ReportValidator { return DEFAULT_INDICATOR_COUNT_VALIDATOR ; } protected get dimensionCountValidator ( ) : ReportValidator { return DEFAULT_DIMENSION_COUNT_VALIDATOR ; } getDef ( ) { return this . def ; } getType ( ) { return this . def . type ; } shouldHasIndicator ( ) { return true ; } getMaxIndicatorCount ( ) { const { maxIndicatorCount = Infinity } = this . def ; return maxIndicatorCount ; } canAppendIndicators ( report ) { const currentCount = report . indicators ?. length || 0 ; return currentCount < this . getMaxIndicatorCount ( ) ; } canReduceIndicators ( report ) { const currentCount = report . indicators ?. length || 0 ; const { minIndicatorCount = 1 } = this . def ; return currentCount > minIndicatorCount ; } defendIndicatorMinCount ( report ) { if ( ! report . indicators ) { report . indicators = [ ] ; } const { minIndicatorCount = 1 } = this . def ; new Array ( Math . max ( minIndicatorCount - report . indicators . length , 0 ) ) . fill ( 1 ) . forEach ( ( ) => report . indicators . push ( { columnId : '' , name : '' , arithmetic : ReportIndicatorArithmetic . NONE } ) ) ; } defendIndicatorMaxCount ( report ) { if ( ! report . indicators ) { report . indicators = [ ] ; } const maxIndicatorCount = this . getMaxIndicatorCount ( ) ; if ( report . indicators . length > maxIndicatorCount ) { report . indicators . length = maxIndicatorCount ; } } shouldHasDimension ( ) { return true ; } getMaxDimensionCount ( ) { const { maxDimensionCount = Infinity } = this . def ; return maxDimensionCount ; } canAppendDimensions ( report ) { const currentCount = report . dimensions ?. length || 0 ; return currentCount < this . getMaxDimensionCount ( ) ; } canReduceDimensions ( report ) { const currentCount = report . dimensions ?. length || 0 ; const { minDimensionCount = 1 } = this . def ; return currentCount > minDimensionCount ; } defendDimensionMinCount ( report ) { if ( ! report . dimensions ) { report . dimensions = [ ] ; } const { minDimensionCount = 1 } = this . def ; new Array ( Math . max ( minDimensionCount - report . dimensions . length , 0 ) ) . fill ( 1 ) . forEach ( ( ) => report . dimensions . push ( { columnId : '' , name : '' } ) ) ; } defendDimensionMaxCount ( report ) { if ( ! report . dimensions ) { report . dimensions = [ ] ; } const maxDimensionCount = this . getMaxDimensionCount ( ) ; if ( report . dimensions . length > maxDimensionCount ) { report . dimensions . length = maxDimensionCount ; } } shouldHasTruncation ( ) { return true ; } shouldHasFunnel ( ) { return true ; } defend ( report ) { this . defendIndicatorMinCount ( report ) ; this . defendIndicatorMaxCount ( report ) ; this . defendDimensionMinCount ( report ) ; this . defendDimensionMaxCount ( report ) ; } validate ( report ) { const validationResult = this . getValidators ( ) . reduce ( ( prev , validate ) => { if ( ! prev . pass ) { return prev ; } else { return validate ( report , this . def ) ; } } , { pass : true } ) ; return validationResult . pass || validationResult . error ; } abstract buildOptions ( report , dataset ) ; findColumnExtremum ( dataset , columnIndex ) { return dataset . data . reduce ( ( extremum , row ) => { const value = ( row [ columnIndex ] || 0 ) as number ; if ( value > extremum . max ) { extremum . max = value ; } if ( value < extremum . min ) { extremum . min = value ; } return extremum ; } , { max : - Infinity , min : Infinity } ) ; } ; protected getValidators ( ) { return [ this . indicatorCountValidator , this . dimensionCountValidator ] ; } protected formatNumber ( value , decimal = 0 ) { if ( typeof value === 'number' ) { return createNumberFormat ( decimal ) ( value ) ; } else { return value ; } } ; /**
     * get dimension column index offset from starting.
     * in dataset, order of columns as below,
     * indicator 1, indicator 2, ..., indicator n, dimension 1, dimension 2, ..., dimension n.
     */ protected getDimensionColumnIndexOffset ( report ) { return Math . min ( getDimensionColumnIndexOffset ( report ) , this . getMaxIndicatorCount ( ) ) ; } ; protected buildDescartesByDimensions ( report , dataset ) { const { data } = dataset ; const { dimensions } = report ; const columnIndexOffset = this . getDimensionColumnIndexOffset ( report ) ; const dimensionIndexes = new Array ( dimensions . length ) . fill ( 1 ) . map ( ( v , index ) => index + columnIndexOffset ) ; return data . map ( row => { return { value : dimensionIndexes . map ( index => row [ index ] ) . join ( ',' ) , row } ; } ) ; } ; protected buildTreeData ( report , dataset ) { return buildTreeData ( report , dataset ) ; } }