UNPKG

@gooddata/react-components

Version:

GoodData.UI - A powerful JavaScript library for building analytical applications

357 lines (313 loc) • 13.2 kB
// (C) 2007-2020 GoodData Corporation import get = require("lodash/get"); import flatMap = require("lodash/flatMap"); import { VisualizationObject, Localization } from "@gooddata/typings"; import { string as stringUtils } from "@gooddata/js-utils"; import DerivedMeasureTitleSuffixFactory from "../factory/DerivedMeasureTitleSuffixFactory"; import ArithmeticMeasureTitleFactory from "../factory/ArithmeticMeasureTitleFactory"; import { OverTimeComparisonType, OverTimeComparisonTypes } from "../interfaces/OverTimeComparison"; import { IMeasureTitleProps } from "../interfaces/MeasureTitle"; import IMeasureDefinitionType = VisualizationObject.IMeasureDefinitionType; import IMeasure = VisualizationObject.IMeasure; import IBucket = VisualizationObject.IBucket; import BucketItem = VisualizationObject.BucketItem; import IVisualizationObjectContent = VisualizationObject.IVisualizationObjectContent; import isMeasure = VisualizationObject.isMeasure; const DEFAULT_MAX_ARITHMETIC_MEASURE_TITLE_LENGTH = 50; function getAllMeasures(mdObject: IVisualizationObjectContent): IMeasure[] { const buckets: IBucket[] = get<IVisualizationObjectContent, "buckets", IBucket[]>( mdObject, "buckets", [], ); const allBucketItems = flatMap<IBucket, BucketItem>(buckets, (bucket: IBucket) => bucket.items); return allBucketItems.reduce((measureItems, bucketItem) => { if (VisualizationObject.isMeasure(bucketItem)) { measureItems.push(bucketItem); } return measureItems; }, []); } function getMasterMeasureIdentifier(definition: IMeasureDefinitionType): string | null { if (VisualizationObject.isPopMeasureDefinition(definition)) { return definition.popMeasureDefinition.measureIdentifier; } else if (VisualizationObject.isPreviousPeriodMeasureDefinition(definition)) { return definition.previousPeriodMeasure.measureIdentifier; } return null; } function findOverTimeComparisonType(measureDefinitionType: IMeasureDefinitionType): OverTimeComparisonType { if (VisualizationObject.isPopMeasureDefinition(measureDefinitionType)) { return OverTimeComparisonTypes.SAME_PERIOD_PREVIOUS_YEAR; } else if (VisualizationObject.isPreviousPeriodMeasureDefinition(measureDefinitionType)) { return OverTimeComparisonTypes.PREVIOUS_PERIOD; } return OverTimeComparisonTypes.NOTHING; } function containsMeasureTitleItem(measureTitleProps: IMeasureTitleProps[], localIdentifier: string): boolean { return measureTitleProps.some(prop => prop.localIdentifier === localIdentifier); } function containsMeasureTitleItems( measureTitleProps: IMeasureTitleProps[], localIdentifiers: string[], ): boolean { return localIdentifiers.every(identifier => containsMeasureTitleItem(measureTitleProps, identifier)); } function findMeasureTitleItem( measureTitles: IMeasureTitleProps[], localIdentifier: string, ): IMeasureTitleProps | null { return measureTitles.find(prop => prop.localIdentifier === localIdentifier) || null; } function findTitleForDerivedMeasure( measureDefinitionType: IMeasureDefinitionType, measureTitleProps: IMeasureTitleProps[], suffixFactory: DerivedMeasureTitleSuffixFactory, ): string { const masterMeasureIdentifier = getMasterMeasureIdentifier(measureDefinitionType); if (masterMeasureIdentifier === null) { return undefined; } const measureProps = findMeasureTitleItem(measureTitleProps, masterMeasureIdentifier); if (measureProps === null) { return undefined; } const derivedMeasureTitleBase = measureProps.alias || measureProps.title || ""; const overTimeComparisonType = findOverTimeComparisonType(measureDefinitionType); return derivedMeasureTitleBase + suffixFactory.getSuffix(overTimeComparisonType); } function buildMeasureTitle(bucketItem: IMeasure): IMeasureTitleProps | null { if (VisualizationObject.isMeasureDefinition(bucketItem.measure.definition)) { const { localIdentifier, title, alias } = bucketItem.measure; return { localIdentifier, title, alias, }; } return null; } function buildArithmeticMeasureTitle( bucketItem: IMeasure, measureTitleProps: IMeasureTitleProps[], titleFactory: ArithmeticMeasureTitleFactory, maxArithmeticMeasureTitleLength: number, ): IMeasureTitleProps | null { if (VisualizationObject.isArithmeticMeasureDefinition(bucketItem.measure.definition)) { const { alias, localIdentifier } = bucketItem.measure; const arithmeticMeasure = bucketItem.measure.definition.arithmeticMeasure; if (containsMeasureTitleItems(measureTitleProps, arithmeticMeasure.measureIdentifiers)) { const fullLengthTitle = titleFactory.getTitle( { operator: arithmeticMeasure.operator, masterMeasureLocalIdentifiers: arithmeticMeasure.measureIdentifiers, }, measureTitleProps, ); const title = stringUtils.shortenText(fullLengthTitle, { maxLength: maxArithmeticMeasureTitleLength, }); return { localIdentifier, title, alias, }; } } return null; } function buildDerivedMeasureTitle( bucketItem: IMeasure, measureTitleProps: IMeasureTitleProps[], suffixFactory: DerivedMeasureTitleSuffixFactory, ): IMeasureTitleProps | null { if ( VisualizationObject.isPopMeasureDefinition(bucketItem.measure.definition) || VisualizationObject.isPreviousPeriodMeasureDefinition(bucketItem.measure.definition) ) { const { alias, localIdentifier } = bucketItem.measure; const definition = bucketItem.measure.definition; const masterMeasureIdentifier = getMasterMeasureIdentifier(definition); if (containsMeasureTitleItem(measureTitleProps, masterMeasureIdentifier)) { return { localIdentifier, title: findTitleForDerivedMeasure(definition, measureTitleProps, suffixFactory), alias, }; } } return null; } function buildMeasureTitles( measureBucketItems: VisualizationObject.IMeasure[], locale: Localization.ILocale, maxArithmeticMeasureTitleLength: number, ): IMeasureTitleProps[] { const titleFactory = new ArithmeticMeasureTitleFactory(locale); const suffixFactory = new DerivedMeasureTitleSuffixFactory(locale); const measureTitleProps: IMeasureTitleProps[] = []; let isMeasureTitlePropsChanged = true; while (isMeasureTitlePropsChanged) { isMeasureTitlePropsChanged = false; measureBucketItems.forEach(bucketItem => { if (!containsMeasureTitleItem(measureTitleProps, bucketItem.measure.localIdentifier)) { const newMeasureTitleProp = buildMeasureTitle(bucketItem) || buildArithmeticMeasureTitle( bucketItem, measureTitleProps, titleFactory, maxArithmeticMeasureTitleLength, ) || buildDerivedMeasureTitle(bucketItem, measureTitleProps, suffixFactory); if (newMeasureTitleProp !== null) { measureTitleProps.push(newMeasureTitleProp); isMeasureTitlePropsChanged = true; } } }); } return measureTitleProps; } function updateBucketItemTitle( bucketItem: VisualizationObject.BucketItem, measureTitleProps: IMeasureTitleProps[], ): BucketItem { if (isMeasure(bucketItem)) { const measureTitleProp = findMeasureTitleItem(measureTitleProps, bucketItem.measure.localIdentifier); if (measureTitleProp !== null) { const { title, alias } = measureTitleProp; return { ...bucketItem, measure: { ...bucketItem.measure, title, alias, }, }; } } return bucketItem; } function updateBucketTitles( bucket: VisualizationObject.IBucket, measureTitleProps: IMeasureTitleProps[], ): VisualizationObject.IBucket { return { ...bucket, items: bucket.items.map(bucketItem => updateBucketItemTitle(bucketItem, measureTitleProps)), }; } function updateVisualizationObjectTitles( mdObject: IVisualizationObjectContent, measureTitleProps: IMeasureTitleProps[], ): IVisualizationObjectContent { return { ...mdObject, buckets: mdObject.buckets.map(bucket => updateBucketTitles(bucket, measureTitleProps)), }; } /** * The function fills the titles of the measures that does not have it set. * * The derived measures * have the title built from the current name of the master measure and suffix based on the derived measure type. * * The arithmetic measures * have the title built from the current names of the referenced master measures and type of the arithmetic * operation. * * @param {VisualizationObject.IVisualizationObjectContent} mdObject - metadata object that must be processed. * @param {Localization.ILocale} locale - locale used for localization of the measure titles. * @param {number} maxArithmeticMeasureTitleLength - maximum length of generated arithmetic measures titles. * Longer names will be shortened. Default value is 50 characters. * * @returns {VisualizationObject.IVisualizationObjectContent} * * @internal */ export function fillMissingTitles( mdObject: IVisualizationObjectContent, locale: Localization.ILocale, maxArithmeticMeasureTitleLength: number = DEFAULT_MAX_ARITHMETIC_MEASURE_TITLE_LENGTH, ): IVisualizationObjectContent { const measureBucketItems = getAllMeasures(mdObject); const measureTitleProps = buildMeasureTitles(measureBucketItems, locale, maxArithmeticMeasureTitleLength); return updateVisualizationObjectTitles(mdObject, measureTitleProps); } function isAdhocMeasure(definition: IMeasureDefinitionType): boolean { return ( VisualizationObject.isMeasureDefinition(definition) && (!!definition.measureDefinition.aggregation || !!definition.measureDefinition.computeRatio || !!definition.measureDefinition.filters) ); } function isNonSimpleMeasure(definition: IMeasureDefinitionType): boolean { return ( VisualizationObject.isArithmeticMeasureDefinition(definition) || VisualizationObject.isPopMeasureDefinition(definition) || VisualizationObject.isPreviousPeriodMeasureDefinition(definition) ); } function removeTitleForSimpleMeasure(bucket: IBucket): IBucket { return { ...bucket, items: bucket.items.map( (bucketItem: VisualizationObject.BucketItem): VisualizationObject.BucketItem => { if ( VisualizationObject.isMeasure(bucketItem) && !isAdhocMeasure(bucketItem.measure.definition) && !isNonSimpleMeasure(bucketItem.measure.definition) ) { delete bucketItem.measure.title; } return bucketItem; }, ), }; } function isBucketContainingMeasureDateFilter(bucket: IBucket): boolean { return !!bucket.items.find( (bucketItem: VisualizationObject.BucketItem): boolean => { if (!VisualizationObject.isMeasure(bucketItem)) { return false; } const definition: IMeasureDefinitionType = bucketItem.measure.definition; if (!VisualizationObject.isMeasureDefinition(definition)) { return false; } const filters: VisualizationObject.VisualizationObjectFilter[] = definition.measureDefinition.filters; return filters && !!filters.find(VisualizationObject.isDateFilter); }, ); } function isBucketsContainingMeasureDateFilter(buckets: IBucket[]): boolean { return !!buckets.find(isBucketContainingMeasureDateFilter); } /** * This function ignores the titles of simple measures. * * For simple measures, their titles are removed. * For adhoc or non-simple measures, their titles are left intact. * * @param {VisualizationObject.IVisualizationObjectContent} mdObject - metadata object that must be processed. * * @returns {VisualizationObject.IVisualizationObjectContent} * * @internal */ export function ignoreTitlesForSimpleMeasures( mdObject: IVisualizationObjectContent, ): IVisualizationObjectContent { if (isBucketsContainingMeasureDateFilter(mdObject.buckets)) { // If there is one bucket containing a measure with date filter, all other measures are considered adhoc measures // and all the titles should be left intact return mdObject; } return { ...mdObject, buckets: mdObject.buckets.map(removeTitleForSimpleMeasure), }; }