virool-pivot
Version:
A web-based exploratory visualization UI for Druid.io
262 lines (229 loc) • 9.7 kB
text/typescript
import { $, AttributeInfo, RefExpression } from 'plywood';
import { DataSource, DataSourceJS, RefreshRule, Dimension, Measure } from '../../../common/models/index';
export function attributeToYAML(attribute: AttributeInfo): string[] {
var lines: string[] = [
` - name: ${attribute.name}`,
` type: ${attribute.type}`
];
if (attribute.special) {
lines.push(` special: ${attribute.special}`);
}
lines.push('');
return lines;
}
export function dimensionToYAML(dimension: Dimension): string[] {
var lines: string[] = [
` - name: ${dimension.name}`,
` title: ${dimension.title}`
];
if (dimension.kind !== 'string') {
lines.push(` kind: ${dimension.kind}`);
}
lines.push(` expression: ${dimension.expression.toString()}`);
lines.push('');
return lines;
}
export function measureToYAML(measure: Measure): string[] {
var lines: string[] = [
` - name: ${measure.name}`,
` title: ${measure.title}`
];
var ex = measure.expression;
var lastAction = ex.lastAction();
var comment = ''; // Make a comment if this is a .sum(min_blah) or similar
if (
lastAction.action === 'sum' &&
/\bmin\b|\bmax\b|\bunique\b|\buniques\b/i.test(((lastAction.expression as RefExpression).name || '').replace(/_/g, ' ')) // \b matches "_" :-(
) {
comment = ' # double check please';
}
lines.push(` expression: ${ex.toString()}${comment}`);
var format = measure.format;
if (format !== Measure.DEFAULT_FORMAT) {
lines.push(` format: ${format}`);
}
lines.push('');
return lines;
}
export function dataSourceToYAML(dataSource: DataSource, withComments: boolean): string[] {
var lines: string[] = [
` - name: ${dataSource.name}`,
` title: ${dataSource.title}`,
` engine: ${dataSource.engine}`,
` source: ${dataSource.source}`,
``
];
var timeAttribute = dataSource.timeAttribute;
if (timeAttribute && !(dataSource.engine === 'druid' && timeAttribute.name === '__time')) {
if (withComments) {
lines.push(" # The primary time attribute of the data refers to the attribute that must always be filtered on");
lines.push(" # This is particularly useful for Druid data sources as they must always have a time filter.");
}
lines.push(` timeAttribute: ${timeAttribute.name}`, '');
}
var refreshRule = dataSource.refreshRule;
if (withComments) {
lines.push(" # The refresh rule describes how often the data source looks for new data. Default: 'query'/PT1M (every minute)");
}
lines.push(` refreshRule:`);
lines.push(` rule: ${refreshRule.rule}`);
if (refreshRule.time) {
lines.push(` time: ${refreshRule.time.toISOString()}`);
}
if (refreshRule.refresh) {
lines.push(` refresh: ${refreshRule.refresh.toString()}`);
}
lines.push('');
var defaultTimezone = dataSource.defaultTimezone;
if (withComments) {
lines.push(" # The default timezone for this dataset to operate in defaults to UTC");
}
if (defaultTimezone.equals(DataSource.DEFAULT_TIMEZONE)) {
if (withComments) {
lines.push(` #defaultTimezone: ${DataSource.DEFAULT_TIMEZONE.toString()}`, '');
}
} else {
lines.push(` defaultTimezone: ${defaultTimezone.toString()}}`, '');
}
var defaultDuration = dataSource.defaultDuration;
if (withComments) {
lines.push(` # The default duration for the time filter (if not set ${DataSource.DEFAULT_DURATION.toString()} is used)`);
}
if (defaultDuration.equals(DataSource.DEFAULT_DURATION)) {
if (withComments) {
lines.push(` #defaultDuration: ${DataSource.DEFAULT_DURATION.toString()}`, '');
}
} else {
lines.push(` defaultDuration: ${defaultDuration.toString()}`, '');
}
var defaultSortMeasure = dataSource.defaultSortMeasure;
if (withComments) {
lines.push(" # The default sort measure name (if not set the first measure name is used)");
}
lines.push(` defaultSortMeasure: ${defaultSortMeasure}`, '');
var defaultPinnedDimensions = dataSource.defaultPinnedDimensions.toArray();
if (withComments) {
lines.push(" # The names of dimensions that are pinned by default (in order that they will appear in the pin bar)");
}
lines.push(` defaultPinnedDimensions: ${JSON.stringify(defaultPinnedDimensions)}`, '');
var introspection = dataSource.introspection;
if (withComments) {
lines.push(
" # How the dataset should be introspected",
" # possible options are:",
" # * none - Do not do any introspection, take what is written in the config as the rule of law.",
" # * no-autofill - Introspect the datasource but do not automatically generate dimensions or measures",
" # * autofill-dimensions-only - Introspect the datasource, automatically generate dimensions only",
" # * autofill-measures-only - Introspect the datasource, automatically generate measures only",
" # * autofill-all - (default) Introspect the datasource, automatically generate dimensions and measures"
);
}
lines.push(` introspection: ${introspection}`, '');
var attributeOverrides = dataSource.attributeOverrides;
if (withComments) {
lines.push(" # The list of attribute overrides in case introspection get something wrong");
}
lines.push(' attributeOverrides:');
if (withComments) {
lines.push(
" # A general attribute override looks like so:",
" #",
" # name: user_unique",
" # ^ the name of the attribute (the column in the database)",
" #",
" # type: STRING",
" # ^ (optional) plywood type of the attribute",
" #",
" # special: unique",
" # ^ (optional) any kind of special significance associated with this attribute",
""
);
}
lines = lines.concat.apply(lines, attributeOverrides.map(attributeToYAML));
var dimensions = dataSource.dimensions.toArray();
if (withComments) {
lines.push(" # The list of dimensions defined in the UI. The order here will be reflected in the UI");
}
lines.push(' dimensions:');
if (withComments) {
lines.push(
" # A general dimension looks like so:",
" #",
" # name: channel",
" # ^ the name of the dimension as used in the URL (you should try not to change these)",
" #",
" # title: The Channel",
" # ^ (optional) the human readable title. If not set a title is generated from the 'name'",
" #",
" # kind: string",
" # ^ (optional) the kind of the dimension. Can be 'string', 'time', 'number', or 'boolean'. Defaults to 'string'",
" #",
" # expression: $channel",
" # ^ (optional) the Plywood bucketing expression for this dimension. Defaults to '$name'",
" # if, say, channel was called 'cnl' in the data you would put '$cnl' here",
" # See also the expressions API reference: https://plywood.imply.io/expressions",
" #",
" # url: string",
" # ^ (optional) a url (including protocol) associated with the dimension, with optional token '%s'",
" # that is replaced by the dimension value to generate links specific to each value.",
""
);
}
lines = lines.concat.apply(lines, dimensions.map(dimensionToYAML));
if (withComments) {
lines.push(
" # This is the place where you might want to add derived dimensions.",
" #",
" # Here are some examples of possible derived dimensions:",
" #",
" # - name: is_usa",
" # title: Is USA?",
" # expression: $country == 'United States'",
" #",
" # - name: file_version",
" # expression: $filename.extract('(\\d+\\.\\d+\\.\\d+)')",
""
);
}
var measures = dataSource.measures.toArray();
if (withComments) {
lines.push(" # The list of measures defined in the UI. The order here will be reflected in the UI");
}
lines.push(` measures:`);
if (withComments) {
lines.push(
" # A general measure looks like so:",
" #",
" # name: avg_revenue",
" # ^ the name of the dimension as used in the URL (you should try not to change these)",
" #",
" # title: Average Revenue",
" # ^ (optional) the human readable title. If not set a title is generated from the 'name'",
" #",
" # expression: $main.sum($revenue) / $main.sum($volume) * 10",
" # ^ (optional) the Plywood bucketing expression for this dimension.",
" # Usually defaults to '$main.sum($name)' but if the name contains 'min' or 'max' will use that as the aggregate instead of sum.",
" # this is the place to define your fancy formulas",
""
);
}
lines = lines.concat.apply(lines, measures.map(measureToYAML));
if (withComments) {
lines.push(
" # This is the place where you might want to add derived measures (a.k.a Post Aggregators).",
" #",
" # Here are some examples of possible derived measures:",
" #",
" # - name: ecpm",
" # title: eCPM",
" # expression: $main.sum($revenue) / $main.sum($impressions) * 1000",
" #",
" # - name: usa_revenue",
" # title: USA Revenue",
" # expression: $main.filter($country == 'United States').sum($revenue)",
""
);
}
lines.push('');
return lines;
}