UNPKG

virool-pivot

Version:

A web-based exploratory visualization UI for Druid.io

213 lines (185 loc) 5.76 kB
import { List } from 'immutable'; import { Class, Instance, isInstanceOf } from 'immutable-class'; import * as numeral from 'numeral'; import { $, Expression, ExpressionJS, Datum, ApplyAction, AttributeInfo, ChainExpression, helper } from 'plywood'; import { verifyUrlSafeName, makeTitle, makeUrlSafeName } from '../../utils/general/general'; function formatFnFactory(format: string): (n: number) => string { return (n: number) => { if (isNaN(n) || !isFinite(n)) return '-'; return numeral(n).format(format); }; } export interface MeasureValue { name: string; title?: string; expression?: Expression; format?: string; } export interface MeasureJS { name: string; title?: string; expression?: ExpressionJS | string; format?: string; } var check: Class<MeasureValue, MeasureJS>; export class Measure implements Instance<MeasureValue, MeasureJS> { static DEFAULT_FORMAT = '0,0.0 a'; static INTEGER_FORMAT = '0,0 a'; static isMeasure(candidate: any): candidate is Measure { return isInstanceOf(candidate, Measure); } static getMeasure(measures: List<Measure>, measureName: string): Measure { if (!measureName) return null; measureName = measureName.toLowerCase(); // Case insensitive return measures.find(measure => measure.name.toLowerCase() === measureName); } /** * Look for all instances of aggregateAction($blah) and return the blahs * @param ex * @returns {string[]} */ static getAggregateReferences(ex: Expression): string[] { var references: string[] = []; ex.forEach((ex: Expression) => { if (ex instanceof ChainExpression) { var actions = ex.actions; for (var action of actions) { if (action.isAggregate()) { references = references.concat(action.getFreeReferences()); } } } }); return helper.deduplicateSort(references); } /** * Look for all instances of countDistinct($blah) and return the blahs * @param ex * @returns {string[]} */ static getCountDistinctReferences(ex: Expression): string[] { var references: string[] = []; ex.forEach((ex: Expression) => { if (ex instanceof ChainExpression) { var actions = ex.actions; for (var action of actions) { if (action.action === 'countDistinct') { references = references.concat(action.getFreeReferences()); } } } }); return helper.deduplicateSort(references); } static measuresFromAttributeInfo(attribute: AttributeInfo): Measure[] { var { name, special } = attribute; var $main = $('main'); var ref = $(name); if (special) { if (special === 'unique' || special === 'theta') { return [ new Measure({ name: makeUrlSafeName(name), expression: $main.countDistinct(ref) }) ]; } else if (special === 'histogram') { return [ new Measure({ name: makeUrlSafeName(name + '_p95'), expression: $main.quantile(ref, 0.95) }), new Measure({ name: makeUrlSafeName(name + '_p99'), expression: $main.quantile(ref, 0.99) }) ]; } } var expression = $main.sum(ref); var makerAction = attribute.makerAction; if (makerAction) { switch (makerAction.action) { case 'min': expression = $main.min(ref); break; case 'max': expression = $main.max(ref); break; //default: // sum, count } } return [new Measure({ name: makeUrlSafeName(name), expression })]; } static fromJS(parameters: MeasureJS): Measure { var name = parameters.name; return new Measure({ name, title: parameters.title, expression: parameters.expression ? Expression.fromJSLoose(parameters.expression) : $('main').sum($(name)), format: parameters.format }); } public name: string; public title: string; public expression: Expression; public format: string; public formatFn: (n: number) => string; constructor(parameters: MeasureValue) { var name = parameters.name; verifyUrlSafeName(name); this.name = name; this.title = parameters.title || makeTitle(name); var expression = parameters.expression; if (!expression) throw new Error('measure must have expression'); this.expression = expression; var format = parameters.format || Measure.DEFAULT_FORMAT; if (format[0] === '(') throw new Error('can not have format that uses ( )'); this.format = format; this.formatFn = formatFnFactory(format); } public valueOf(): MeasureValue { return { name: this.name, title: this.title, expression: this.expression, format: this.format }; } public toJS(): MeasureJS { var js: MeasureJS = { name: this.name, title: this.title, expression: this.expression.toJS() }; if (this.format !== Measure.DEFAULT_FORMAT) js.format = this.format; return js; } public toJSON(): MeasureJS { return this.toJS(); } public toString(): string { return `[Measure: ${this.name}]`; } public equals(other: Measure): boolean { return Measure.isMeasure(other) && this.name === other.name && this.title === other.title && this.expression.equals(other.expression) && this.format === other.format; } public toApplyAction(): ApplyAction { var { name, expression } = this; return new ApplyAction({ name: name, expression: expression }); } public formatDatum(datum: Datum): string { return this.formatFn(datum[this.name] as number); } } check = Measure;