mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[I18n] Translate Timelion (#23880)
* Add translations for timelion plugin * Fix bugs * Fix messages ids, resolve comments * Update translations * Refactor links messages * Fix values bug * Use template literals to avoid single quote escaping
This commit is contained in:
parent
6d79657b41
commit
ff8675d1c4
70 changed files with 1572 additions and 513 deletions
|
@ -2,7 +2,7 @@
|
|||
"paths": {
|
||||
"common.ui": "src/ui",
|
||||
"console": "src/core_plugins/console",
|
||||
"inputControl":"src/core_plugins/input_control_vis",
|
||||
"inputControl": "src/core_plugins/input_control_vis",
|
||||
"kbn": "src/core_plugins/kibana",
|
||||
"kbnVislibVisTypes": "src/core_plugins/kbn_vislib_vis_types",
|
||||
"markdownVis": "src/core_plugins/markdown_vis",
|
||||
|
@ -12,6 +12,7 @@
|
|||
"regionMap": "src/core_plugins/region_map",
|
||||
"statusPage": "src/core_plugins/status_page",
|
||||
"tileMap": "src/core_plugins/tile_map",
|
||||
"timelion": "src/core_plugins/timelion",
|
||||
"tagCloud": "src/core_plugins/tagcloud",
|
||||
"xpack.grokDebugger": "x-pack/plugins/grokdebugger",
|
||||
"xpack.idxMgmt": "x-pack/plugins/index_management",
|
||||
|
|
|
@ -103,7 +103,8 @@ export default function (kibana) {
|
|||
description: `<em>[experimental]</em> Your API key from www.quandl.com`,
|
||||
category: ['timelion'],
|
||||
}
|
||||
}
|
||||
},
|
||||
translations: [],
|
||||
},
|
||||
init: require('./init.js'),
|
||||
});
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import _ from 'lodash';
|
||||
import processFunctionDefinition from './server/lib/process_function_definition';
|
||||
|
||||
|
@ -33,7 +34,15 @@ export default function (server) {
|
|||
}
|
||||
|
||||
function getFunction(name) {
|
||||
if (!functions[name]) throw new Error ('No such function: ' + name);
|
||||
if (!functions[name]) {
|
||||
throw new Error(
|
||||
i18n.translate('timelion.noFunctionErrorMessage', {
|
||||
defaultMessage: 'No such function: {name}',
|
||||
values: { name },
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return functions[name];
|
||||
}
|
||||
|
||||
|
|
|
@ -91,6 +91,7 @@ app.controller('timelion', function (
|
|||
kbnUrl,
|
||||
Notifier,
|
||||
Private,
|
||||
i18n,
|
||||
) {
|
||||
|
||||
// Keeping this at app scope allows us to keep the current page when the user
|
||||
|
@ -114,22 +115,30 @@ app.controller('timelion', function (
|
|||
|
||||
$scope.topNavMenu = [{
|
||||
key: 'new',
|
||||
description: 'New Sheet',
|
||||
description: i18n('timelion.topNavMenu.newDescription', {
|
||||
defaultMessage: 'New Sheet',
|
||||
}),
|
||||
run: function () { kbnUrl.change('/'); },
|
||||
testId: 'timelionNewButton',
|
||||
}, {
|
||||
key: 'add',
|
||||
description: 'Add a chart',
|
||||
description: i18n('timelion.topNavMenu.addDescription', {
|
||||
defaultMessage: 'Add a chart',
|
||||
}),
|
||||
run: function () { $scope.newCell(); },
|
||||
testId: 'timelionAddChartButton',
|
||||
}, {
|
||||
key: 'save',
|
||||
description: 'Save Sheet',
|
||||
description: i18n('timelion.topNavMenu.saveDescription', {
|
||||
defaultMessage: 'Save Sheet',
|
||||
}),
|
||||
template: require('plugins/timelion/partials/save_sheet.html'),
|
||||
testId: 'timelionSaveButton',
|
||||
}, {
|
||||
key: 'delete',
|
||||
description: 'Delete current sheet',
|
||||
description: i18n('timelion.topNavMenu.deleteDescription', {
|
||||
defaultMessage: 'Delete current sheet',
|
||||
}),
|
||||
disableButton: function () {
|
||||
return !savedSheet.id;
|
||||
},
|
||||
|
@ -137,32 +146,55 @@ app.controller('timelion', function (
|
|||
const title = savedSheet.title;
|
||||
function doDelete() {
|
||||
savedSheet.delete().then(() => {
|
||||
toastNotifications.addSuccess(`Deleted '${title}'`);
|
||||
toastNotifications.addSuccess(i18n(
|
||||
'timelion.topNavMenu.delete.modal.successNotificationText',
|
||||
{
|
||||
defaultMessage: `Deleted '{title}'`,
|
||||
values: { title },
|
||||
}
|
||||
));
|
||||
kbnUrl.change('/');
|
||||
}).catch(error => fatalError(error, location));
|
||||
}
|
||||
|
||||
const confirmModalOptions = {
|
||||
onConfirm: doDelete,
|
||||
confirmButtonText: 'Delete',
|
||||
title: `Delete Timelion sheet '${title}'?`
|
||||
confirmButtonText: i18n('timelion.topNavMenu.delete.modal.confirmButtonLabel', {
|
||||
defaultMessage: 'Delete',
|
||||
}),
|
||||
title: i18n('timelion.topNavMenu.delete.modalTitle', {
|
||||
defaultMessage: `Delete Timelion sheet '{title}'?`,
|
||||
values: { title }
|
||||
}),
|
||||
};
|
||||
confirmModal(`You can't recover deleted sheets.`, confirmModalOptions);
|
||||
|
||||
confirmModal(
|
||||
i18n('timelion.topNavMenu.delete.modal.warningText', {
|
||||
defaultMessage: `You can't recover deleted sheets.`,
|
||||
}),
|
||||
confirmModalOptions
|
||||
);
|
||||
},
|
||||
testId: 'timelionDeleteButton',
|
||||
}, {
|
||||
key: 'open',
|
||||
description: 'Open Sheet',
|
||||
description: i18n('timelion.topNavMenu.openDescription', {
|
||||
defaultMessage: 'Open Sheet',
|
||||
}),
|
||||
template: require('plugins/timelion/partials/load_sheet.html'),
|
||||
testId: 'timelionOpenButton',
|
||||
}, {
|
||||
key: 'options',
|
||||
description: 'Options',
|
||||
description: i18n('timelion.topNavMenu.optionsDescription', {
|
||||
defaultMessage: 'Options',
|
||||
}),
|
||||
template: require('plugins/timelion/partials/sheet_options.html'),
|
||||
testId: 'timelionOptionsButton',
|
||||
}, {
|
||||
key: 'help',
|
||||
description: 'Help',
|
||||
description: i18n('timelion.topNavMenu.helpDescription', {
|
||||
defaultMessage: 'Help',
|
||||
}),
|
||||
template: '<timelion-help></timelion-help>',
|
||||
testId: 'timelionDocsButton',
|
||||
}];
|
||||
|
@ -289,7 +321,13 @@ app.controller('timelion', function (
|
|||
savedSheet.timelion_rows = $scope.state.rows;
|
||||
savedSheet.save().then(function (id) {
|
||||
if (id) {
|
||||
toastNotifications.addSuccess(`Saved sheet '${savedSheet.title}'`);
|
||||
toastNotifications.addSuccess(
|
||||
i18n('timelion.saveSheet.successNotificationText', {
|
||||
defaultMessage: `Saved sheet '{title}'`,
|
||||
values: { title: savedSheet.title },
|
||||
})
|
||||
);
|
||||
|
||||
if (savedSheet.id !== $routeParams.id) {
|
||||
kbnUrl.change('/{{id}}', { id: savedSheet.id });
|
||||
}
|
||||
|
@ -307,7 +345,12 @@ app.controller('timelion', function (
|
|||
savedExpression.visState.title = title;
|
||||
savedExpression.save().then(function (id) {
|
||||
if (id) {
|
||||
toastNotifications.addSuccess(`Saved expression '${savedExpression.title}'`);
|
||||
toastNotifications.addSuccess(
|
||||
i18n('timelion.saveExpression.successNotificationText', {
|
||||
defaultMessage: `Saved expression '{title}'`,
|
||||
values: { title: savedExpression.title },
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -21,18 +21,18 @@
|
|||
<button
|
||||
class="timCell__action"
|
||||
ng-click="removeCell($index)"
|
||||
tooltip="Remove"
|
||||
tooltip="{{ ::'timelion.cells.actions.removeTooltip' | i18n: { defaultMessage: 'Remove' } }}"
|
||||
tooltip-append-to-body="1"
|
||||
aria-label="Remove chart"
|
||||
aria-label="{{ ::'timelion.cells.actions.removeAriaLabel' | i18n: { defaultMessage: 'Remove chart' } }}"
|
||||
>
|
||||
<span class="fa fa-remove"></span>
|
||||
</button>
|
||||
<button
|
||||
class="timCell__action"
|
||||
tooltip="Drag to reorder"
|
||||
tooltip="{{ ::'timelion.cells.actions.reorderTooltip' | i18n: { defaultMessage: 'Drag to reorder' } }}"
|
||||
tooltip-append-to-body="1"
|
||||
sv-handle
|
||||
aria-label="Drag to reorder"
|
||||
aria-label="{{ ::'timelion.cells.actions.reorderAriaLabel' | i18n: { defaultMessage: 'Drag to reorder' } }}"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span class="fa fa-arrows"></span>
|
||||
|
@ -40,9 +40,9 @@
|
|||
<button
|
||||
class="timCell__action"
|
||||
ng-click="transient.fullscreen = true"
|
||||
tooltip="Full screen"
|
||||
tooltip="{{ ::'timelion.cells.actions.fullscreenTooltip' | i18n: { defaultMessage: 'Full screen' } }}"
|
||||
tooltip-append-to-body="1"
|
||||
aria-label="Full screen chart"
|
||||
aria-label="{{ ::'timelion.cells.actions.fullscreenAriaLabel' | i18n: { defaultMessage: 'Full screen chart' } }}"
|
||||
>
|
||||
<span class="fa fa-expand"></span>
|
||||
</button>
|
||||
|
|
|
@ -16,12 +16,11 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import panelRegistryProvider from '../../lib/panel_registry';
|
||||
|
||||
require('ui/modules')
|
||||
.get('apps/timelion', [])
|
||||
.directive('chart', function (Private) {
|
||||
.directive('chart', function (Private, i18n) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
|
@ -46,7 +45,12 @@ require('ui/modules')
|
|||
const panelSchema = panelRegistry.byName[seriesList.render.type];
|
||||
|
||||
if (!panelSchema) {
|
||||
$elem.text('No such panel type: ' + seriesList.render.type);
|
||||
$elem.text(
|
||||
i18n('timelion.chart.seriesList.noSchemaWarning', {
|
||||
defaultMessage: 'No such panel type: {renderType}',
|
||||
values: { renderType: seriesList.render.type },
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
<button
|
||||
class="timCell__action"
|
||||
ng-click="transient.fullscreen = false"
|
||||
tooltip="Exit full screen"
|
||||
tooltip="{{ ::'timelion.fullscreen.exitTooltip' | i18n: { defaultMessage: 'Exit full screen' } }}"
|
||||
tooltip-append-to-body="1"
|
||||
aria-label="Exit full screen"
|
||||
aria-label="{{ ::'timelion.fullscreen.exitAriaLabel' | i18n: { defaultMessage: 'Exit full screen' } }}"
|
||||
>
|
||||
<span class="fa fa-compress"></span>
|
||||
</button>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
role="textbox"
|
||||
rows="{{ rows }}"
|
||||
class="timExpressionInput kuiTextArea fullWidth"
|
||||
placeholder="Try a query with .es(*)"
|
||||
placeholder="{{ ::'timelion.expressionInputPlaceholder' | i18n: { defaultMessage: 'Try a query with {esQuery}', values: { esQuery: '.es(*)' } } }}"
|
||||
ng-model="sheet"
|
||||
ng-focus="onFocusInput()"
|
||||
ng-keydown="onKeyDownInput($event)"
|
||||
|
@ -22,7 +22,7 @@
|
|||
ng-mousedown="onMouseDownInput()"
|
||||
ng-mouseup="onMouseUpInput()"
|
||||
ng-click="onClickExpression()"
|
||||
aria-label="Timelion expression"
|
||||
aria-label="{{ ::'timelion.expressionInputAriaLabel' | i18n: { defaultMessage: 'Timelion expression' } }}"
|
||||
aria-multiline="false"
|
||||
aria-autocomplete="list"
|
||||
aria-controls="timelionSuggestionList"
|
||||
|
|
|
@ -26,14 +26,27 @@
|
|||
<h4>
|
||||
<strong>.{{suggestion.name}}()</strong>
|
||||
<small id="timelionSuggestionDescription{{$index}}">
|
||||
{{suggestion.help}}
|
||||
{{suggestion.chainable ? '(Chainable)' : '(Data Source)'}}
|
||||
<span
|
||||
ng-if="suggestion.chainable"
|
||||
i18n-id="timelion.expressionSuggestions.func.description.chainableText"
|
||||
i18n-default-message="{help} (Chainable)"
|
||||
i18n-values="{ help: suggestion.help }"
|
||||
></span>
|
||||
<span
|
||||
ng-if="!suggestion.chainable"
|
||||
i18n-id="timelion.expressionSuggestions.func.description.dataSourceText"
|
||||
i18n-default-message="{help} (Data Source)"
|
||||
i18n-values="{ help: suggestion.help }"
|
||||
></span>
|
||||
</small>
|
||||
</h4>
|
||||
|
||||
<div ng-show="suggestion.args.length > (suggestion.chainable ? 1: 0)">
|
||||
<div ng-show="suggestions.length > 1">
|
||||
<strong>Arguments:</strong>
|
||||
<strong
|
||||
i18n-id="timelion.expressionSuggestions.arg.listTitle"
|
||||
i18n-default-message="Arguments:"
|
||||
></strong>
|
||||
<span ng-repeat="arg in suggestion.args" ng-hide="$index < 1 && suggestion.chainable">
|
||||
<strong>{{arg.name}}</strong>=(<em>{{arg.types.join(' | ')}}</em>)
|
||||
<em ng-show="!$last">,</em>
|
||||
|
@ -43,9 +56,21 @@
|
|||
<div class="timSuggestions__details" ng-show="suggestions.length === 1">
|
||||
<table class="table table-striped table-condensed table-bordered">
|
||||
<thead>
|
||||
<th scope="col">Argument Name</th>
|
||||
<th scope="col">Accepted Types</th>
|
||||
<th scope="col">Information</th>
|
||||
<th
|
||||
scope="col"
|
||||
i18n-id="timelion.expressionSuggestions.arg.nameTitle"
|
||||
i18n-default-message="Argument Name"
|
||||
></th>
|
||||
<th
|
||||
scope="col"
|
||||
i18n-id="timelion.expressionSuggestions.arg.typesTitle"
|
||||
i18n-default-message="Accepted Types"
|
||||
></th>
|
||||
<th
|
||||
scope="col"
|
||||
i18n-id="timelion.expressionSuggestions.arg.infoTitle"
|
||||
i18n-default-message="Information"
|
||||
></th>
|
||||
</thead>
|
||||
<tr ng-repeat="arg in suggestion.args" ng-hide="$index < 1 && suggestion.chainable">
|
||||
<td>{{arg.name}}</td>
|
||||
|
|
|
@ -1,21 +1,36 @@
|
|||
<div class="euiText timHelp">
|
||||
<div ng-show="page === 1">
|
||||
<div>
|
||||
<h1>Welcome to <strong>Timelion</strong>!</h1>
|
||||
<h1
|
||||
i18n-id="timelion.help.welcomeTitle"
|
||||
i18n-default-message="Welcome to {strongTimelionLabel}!"
|
||||
i18n-values="{ strongTimelionLabel: '<strong>Timelion</strong>' }"
|
||||
></h1>
|
||||
<p
|
||||
i18n-id="timelion.help.welcome.content.paragraph1"
|
||||
i18n-default-message="Timelion is the clawing, gnashing, zebra killing, pluggable time
|
||||
series interface for {emphasizedEverything}. If your datastore can
|
||||
produce a time series, then you have all of the awesome power of
|
||||
Timelion at your disposal. Timelion lets you compare, combine, and
|
||||
combobulate datasets across multiple datasources with one
|
||||
easy-to-master expression syntax. This tutorial focuses on
|
||||
Elasticsearch, but you'll quickly discover that what you learn here
|
||||
applies to any datasource Timelion supports."
|
||||
i18n-values="{ emphasizedEverything: '<em>' + translations.emphasizedEverythingText + '</em>' }"
|
||||
></p>
|
||||
<p>
|
||||
Timelion is the clawing, gnashing, zebra killing, pluggable time
|
||||
series interface for <em>everything</em>. If your datastore can
|
||||
produce a time series, then you have all of the awesome power of
|
||||
Timelion at your disposal. Timelion lets you compare, combine, and
|
||||
combobulate datasets across multiple datasources with one
|
||||
easy-to-master expression syntax. This tutorial focuses on
|
||||
Elasticsearch, but you'll quickly discover that what you learn here
|
||||
applies to any datasource Timelion supports.
|
||||
</p>
|
||||
<p>
|
||||
Ready to get started? Click <strong>Next</strong>. Want to skip the
|
||||
tutorial and view the docs? <a ng-click="setPage(0)">
|
||||
Jump to the function reference</a>.
|
||||
<span
|
||||
i18n-id="timelion.help.welcome.content.paragraph2"
|
||||
i18n-default-message="Ready to get started? Click {strongNext}. Want to skip the tutorial and view the docs?"
|
||||
i18n-values="{
|
||||
strongNext: '<strong>' + translations.strongNextText + '</strong>',
|
||||
}"
|
||||
></span>
|
||||
<a
|
||||
ng-click="setPage(0)"
|
||||
i18n-id="timelion.help.welcome.content.functionReferenceLinkText"
|
||||
i18n-default-message="Jump to the function reference"
|
||||
></a>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="timHelp__buttons">
|
||||
|
@ -24,7 +39,7 @@
|
|||
ng-click="opts.dontShowHelp()"
|
||||
class="kuiButton kuiButton--hollow"
|
||||
>
|
||||
Don't show this again
|
||||
{{translations.dontShowHelpButtonLabel}}
|
||||
</button>
|
||||
|
||||
|
||||
|
@ -32,7 +47,7 @@
|
|||
ng-click="setPage(page+1)"
|
||||
class="kuiButton kuiButton--primary"
|
||||
>
|
||||
Next
|
||||
{{translations.nextButtonLabel}}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
@ -40,20 +55,28 @@
|
|||
<div ng-show="page === 2">
|
||||
<div ng-show="!es.valid">
|
||||
<div>
|
||||
<h2>First time configuration</h2>
|
||||
<p>
|
||||
If you're using Logstash, you don't need to configure anything to
|
||||
start exploring your log data with Timelion. To search other
|
||||
indices, go to <strong>Management / Kibana / Advanced Settings
|
||||
</strong> and configure the <code>timelion:es.default_index</code>
|
||||
and <code>timelion:es.timefield</code> settings to match your
|
||||
indices.
|
||||
</p>
|
||||
<p>
|
||||
You'll also see some other Timelion settings. For now, you don't need
|
||||
to worry about them. Later, you'll see that you can set most of
|
||||
them on the fly if you need to.
|
||||
</p>
|
||||
<h2
|
||||
i18n-id="timelion.help.configuration.notValidTitle"
|
||||
i18n-default-message="First time configuration"
|
||||
></h2>
|
||||
<p
|
||||
i18n-id="timelion.help.configuration.notValid.paragraph1"
|
||||
i18n-default-message="If you're using Logstash, you don't need to configure anything to
|
||||
start exploring your log data with Timelion. To search other
|
||||
indices, go to {advancedSettingsPath} and configure the {esDefaultIndex}
|
||||
and {esTimefield} settings to match your indices."
|
||||
i18n-values="{
|
||||
advancedSettingsPath: '<strong>' + translations.notValidAdvancedSettingsPath + '</strong>',
|
||||
esDefaultIndex: '<code>timelion:es.default_index</code>',
|
||||
esTimefield: '<code>timelion:es.timefield</code>',
|
||||
}"
|
||||
></p>
|
||||
<p
|
||||
i18n-id="timelion.help.configuration.notValid.paragraph2"
|
||||
i18n-default-message="You'll also see some other Timelion settings. For now, you don't need
|
||||
to worry about them. Later, you'll see that you can set most of
|
||||
them on the fly if you need to."
|
||||
></p>
|
||||
</div>
|
||||
<div class="timHelp__buttons">
|
||||
|
||||
|
@ -61,74 +84,139 @@
|
|||
ng-click="setPage(page-1)"
|
||||
class="kuiButton kuiButton--primary"
|
||||
>
|
||||
Previous
|
||||
{{translations.previousButtonLabel}}
|
||||
</button>
|
||||
|
||||
<span ng-show="es.invalidCount > 0 && !es.valid">
|
||||
Could not validate Elasticsearch settings:
|
||||
<strong>{{es.invalidReason}}</strong>. Check your Advanced Settings
|
||||
and try again. ({{es.invalidCount}})
|
||||
</span>
|
||||
<span
|
||||
ng-show="es.invalidCount > 0 && !es.valid"
|
||||
i18n-id="timelion.help.configuration.notValid.notValidSettingsErrorMessage"
|
||||
i18n-default-message="Could not validate Elasticsearch settings: {reason}.
|
||||
Check your Advanced Settings and try again. ({count})"
|
||||
i18n-values="{
|
||||
reason: '<strong>' + es.invalidReason + '</strong>',
|
||||
count: es.invalidCount,
|
||||
}"
|
||||
></span>
|
||||
|
||||
<button
|
||||
ng-click="recheckElasticsearch()"
|
||||
class="kuiButton kuiButton--primary"
|
||||
>
|
||||
Validate Config
|
||||
</button>
|
||||
i18n-id="timelion.help.configuration.notValid.validateButtonLabel"
|
||||
i18n-default-message="Validate Config"
|
||||
></button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="es.valid">
|
||||
<div>
|
||||
<h2>Good news, Elasticsearch is configured correctly!</h2>
|
||||
<h2
|
||||
i18n-id="timelion.help.configuration.validTitle"
|
||||
i18n-default-message="Good news, Elasticsearch is configured correctly!"
|
||||
></h2>
|
||||
<p>
|
||||
We validated your default index and your timefield and everything
|
||||
looks ok. We found data from <strong>{{es.stats.min}}</strong> to
|
||||
<strong>{{es.stats.max}}</strong>. You're probably all set. If this
|
||||
doesn't look right, see <a ng-click="es.valid = false">First time
|
||||
configuration</a> for information about configuring the Elasticsearch
|
||||
datasource.
|
||||
</p>
|
||||
<p>
|
||||
You should already see one chart, but you might need to make a
|
||||
couple adjustments before you see any interesting data:
|
||||
<span
|
||||
i18n-id="timelion.help.configuration.valid.paragraph1Part1"
|
||||
i18n-default-message="We validated your default index and your timefield and everything
|
||||
looks ok. We found data from {statsMin} to {statsMax}.
|
||||
You're probably all set. If this doesn't look right, see"
|
||||
i18n-values="{
|
||||
statsMin: '<strong>' + es.stats.min + '</strong>',
|
||||
statsMax: '<strong>' + es.stats.max + '</strong>',
|
||||
}"
|
||||
i18n-context="Part of composite text timelion.help.configuration.valid.paragraph1Part1 +
|
||||
timelion.help.configuration.firstTimeConfigurationLinkText +
|
||||
timelion.help.configuration.valid.paragraph1Part2"
|
||||
></span>
|
||||
<a
|
||||
ng-click="es.valid = false"
|
||||
i18n-id="timelion.help.configuration.firstTimeConfigurationLinkText"
|
||||
i18n-default-message="First time configuration"
|
||||
i18n-context="Part of composite text timelion.help.configuration.valid.paragraph1Part1 +
|
||||
timelion.help.configuration.firstTimeConfigurationLinkText +
|
||||
timelion.help.configuration.valid.paragraph1Part2"
|
||||
></a>
|
||||
<span
|
||||
i18n-id="timelion.help.configuration.valid.paragraph1Part2"
|
||||
i18n-default-message="for information about configuring the Elasticsearch datasource."
|
||||
i18n-context="Part of composite text timelion.help.configuration.valid.paragraph1Part1 +
|
||||
timelion.help.configuration.firstTimeConfigurationLinkText +
|
||||
timelion.help.configuration.valid.paragraph1Part2"
|
||||
></span>
|
||||
</p>
|
||||
<p
|
||||
i18n-id="timelion.help.configuration.valid.paragraph2"
|
||||
i18n-default-message="You should already see one chart, but you might need to make a
|
||||
couple adjustments before you see any interesting data:"
|
||||
></p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Intervals</strong>
|
||||
<strong
|
||||
i18n-id="timelion.help.configuration.valid.intervalsTitle"
|
||||
i18n-default-message="Intervals"
|
||||
></strong>
|
||||
<p>
|
||||
The interval selector at the right of the input bar lets you
|
||||
control the sampling frequency. It's currently set to
|
||||
<code>{{state.interval}}</code>.
|
||||
<span
|
||||
i18n-id="timelion.help.configuration.valid.intervalsTextPart1"
|
||||
i18n-default-message="The interval selector at the right of the input bar lets you
|
||||
control the sampling frequency. It's currently set to {interval}."
|
||||
i18n-values="{ interval: '<code>' + state.interval + '</code>' }"
|
||||
i18n-context="Part of composite text
|
||||
timelion.help.configuration.valid.intervalsTextPart1 +
|
||||
(timelion.help.configuration.valid.intervalIsAutoText ||
|
||||
timelion.help.configuration.valid.intervals.content.intervalIsNotAutoText) +
|
||||
timelion.help.configuration.valid.intervalsTextPart2"
|
||||
></span>
|
||||
<span ng-show="state.interval == 'auto'">
|
||||
<strong>You're all set!</strong>
|
||||
<strong
|
||||
i18n-id="timelion.help.configuration.valid.intervalIsAutoText"
|
||||
i18n-default-message="You're all set!"
|
||||
i18n-context="Part of composite text
|
||||
timelion.help.configuration.valid.intervalsTextPart1 +
|
||||
(timelion.help.configuration.valid.intervalIsAutoText ||
|
||||
timelion.help.configuration.valid.intervals.content.intervalIsNotAutoText) +
|
||||
timelion.help.configuration.valid.intervalsTextPart2"
|
||||
></strong>
|
||||
</span>
|
||||
<span ng-show="state.interval != 'auto'">
|
||||
Set it to <code>auto </code> to let Timelion choose an
|
||||
appropriate interval.
|
||||
</span>
|
||||
If Timelion thinks your combination of time range and interval
|
||||
will produce too many data points, it throws an error. You can
|
||||
adjust that limit by configuring <code>timelion:max_buckets</code>
|
||||
in <strong>Management/Kibana/Advanced Settings</strong>.
|
||||
<span
|
||||
ng-show="state.interval != 'auto'"
|
||||
i18n-id="timelion.help.configuration.valid.intervals.content.intervalIsNotAutoText"
|
||||
i18n-default-message="Set it to {auto} to let Timelion choose an appropriate interval."
|
||||
i18n-context="Part of composite text
|
||||
timelion.help.configuration.valid.intervalsTextPart1 +
|
||||
(timelion.help.configuration.valid.intervalIsAutoText ||
|
||||
timelion.help.configuration.valid.intervals.content.intervalIsNotAutoText) +
|
||||
timelion.help.configuration.valid.intervalsTextPart2"
|
||||
i18n-values="{ auto: '<code>auto </code>' }"
|
||||
></span>
|
||||
<span
|
||||
i18n-id="timelion.help.configuration.valid.intervalsTextPart2"
|
||||
i18n-default-message="If Timelion thinks your combination of time range and interval
|
||||
will produce too many data points, it throws an error.
|
||||
You can adjust that limit by configuring {maxBuckets} in {advancedSettingsPath}."
|
||||
i18n-values="{
|
||||
maxBuckets: '<code>timelion:max_buckets</code>',
|
||||
advancedSettingsPath: '<strong>' + translations.validAdvancedSettingsPath + '</strong>',
|
||||
}"
|
||||
></span>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Time range</strong>
|
||||
<p>
|
||||
Use the timepicker in the
|
||||
Kibana toolbar to select the time period that contains the
|
||||
data you want to visualize. Make sure you select a time
|
||||
period that includes all or part of the time range shown
|
||||
above.
|
||||
</p>
|
||||
<strong
|
||||
i18n-id="timelion.help.configuration.valid.timeRangeTitle"
|
||||
i18n-default-message="Time range"
|
||||
></strong>
|
||||
<p
|
||||
i18n-id="timelion.help.configuration.valid.timeRangeText"
|
||||
i18n-default-message="Use the timepicker in the Kibana toolbar to select the time period
|
||||
that contains the data you want to visualize. Make sure you select
|
||||
a time period that includes all or part of the time range shown above."
|
||||
></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
Now, you should see a line chart that displays a count of your
|
||||
data points over time.
|
||||
</p>
|
||||
<p
|
||||
i18n-id="timelion.help.configuration.valid.paragraph3"
|
||||
i18n-default-message="Now, you should see a line chart that displays a count of your data points over time."
|
||||
></p>
|
||||
</div>
|
||||
<div class="timHelp__buttons">
|
||||
|
||||
|
@ -136,7 +224,7 @@
|
|||
ng-click="setPage(page-1)"
|
||||
class="kuiButton kuiButton--primary"
|
||||
>
|
||||
Previous
|
||||
{{translations.previousButtonLabel}}
|
||||
</button>
|
||||
|
||||
|
||||
|
@ -144,7 +232,7 @@
|
|||
ng-click="setPage(page+1)"
|
||||
class="kuiButton kuiButton--primary"
|
||||
>
|
||||
Next
|
||||
{{translations.nextButtonLabel}}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
@ -152,60 +240,131 @@
|
|||
</div>
|
||||
<div ng-show="page === 3">
|
||||
<div>
|
||||
<h2>Querying the Elasticsearch datasource</h2>
|
||||
<h2
|
||||
i18n-id="timelion.help.queryingTitle"
|
||||
i18n-default-message="Querying the Elasticsearch datasource"
|
||||
></h2>
|
||||
<p
|
||||
i18n-id="timelion.help.querying.paragraph1"
|
||||
i18n-default-message="Now that we've validated that you have a working Elasticsearch
|
||||
datasource, you can start submitting queries. For starters,
|
||||
enter {esPattern} in the input bar and hit enter."
|
||||
i18n-values="{
|
||||
esPattern: '<code>.es(*)</code>',
|
||||
}"
|
||||
></p>
|
||||
<p>
|
||||
Now that we've validated that you have a working Elasticsearch
|
||||
datasource, you can start submitting queries. For starters,
|
||||
enter <code>.es(*)</code> in the input bar and hit enter.
|
||||
</p>
|
||||
<p>
|
||||
This says <em>hey Elasticsearch, find everything in my default
|
||||
index</em>. If you want to find a subset, you could enter something
|
||||
like <code>.es(html)</code> to count events that match <em>html</em>,
|
||||
or <code>.es('user:bob AND bytes:>100')</code> to find events
|
||||
that contain <em>bob</em> in the <code>user</code> field and have a
|
||||
<code>bytes</code> field that is greater than 100. Note that this query
|
||||
is enclosed in single quotes—that's because it contains
|
||||
spaces. You can enter any
|
||||
<span
|
||||
i18n-id="timelion.help.querying.paragraph2Part1"
|
||||
i18n-default-message="This says {esAsteriskQueryDescription}. If you want to find a subset, you could enter something
|
||||
like {htmlQuery} to count events that match {html}, or {bobQuery}
|
||||
to find events that contain {bob} in the {user} field and have a {bytes}
|
||||
field that is greater than 100. Note that this query is enclosed in single
|
||||
quotes—that's because it contains spaces. You can enter any"
|
||||
i18n-values="{
|
||||
esAsteriskQueryDescription: '<em>' + translations.esAsteriskQueryDescription + '</em>',
|
||||
html: '<em>html</em>',
|
||||
htmlQuery: '<code>.es(html)</code>',
|
||||
bobQuery: '<code>.es(\'user:bob AND bytes:>100\')</code>',
|
||||
bob: '<em>bob</em>',
|
||||
user: '<code>user</code>',
|
||||
bytes: '<code>bytes</code>',
|
||||
}"
|
||||
i18n-context="Part of composite text
|
||||
timelion.help.querying.paragraph2Part1 +
|
||||
timelion.help.querying.luceneQueryLinkText +
|
||||
timelion.help.querying.paragraph2Part2"
|
||||
></span>
|
||||
<a
|
||||
href="https://www.elastic.co/guide/en/elasticsearch/reference/5.1/query-dsl-query-string-query.html#query-string-syntax"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Lucene query string
|
||||
</a>
|
||||
as the first argument to the <code>.es()</code> function.
|
||||
href="https://www.elastic.co/guide/en/elasticsearch/reference/5.1/query-dsl-query-string-query.html#query-string-syntax"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
i18n-id="timelion.help.querying.luceneQueryLinkText"
|
||||
i18n-default-message="Lucene query string"
|
||||
i18n-context="Part of composite text
|
||||
timelion.help.querying.paragraph2Part1 +
|
||||
timelion.help.querying.luceneQueryLinkText +
|
||||
timelion.help.querying.paragraph2Part2"
|
||||
></a>
|
||||
<span
|
||||
i18n-id="timelion.help.querying.paragraph2Part2"
|
||||
i18n-default-message="as the first argument to the {esQuery} function."
|
||||
i18n-values="{
|
||||
esQuery: '<code>.es()</code>',
|
||||
}"
|
||||
i18n-context="Part of composite text
|
||||
timelion.help.querying.paragraph2Part1 +
|
||||
timelion.help.querying.luceneQueryLinkText +
|
||||
timelion.help.querying.paragraph2Part2"
|
||||
></span>
|
||||
</p>
|
||||
<h4>Passing arguments</h4>
|
||||
<h4
|
||||
i18n-id="timelion.help.querying.passingArgumentsTitle"
|
||||
i18n-default-message="Passing arguments"
|
||||
></h4>
|
||||
<p
|
||||
i18n-id="timelion.help.querying.passingArgumentsText"
|
||||
i18n-default-message="Timelion has a number of shortcuts that make it easy to do common things.
|
||||
One is that for simple arguments that don't contain spaces or special
|
||||
characters, you don't need to use quotes. Many functions also have defaults.
|
||||
For example, {esEmptyQuery} and {esStarQuery} do the same thing.
|
||||
Arguments also have names, so you don't have to specify them in a specific order.
|
||||
For example, you can enter {esLogstashQuery} to tell the Elasticsearch datasource
|
||||
{esIndexQueryDescription}."
|
||||
i18n-values="{
|
||||
esEmptyQuery: '<code>.es()</code>',
|
||||
esStarQuery: '<code>.es(*)</code>',
|
||||
esLogstashQuery: '<code>.es(index=\'logstash-*\', q=\'*\')</code>',
|
||||
esIndexQueryDescription: '<em>' + translations.esIndexQueryDescription + '</em>',
|
||||
}"
|
||||
></p>
|
||||
<h4
|
||||
i18n-id="timelion.help.querying.countTitle"
|
||||
i18n-default-message="Beyond count"
|
||||
></h4>
|
||||
<p>
|
||||
Timelion has a number of shortcuts that make it easy to do common
|
||||
things. One is that for simple arguments that don't contain spaces or
|
||||
special characters, you don't need to use quotes. Many functions also
|
||||
have defaults. For example, <code>.es()</code> and <code>.es(*)</code>
|
||||
do the same thing. Arguments also have names, so you don't have to
|
||||
specify them in a specific order. For example, you can enter
|
||||
<code>.es(index='logstash-*', q='*')</code> to tell the
|
||||
Elasticsearch datasource <em>use * as the q (query) for the
|
||||
logstash-* index</em>.
|
||||
</p>
|
||||
<h4>Beyond count</h4>
|
||||
<p>
|
||||
Counting events is all well and good, but the Elasticsearch datasource
|
||||
also supports any
|
||||
<span
|
||||
i18n-id="timelion.help.querying.countTextPart1"
|
||||
i18n-default-message="Counting events is all well and good, but the Elasticsearch datasource also supports any"
|
||||
i18n-context="Part of composite text
|
||||
timelion.help.querying.countTextPart1 +
|
||||
timelion.help.querying.countMetricAggregationLinkText +
|
||||
timelion.help.querying.countTextPart2"
|
||||
></span>
|
||||
<a
|
||||
href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics.html"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Elasticsearch metric aggregation
|
||||
</a>
|
||||
that returns a single value. Some of the most useful are
|
||||
<code>min</code>, <code>max</code>, <code>avg</code>, <code>sum</code>,
|
||||
and <code>cardinality</code>. Let's say you want a unique count of the
|
||||
<code>src_ip</code> field. Simply use the <code>cardinality</code>
|
||||
metric: <code>.es(*, metric='cardinality:src_ip')</code>. To get the
|
||||
average of the <code>bytes</code> field, you can use the
|
||||
<code>avg</code> metric: <code>.es(metric='avg:bytes')</code>.
|
||||
href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics.html"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
i18n-id="timelion.help.querying.countMetricAggregationLinkText"
|
||||
i18n-default-message="Elasticsearch metric aggregation"
|
||||
i18n-context="Part of composite text
|
||||
timelion.help.querying.countTextPart1 +
|
||||
timelion.help.querying.countMetricAggregationLinkText +
|
||||
timelion.help.querying.countTextPart2"
|
||||
></a>
|
||||
<span
|
||||
i18n-id="timelion.help.querying.countTextPart2"
|
||||
i18n-default-message="that returns a single value. Some of the most useful are
|
||||
{min}, {max}, {avg}, {sum}, and {cardinality}.
|
||||
Let's say you want a unique count of the {srcIp} field.
|
||||
Simply use the {cardinality} metric: {esCardinalityQuery}. To get the
|
||||
average of the {bytes} field, you can use the {avg} metric: {esAvgQuery}."
|
||||
i18n-values="{
|
||||
min: '<code>min</code>',
|
||||
max: '<code>max</code>',
|
||||
avg: '<code>avg</code>',
|
||||
sum: '<code>sum</code>',
|
||||
cardinality: '<code>cardinality</code>',
|
||||
bytes: '<code>bytes</code>',
|
||||
srcIp: '<code>src_ip</code>',
|
||||
esCardinalityQuery: '<code>.es(*, metric=\'cardinality:src_ip\')</code>',
|
||||
esAvgQuery: '<code>.es(metric=\'avg:bytes\')</code>',
|
||||
}"
|
||||
i18n-context="Part of composite text
|
||||
timelion.help.querying.countTextPart1 +
|
||||
timelion.help.querying.countMetricAggregationLinkText +
|
||||
timelion.help.querying.countTextPart2"
|
||||
></span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="timHelp__buttons">
|
||||
|
@ -214,7 +373,7 @@
|
|||
ng-click="setPage(page-1)"
|
||||
class="kuiButton kuiButton--primary"
|
||||
>
|
||||
Previous
|
||||
{{translations.previousButtonLabel}}
|
||||
</button>
|
||||
|
||||
|
||||
|
@ -222,7 +381,7 @@
|
|||
ng-click="setPage(page+1)"
|
||||
class="kuiButton kuiButton--primary"
|
||||
>
|
||||
Next
|
||||
{{translations.nextButtonLabel}}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
@ -230,60 +389,95 @@
|
|||
|
||||
<div ng-show="page === 4">
|
||||
<div>
|
||||
<h2>Expressing yourself with expressions</h2>
|
||||
<p>
|
||||
Every expression starts with a datasource function. From there, you
|
||||
can append new functions to the datasource to transform and augment
|
||||
it.
|
||||
</p>
|
||||
<p>
|
||||
By the way, from here on out you probably know more about your data
|
||||
than we do. Feel free to replace the sample queries with something
|
||||
more meaningful!
|
||||
</p>
|
||||
<p>
|
||||
We're going to experiment, so click <strong>Add</strong> in the Kibana
|
||||
toolbar to add another chart or three. Then, select a chart, copy
|
||||
one of the following expressions, paste it into the input bar,
|
||||
and hit enter. Rinse, repeat to try out the other expressions.
|
||||
</p>
|
||||
<h2
|
||||
i18n-id="timelion.help.expressionsTitle"
|
||||
i18n-default-message="Expressing yourself with expressions"
|
||||
></h2>
|
||||
<p
|
||||
i18n-id="timelion.help.expressions.paragraph1"
|
||||
i18n-default-message="Every expression starts with a datasource function. From there, you
|
||||
can append new functions to the datasource to transform and augment it."
|
||||
></p>
|
||||
<p
|
||||
i18n-id="timelion.help.expressions.paragraph2"
|
||||
i18n-default-message="By the way, from here on out you probably know more about your data
|
||||
than we do. Feel free to replace the sample queries with something
|
||||
more meaningful!"
|
||||
></p>
|
||||
<p
|
||||
i18n-id="timelion.help.expressions.paragraph3"
|
||||
i18n-default-message="We're going to experiment, so click {strongAdd} in the Kibana toolbar
|
||||
to add another chart or three. Then, select a chart,
|
||||
copy one of the following expressions, paste it into the input bar,
|
||||
and hit enter. Rinse, repeat to try out the other expressions."
|
||||
i18n-values="{ strongAdd: '<strong>' + translations.strongAddText + '</strong>' }"
|
||||
></p>
|
||||
<table class="table table-condensed table-striped">
|
||||
<tr>
|
||||
<td><code>.es(*), .es(US)</code></td>
|
||||
<td><strong>Double the fun.</strong> Two expressions on the same
|
||||
chart.</td>
|
||||
<td
|
||||
i18n-id="timelion.help.expressions.examples.twoExpressionsDescription"
|
||||
i18n-default-message="{descriptionTitle} Two expressions on the same chart."
|
||||
i18n-values="{
|
||||
descriptionTitle: '<strong>' + translations.twoExpressionsDescriptionTitle + '</strong>',
|
||||
}"
|
||||
></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>.es(*).color(#f66), .es(US).bars(1)</code></td>
|
||||
<td>
|
||||
<strong>Custom styling.</strong> Colorizes the first series red
|
||||
and uses 1 pixel wide bars for the second series.
|
||||
</td>
|
||||
<td
|
||||
i18n-id="timelion.help.expressions.examples.customStylingDescription"
|
||||
i18n-default-message="{descriptionTitle} Colorizes the first series red and
|
||||
uses 1 pixel wide bars for the second series."
|
||||
i18n-values="{
|
||||
descriptionTitle: '<strong>' + translations.customStylingDescriptionTitle + '</strong>',
|
||||
}"
|
||||
></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>.es(*).color(#f66).lines(fill=3),
|
||||
.es(US).bars(1).points(radius=3, weight=1)</code>
|
||||
</td>
|
||||
<td>
|
||||
<strong>Named arguments.</strong> Forget trying to remember what
|
||||
order you need to specify arguments in, use named arguments to make
|
||||
the expressions easier to read and write.
|
||||
</td>
|
||||
<td
|
||||
i18n-id="timelion.help.expressions.examples.namedArgumentsDescription"
|
||||
i18n-default-message="{descriptionTitle} Forget trying to remember what order you need
|
||||
to specify arguments in, use named arguments to make
|
||||
the expressions easier to read and write."
|
||||
i18n-values="{
|
||||
descriptionTitle: '<strong>' + translations.namedArgumentsDescriptionTitle + '</strong>',
|
||||
}"
|
||||
></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>(.es(*), .es(GB)).points()</code></td>
|
||||
<td>
|
||||
<strong>Grouped expressions.</strong> You can also chain groups
|
||||
of expressions to functions. Here, both series are shown as
|
||||
points instead of lines.
|
||||
</td>
|
||||
<td
|
||||
i18n-id="timelion.help.expressions.examples.groupedExpressionsDescription"
|
||||
i18n-default-message="{descriptionTitle} You can also chain groups of expressions to
|
||||
functions. Here, both series are shown as points instead of lines."
|
||||
i18n-values="{
|
||||
descriptionTitle: '<strong>' + translations.groupedExpressionsDescriptionTitle + '</strong>',
|
||||
}"
|
||||
></td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
Timelion provides additional view transformation functions you can use
|
||||
to customize the appearance of your charts. For the complete list, see
|
||||
the <a ng-click="setPage(0)">Function reference</a>.
|
||||
<span
|
||||
i18n-id="timelion.help.expressions.paragraph4"
|
||||
i18n-default-message="Timelion provides additional view transformation functions you can use
|
||||
to customize the appearance of your charts. For the complete list, see the"
|
||||
i18n-context="Part of composite text
|
||||
timelion.help.expressions.paragraph4 +
|
||||
timelion.help.expressions.functionReferenceLinkText"
|
||||
></span>
|
||||
<a
|
||||
ng-click="setPage(0)"
|
||||
i18n-id="timelion.help.expressions.functionReferenceLinkText"
|
||||
i18n-default-message="Function reference"
|
||||
i18n-context="Part of composite text
|
||||
timelion.help.expressions.paragraph4 +
|
||||
timelion.help.expressions.functionReferenceLinkText"
|
||||
></a>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="timHelp__buttons">
|
||||
|
@ -292,7 +486,7 @@
|
|||
ng-click="setPage(page-1)"
|
||||
class="kuiButton kuiButton--primary"
|
||||
>
|
||||
Previous
|
||||
{{translations.previousButtonLabel}}
|
||||
</button>
|
||||
|
||||
|
||||
|
@ -300,50 +494,93 @@
|
|||
ng-click="setPage(page+1)"
|
||||
class="kuiButton kuiButton--primary"
|
||||
>
|
||||
Next
|
||||
{{translations.nextButtonLabel}}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="page === 5">
|
||||
<div>
|
||||
<h2>Transforming your data: the real fun begins!</h2>
|
||||
<h2
|
||||
i18n-id="timelion.help.dataTransformingTitle"
|
||||
i18n-default-message="Transforming your data: the real fun begins!"
|
||||
></h2>
|
||||
<p
|
||||
i18n-id="timelion.help.dataTransforming.paragraph1"
|
||||
i18n-default-message="Now that you've mastered the basics, it's time to unleash the power of
|
||||
Timelion. Let's figure out what percentage some subset of our data
|
||||
represents of the whole, over time. For example, what percentage of
|
||||
our web traffic comes from the US?"
|
||||
></p>
|
||||
<p
|
||||
i18n-id="timelion.help.dataTransforming.paragraph2"
|
||||
i18n-default-message="First, we need to find all events that contain US: {esUsQuery}."
|
||||
i18n-values="{ esUsQuery: '<code>.es(\'US\')</code>' }"
|
||||
></p>
|
||||
<p
|
||||
i18n-id="timelion.help.dataTransforming.paragraph3"
|
||||
i18n-default-message="Next, we want to calculate the ratio of US events to the whole.
|
||||
To divide {us} by everything, we can use the {divide} function:
|
||||
{divideDataQuery}."
|
||||
i18n-values="{
|
||||
us: '<code>\'US\'</code>',
|
||||
divide: '<code>divide</code>',
|
||||
divideDataQuery: '<code>.es(\'US\').divide(.es())</code>',
|
||||
}"
|
||||
></p>
|
||||
<p
|
||||
i18n-id="timelion.help.dataTransforming.paragraph4"
|
||||
i18n-default-message="Not bad, but this gives us a number between 0 and 1. To convert it
|
||||
to a percentage, simply multiply by 100: {multiplyDataQuery}."
|
||||
i18n-values="{ multiplyDataQuery: '<code>.es(\'US\').divide(.es()).multiply(100)</code>' }"
|
||||
></p>
|
||||
<p
|
||||
i18n-id="timelion.help.dataTransforming.paragraph5"
|
||||
i18n-default-message="Now we know what percentage of our traffic comes from the US, and
|
||||
can see how it has changed over time! Timelion has a number of
|
||||
built-in arithmetic functions, such as {sum}, {subtract}, {multiply},
|
||||
and {divide}. Many of these can take a series or a number. There are
|
||||
also other useful data transformation functions, such as
|
||||
{movingaverage}, {abs}, and {derivative}."
|
||||
i18n-values="{
|
||||
sum: '<code>sum</code>',
|
||||
subtract: '<code>subtract</code>',
|
||||
multiply: '<code>multiply</code>',
|
||||
divide: '<code>divide</code>',
|
||||
movingaverage: '<code>movingaverage</code>',
|
||||
abs: '<code>abs</code>',
|
||||
derivative: '<code>derivative</code>',
|
||||
}"
|
||||
></p>
|
||||
<p>
|
||||
Now that you've mastered the basics, it's time to unleash the power of
|
||||
Timelion. Let's figure out what percentage some subset of our data
|
||||
represents of the whole, over time. For example, what percentage of
|
||||
our web traffic comes from the US?
|
||||
</p>
|
||||
<p>
|
||||
First, we need to find all events that contain US:
|
||||
<code>.es('US')</code>.
|
||||
</p>
|
||||
<p>
|
||||
Next, we want to calculate the ratio of US events to the whole. To
|
||||
divide <code>'US'</code> by everything, we can use the
|
||||
<code>divide</code> function: <code>.es('US').divide(.es())</code>.
|
||||
</p>
|
||||
<p>
|
||||
Not bad, but this gives us a number between 0 and 1. To convert it
|
||||
to a percentage, simply multiply by 100:
|
||||
<code>.es('US').divide(.es()).multiply(100)</code>.
|
||||
</p>
|
||||
<p>
|
||||
Now we know what percentage of our traffic comes from the US, and
|
||||
can see how it has changed over time!
|
||||
Timelion has a number of built-in arithmetic functions, such as
|
||||
<code>sum</code>, <code>subtract</code>, <code>multiply</code>, and
|
||||
<code>divide</code>. Many of these can take a series or a number.
|
||||
There are also other useful data transformation functions, such as
|
||||
<code>movingaverage</code>, <code>abs</code>, and
|
||||
<code>derivative</code>.
|
||||
</p>
|
||||
<p>Now that you're familiar with the syntax, refer to the
|
||||
<a ng-click="setPage(0)">Function reference</a> to see
|
||||
how to use all of the available Timelion functions. You can view
|
||||
the reference at any time by clicking <strong>Docs</strong>
|
||||
in the Kibana toolbar. To get back to this tutorial, click the
|
||||
<strong>Tutorial</strong> link at the top of the reference.
|
||||
<span
|
||||
i18n-id="timelion.help.dataTransforming.paragraph6Part1"
|
||||
i18n-default-message="Now that you're familiar with the syntax, refer to the"
|
||||
i18n-context="Part of composite text
|
||||
timelion.help.dataTransforming.paragraph6Part1 +
|
||||
timelion.help.dataTransforming.functionReferenceLinkText +
|
||||
timelion.help.dataTransforming.paragraph6Part2"
|
||||
></span>
|
||||
<a
|
||||
ng-click="setPage(0)"
|
||||
i18n-id="timelion.help.dataTransforming.functionReferenceLinkText"
|
||||
i18n-default-message="Function reference"
|
||||
i18n-context="Part of composite text
|
||||
timelion.help.dataTransforming.paragraph6Part1 +
|
||||
timelion.help.dataTransforming.functionReferenceLinkText +
|
||||
timelion.help.dataTransforming.paragraph6Part2"
|
||||
></a>
|
||||
<span
|
||||
i18n-id="timelion.help.dataTransforming.paragraph6Part2"
|
||||
i18n-default-message="to see how to use all of the available Timelion functions.
|
||||
You can view the reference at any time by clicking \{Docs\}
|
||||
in the Kibana toolbar. To get back to this tutorial, click the
|
||||
\{Tutorial\} link at the top of the reference."
|
||||
i18n-context="Part of composite text
|
||||
timelion.help.dataTransforming.paragraph6Part1 +
|
||||
timelion.help.dataTransforming.functionReferenceLinkText +
|
||||
timelion.help.dataTransforming.paragraph6Part2"
|
||||
></span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="timHelp__buttons">
|
||||
|
@ -352,7 +589,7 @@
|
|||
ng-click="setPage(page-1)"
|
||||
class="kuiButton kuiButton--primary"
|
||||
>
|
||||
Previous
|
||||
{{translations.previousButtonLabel}}
|
||||
</button>
|
||||
|
||||
|
||||
|
@ -361,29 +598,34 @@
|
|||
ng-click="opts.dontShowHelp()"
|
||||
class="kuiButton kuiButton--hollow"
|
||||
>
|
||||
Don't show this again
|
||||
{{translations.dontShowHelpButtonLabel}}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="page === 0">
|
||||
<h2 class="kuiLocalDropdownTitle">
|
||||
Help
|
||||
</h2>
|
||||
<h2
|
||||
class="kuiLocalDropdownTitle"
|
||||
i18n-id="timelion.help.mainPageTitle"
|
||||
i18n-default-message="Help"
|
||||
></h2>
|
||||
|
||||
<tabset>
|
||||
<tab heading="Function reference">
|
||||
<tab heading="{{ ::'timelion.help.mainPage.functionReferenceTitle' | i18n: { defaultMessage: 'Function reference' } }}">
|
||||
<div class="list-group-item list-group-item--noBorder">
|
||||
<div class="kuiLocalDropdownHelpText">
|
||||
Click any function for more information. Just getting started?
|
||||
<span
|
||||
i18n-id="timelion.help.mainPage.functionReference.gettingStartedText"
|
||||
i18n-default-message="Click any function for more information. Just getting started?"
|
||||
></span>
|
||||
<a
|
||||
i18n-id="timelion.help.mainPage.functionReference.welcomePageLinkText"
|
||||
i18n-default-message="Check out the tutorial"
|
||||
class="kuiLink"
|
||||
ng-click="setPage(1)"
|
||||
kbn-accessible-click
|
||||
>
|
||||
Check out the tutorial
|
||||
</a>.
|
||||
></a>.
|
||||
</div>
|
||||
|
||||
<div class="timHelp__functions">
|
||||
|
@ -409,9 +651,21 @@
|
|||
ng-show="function.args.length > (function.chainable ? 1: 0)"
|
||||
>
|
||||
<thead>
|
||||
<th scope="col">Argument Name</th>
|
||||
<th scope="col">Accepted Types</th>
|
||||
<th scope="col">Information</th>
|
||||
<th
|
||||
scope="col"
|
||||
i18n-id="timelion.help.mainPage.functionReference.detailsTable.argumentNameColumnLabel"
|
||||
i18n-default-message="Argument Name"
|
||||
></th>
|
||||
<th
|
||||
scope="col"
|
||||
i18n-id="timelion.help.mainPage.functionReference.detailsTable.acceptedTypesColumnLabel"
|
||||
i18n-default-message="Accepted Types"
|
||||
></th>
|
||||
<th
|
||||
scope="col"
|
||||
i18n-id="timelion.help.mainPage.functionReference.detailsTable.informationColumnLabel"
|
||||
i18n-default-message="Information"
|
||||
></th>
|
||||
</thead>
|
||||
<tr
|
||||
ng-repeat="arg in function.args"
|
||||
|
@ -423,10 +677,10 @@
|
|||
</tr>
|
||||
</table>
|
||||
<div ng-hide="function.args.length > (function.chainable ? 1: 0)">
|
||||
<em>
|
||||
This function does not accept any arguments.
|
||||
Well that's simple, isn't it?
|
||||
</em>
|
||||
<em
|
||||
i18n-id="timelion.help.mainPage.functionReference.noArgumentsFunctionErrorMessage"
|
||||
i18n-default-message="This function does not accept any arguments. Well that's simple, isn't it?"
|
||||
></em>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
@ -436,26 +690,50 @@
|
|||
</div>
|
||||
</tab>
|
||||
|
||||
<tab heading="Keyboard tips">
|
||||
<tab heading="{{ ::'timelion.help.mainPage.keyboardTipsTitle' | i18n: { defaultMessage: 'Keyboard tips' } }}">
|
||||
<div class="list-group-item list-group-item--noBorder">
|
||||
<!-- General editing tips -->
|
||||
<dl class="dl-horizontal">
|
||||
<dd><strong>General editing</strong></dd>
|
||||
<dd>
|
||||
<strong
|
||||
i18n-id="timelion.help.mainPage.keyboardTips.generalEditingTitle"
|
||||
i18n-default-message="General editing"
|
||||
></strong></dd>
|
||||
<dt></dt>
|
||||
<dt>Ctrl/Cmd + Enter</dt>
|
||||
<dd>Submit request</dd>
|
||||
<dd
|
||||
i18n-id="timelion.help.mainPage.keyboardTips.generalEditing.submitRequestText"
|
||||
i18n-default-message="Submit request"
|
||||
></dd>
|
||||
</dl>
|
||||
|
||||
<!-- Auto complete tips -->
|
||||
<dl class="dl-horizontal">
|
||||
<dt></dt>
|
||||
<dd><strong>When auto-complete is visible</strong></dd>
|
||||
<dt>Down arrow</dt>
|
||||
<dd>Switch focus to auto-complete menu. Use arrows to further select a term</dd>
|
||||
<dd>
|
||||
<strong
|
||||
i18n-id="timelion.help.mainPage.keyboardTips.autoCompleteTitle"
|
||||
i18n-default-message="When auto-complete is visible"
|
||||
></strong>
|
||||
</dd>
|
||||
<dt
|
||||
i18n-id="timelion.help.mainPage.keyboardTips.autoComplete.downArrowLabel"
|
||||
i18n-default-message="Down arrow"
|
||||
></dt>
|
||||
<dd
|
||||
i18n-id="timelion.help.mainPage.keyboardTips.autoComplete.downArrowDescription"
|
||||
i18n-default-message="Switch focus to auto-complete menu. Use arrows to further select a term"
|
||||
></dd>
|
||||
<dt>Enter/Tab</dt>
|
||||
<dd>Select the currently selected or the top most term in auto-complete menu</dd>
|
||||
<dd
|
||||
i18n-id="timelion.help.mainPage.keyboardTips.autoComplete.enterTabDescription"
|
||||
i18n-default-message="Select the currently selected or the top most term in auto-complete menu"
|
||||
></dd>
|
||||
<dt>Esc</dt>
|
||||
<dd>Close auto-complete menu</dd>
|
||||
<dd
|
||||
i18n-id="timelion.help.mainPage.keyboardTips.autoComplete.escDescription"
|
||||
i18n-default-message="Close auto-complete menu"
|
||||
></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</tab>
|
||||
|
|
|
@ -24,7 +24,7 @@ import moment from 'moment';
|
|||
|
||||
const app = uiModules.get('apps/timelion', []);
|
||||
|
||||
app.directive('timelionHelp', function ($http) {
|
||||
app.directive('timelionHelp', function ($http, i18n) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template,
|
||||
|
@ -38,6 +38,52 @@ app.directive('timelionHelp', function ($http) {
|
|||
$scope.es = {
|
||||
invalidCount: 0
|
||||
};
|
||||
|
||||
$scope.translations = {
|
||||
nextButtonLabel: i18n('timelion.help.nextPageButtonLabel', {
|
||||
defaultMessage: 'Next',
|
||||
}),
|
||||
previousButtonLabel: i18n('timelion.help.previousPageButtonLabel', {
|
||||
defaultMessage: 'Previous',
|
||||
}),
|
||||
dontShowHelpButtonLabel: i18n('timelion.help.dontShowHelpButtonLabel', {
|
||||
defaultMessage: `Don't show this again`,
|
||||
}),
|
||||
strongNextText: i18n('timelion.help.welcome.content.strongNextText', {
|
||||
defaultMessage: 'Next',
|
||||
}),
|
||||
emphasizedEverythingText: i18n('timelion.help.welcome.content.emphasizedEverythingText', {
|
||||
defaultMessage: 'everything',
|
||||
}),
|
||||
notValidAdvancedSettingsPath: i18n('timelion.help.configuration.notValid.advancedSettingsPathText', {
|
||||
defaultMessage: 'Management / Kibana / Advanced Settings'
|
||||
}),
|
||||
validAdvancedSettingsPath: i18n('timelion.help.configuration.valid.advancedSettingsPathText', {
|
||||
defaultMessage: 'Management/Kibana/Advanced Settings',
|
||||
}),
|
||||
esAsteriskQueryDescription: i18n('timelion.help.querying.esAsteriskQueryDescriptionText', {
|
||||
defaultMessage: 'hey Elasticsearch, find everything in my default index',
|
||||
}),
|
||||
esIndexQueryDescription: i18n('timelion.help.querying.esIndexQueryDescriptionText', {
|
||||
defaultMessage: 'use * as the q (query) for the logstash-* index',
|
||||
}),
|
||||
strongAddText: i18n('timelion.help.expressions.strongAddText', {
|
||||
defaultMessage: 'Add',
|
||||
}),
|
||||
twoExpressionsDescriptionTitle: i18n('timelion.help.expressions.examples.twoExpressionsDescriptionTitle', {
|
||||
defaultMessage: 'Double the fun.',
|
||||
}),
|
||||
customStylingDescriptionTitle: i18n('timelion.help.expressions.examples.customStylingDescriptionTitle', {
|
||||
defaultMessage: 'Custom styling.',
|
||||
}),
|
||||
namedArgumentsDescriptionTitle: i18n('timelion.help.expressions.examples.namedArgumentsDescriptionTitle', {
|
||||
defaultMessage: 'Named arguments.',
|
||||
}),
|
||||
groupedExpressionsDescriptionTitle: i18n('timelion.help.expressions.examples.groupedExpressionsDescriptionTitle', {
|
||||
defaultMessage: 'Grouped expressions.',
|
||||
}),
|
||||
};
|
||||
|
||||
getFunctions();
|
||||
checkElasticsearch();
|
||||
}
|
||||
|
@ -73,7 +119,7 @@ app.directive('timelionHelp', function ($http) {
|
|||
} catch (e) {
|
||||
if (_.get(resp, 'data.resp.message')) return _.get(resp, 'data.resp.message');
|
||||
if (_.get(resp, 'data.resp.output.payload.message')) return _.get(resp, 'data.resp.output.payload.message');
|
||||
return 'Unknown error';
|
||||
return i18n('timelion.help.unknownErrorMessage', { defaultMessage: 'Unknown error' });
|
||||
}
|
||||
}());
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<input
|
||||
input-focus
|
||||
aria-label="Custom interval"
|
||||
aria-label="{{ ::'timelion.intervals.customIntervalAriaLabel' | i18n: { defaultMessage: 'Custom interval' } }}"
|
||||
class="kuiTextInput timInterval__input"
|
||||
ng-show="interval === 'other'"
|
||||
ng-class="{ 'timInterval__input--compact': interval === 'other' }"
|
||||
ng-model="otherInterval"
|
||||
><select
|
||||
id="timelionInterval"
|
||||
aria-label="Select interval"
|
||||
aria-label="{{ ::'timelion.intervals.selectIntervalAriaLabel' | i18n: { defaultMessage: 'Select interval' } }}"
|
||||
class="kuiSelect timInterval__presets"
|
||||
ng-class="{ 'timInterval__presets--compact': interval === 'other'}"
|
||||
ng-model="interval"
|
||||
|
|
|
@ -42,7 +42,7 @@ app.directive('timelionInterval', function ($compile, $timeout) {
|
|||
'1w': '1 week',
|
||||
'1M': '1 month',
|
||||
'1y': '1 year',
|
||||
'other': 'other'
|
||||
'other': 'other',
|
||||
};
|
||||
|
||||
$scope.$watch('model', function (newVal, oldVal) {
|
||||
|
|
|
@ -10,8 +10,13 @@
|
|||
<span class="fa fa-bolt" ng-click="showStats = !showStats"></span>
|
||||
|
||||
<span class="timApp__stats" ng-show="showStats">
|
||||
Query Time {{stats.queryTime - stats.invokeTime}}ms /
|
||||
Processing Time {{stats.sheetTime - stats.queryTime}}ms
|
||||
<span
|
||||
i18n-id="timelion.topNavMenu.statsDescription"
|
||||
i18n-default-message="Query Time {queryTime}ms / Processing Time {processingTime}ms"
|
||||
i18n-values="{
|
||||
queryTime: stats.queryTime - stats.invokeTime,
|
||||
processingTime: stats.sheetTime - stats.queryTime,
|
||||
}"></span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -43,7 +48,7 @@
|
|||
|
||||
<button
|
||||
type="submit"
|
||||
aria-label="Search"
|
||||
aria-label="{{ ::'timelion.search.submitAriaLabel' | i18n: { defaultMessage: 'Search' } }}"
|
||||
class="kuiButton kuiButton--primary fullWidth kuiVerticalRhythmSmall"
|
||||
>
|
||||
<span aria-hidden="true" class="kuiButton__icon kuiIcon fa-play"></span>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export default function Panel(name, config) {
|
||||
export default function Panel(name, config, i18n) {
|
||||
|
||||
this.name = name;
|
||||
|
||||
|
@ -25,7 +25,12 @@ export default function Panel(name, config) {
|
|||
|
||||
this.render = config.render;
|
||||
|
||||
if (!config.render) throw new Error ('Panel must have a rendering function');
|
||||
|
||||
if (!config.render) {
|
||||
throw new Error (
|
||||
i18n('timelion.panels.noRenderFunctionErrorMessage', {
|
||||
defaultMessage: 'Panel must have a rendering function'
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
import Panel from '../panel';
|
||||
import panelRegistry from '../../lib/panel_registry';
|
||||
|
||||
panelRegistry.register(function timeChartProvider(Private) {
|
||||
panelRegistry.register(function timeChartProvider(Private, i18n) {
|
||||
// Schema is broken out so that it may be extended for use in other plugins
|
||||
// Its also easier to test.
|
||||
return new Panel('timechart', Private(require('./schema'))());
|
||||
return new Panel('timechart', Private(require('./schema'))(), i18n);
|
||||
});
|
||||
|
|
|
@ -19,11 +19,18 @@
|
|||
|
||||
import moment from 'moment';
|
||||
|
||||
export default function xaxisFormatterProvider(config) {
|
||||
export default function xaxisFormatterProvider(config, i18n) {
|
||||
|
||||
function getFormat(esInterval) {
|
||||
const parts = esInterval.match(/(\d+)(ms|s|m|h|d|w|M|y|)/);
|
||||
if (parts == null || parts[1] == null || parts[2] == null) throw new Error ('Unknown interval');
|
||||
|
||||
if (parts == null || parts[1] == null || parts[2] == null) {
|
||||
throw new Error (
|
||||
i18n('timelion.panels.timechart.unknownIntervalErrorMessage', {
|
||||
defaultMessage: 'Unknown interval',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const interval = moment.duration(Number(parts[1]), parts[2]);
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
<form role="form" ng-submit="fetch()">
|
||||
<h2 class="kuiLocalDropdownTitle">
|
||||
Open Sheet
|
||||
</h2>
|
||||
<h2
|
||||
class="kuiLocalDropdownTitle"
|
||||
i18n-id="timelion.topNavMenu.openSheetTitle"
|
||||
i18n-default-message="Open Sheet"
|
||||
></h2>
|
||||
|
||||
<saved-object-finder
|
||||
type="timelion-sheet"
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
<div class="list-group">
|
||||
<button class="list-group-item" ng-click="section = 'sheet'" type="button">
|
||||
<h4 class="list-group-item-heading">Save entire Timelion sheet</h4>
|
||||
<p class="list-group-item-text">
|
||||
You want this option if you mostly use Timelion expressions from within the Timelion app and don't need to
|
||||
add Timelion charts to Kibana dashboards. You may also want this if you make use of references to other
|
||||
panels.
|
||||
</p>
|
||||
<h4
|
||||
class="list-group-item-heading"
|
||||
i18n-id="timelion.topNavMenu.save.saveEntireSheetTitle"
|
||||
i18n-default-message="Save entire Timelion sheet"
|
||||
></h4>
|
||||
<p
|
||||
class="list-group-item-text"
|
||||
i18n-id="timelion.topNavMenu.save.saveEntireSheetDescription"
|
||||
i18n-default-message="You want this option if you mostly use Timelion expressions from within
|
||||
the Timelion app and don't need to add Timelion charts to Kibana
|
||||
dashboards. You may also want this if you make use of references to
|
||||
other panels."
|
||||
></p>
|
||||
</button>
|
||||
|
||||
<div class="list-group-item" ng-show="section == 'sheet'">
|
||||
|
@ -13,17 +20,17 @@
|
|||
<label
|
||||
for="savedSheet"
|
||||
class="kuiLabel kuiVerticalRhythmSmall"
|
||||
>
|
||||
Save sheet as
|
||||
</label>
|
||||
i18n-id="timelion.topNavMenu.save.saveEntireSheetLabel"
|
||||
i18n-default-message="Save sheet as"
|
||||
></label>
|
||||
|
||||
<input
|
||||
id="savedSheet"
|
||||
ng-model="opts.savedSheet.title"
|
||||
input-focus="select"
|
||||
class="form-control kuiVerticalRhythmSmall"
|
||||
placeholder="Name this sheet..."
|
||||
aria-label="Name"
|
||||
placeholder="{{ ::'timelion.topNavMenu.save.saveEntireSheet.inputPlaceholder' | i18n: { defaultMessage: 'Name this sheet...' } }}"
|
||||
aria-label="{{ ::'timelion.topNavMenu.save.saveEntireSheet.inputAriaLabel' | i18n: { defaultMessage: 'Name' } }}"
|
||||
>
|
||||
|
||||
<saved-object-save-as-check-box
|
||||
|
@ -35,34 +42,63 @@
|
|||
ng-disabled="!opts.savedSheet.title"
|
||||
type="submit"
|
||||
class="kuiButton kuiButton--primary kuiVerticalRhythmSmall"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
i18n-id="timelion.topNavMenu.save.saveEntireSheet.submitButtonLabel"
|
||||
i18n-default-message="Save"
|
||||
></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<button class="list-group-item" ng-click="section = 'expression'" type="button">
|
||||
<h4 class="list-group-item-heading">Save current expression as Kibana dashboard panel</h4>
|
||||
<p class="list-group-item-text">
|
||||
Need to add a chart to a Kibana dashboard? We can do that! This option will save your currently selected
|
||||
expression as a panel that can be added to Kibana dashboards as you would add anything else. Note, if you
|
||||
use references to other panels you will need to remove the refences by copying the referenced expression
|
||||
directly into the expression you are saving. Click a chart to select a different expression to save.
|
||||
</p>
|
||||
<h4
|
||||
class="list-group-item-heading"
|
||||
i18n-id="timelion.topNavMenu.save.saveAsDashboardPanelTitle"
|
||||
i18n-default-message="Save current expression as Kibana dashboard panel"
|
||||
></h4>
|
||||
<p
|
||||
class="list-group-item-text"
|
||||
i18n-id="timelion.topNavMenu.save.saveAsDashboardPanelDescription"
|
||||
i18n-default-message="Need to add a chart to a Kibana dashboard? We can do that! This option
|
||||
will save your currently selected expression as a panel that can be
|
||||
added to Kibana dashboards as you would add anything else. Note, if you
|
||||
use references to other panels you will need to remove the refences by
|
||||
copying the referenced expression directly into the expression you are
|
||||
saving. Click a chart to select a different expression to save."
|
||||
></p>
|
||||
</button>
|
||||
|
||||
<div class="list-group-item" ng-show="section == 'expression'">
|
||||
<form role="form" class="container-fluid" ng-submit="opts.saveExpression(panelTitle)">
|
||||
<div class="form-group">
|
||||
<label class="control-label">Currently selected expression</label>
|
||||
<label
|
||||
class="control-label"
|
||||
i18n-id="timelion.topNavMenu.save.saveAsDashboardPanel.selectedExpressionLabel"
|
||||
i18n-default-message="Currently selected expression"
|
||||
></label>
|
||||
<code>{{opts.state.sheet[opts.state.selected]}}</code>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="savedExpression" class="control-label">Save expression as</label>
|
||||
<input id="savedExpression" ng-model="panelTitle" input-focus="select" class="form-control" placeholder="Name this panel">
|
||||
<label
|
||||
for="savedExpression"
|
||||
class="control-label"
|
||||
i18n-id="timelion.topNavMenu.save.saveAsDashboardPanelLabel"
|
||||
i18n-default-message="Save expression as"
|
||||
></label>
|
||||
<input
|
||||
id="savedExpression"
|
||||
ng-model="panelTitle"
|
||||
input-focus="select"
|
||||
class="form-control"
|
||||
placeholder="{{ ::'timelion.topNavMenu.save.saveAsDashboardPanel.inputPlaceholder' | i18n: { defaultMessage: 'Name this panel' } }}"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button ng-disabled="!panelTitle" type="submit" class="kuiButton kuiButton--primary">Save</button>
|
||||
<button
|
||||
ng-disabled="!panelTitle"
|
||||
type="submit"
|
||||
class="kuiButton kuiButton--primary"
|
||||
i18n-id="timelion.topNavMenu.save.saveAsDashboardPanel.submitButtonLabel"
|
||||
i18n-default-message="Save"
|
||||
></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
<form role="form">
|
||||
<h2 class="kuiLocalDropdownTitle">
|
||||
Sheet options
|
||||
</h2>
|
||||
<h2
|
||||
class="kuiLocalDropdownTitle"
|
||||
i18n-id="timelion.topNavMenu.sheetOptionsTitle"
|
||||
i18n-default-message="Sheet options"
|
||||
></h2>
|
||||
|
||||
<div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="timelionColCount">Columns <small>Column count must divide evenly into 12</small></label>
|
||||
<label
|
||||
for="timelionColCount"
|
||||
i18n-id="timelion.topNavMenu.options.columnsCountLabel"
|
||||
i18n-default-message="Columns (Column count must divide evenly into 12)"
|
||||
></label>
|
||||
<select class="form-control"
|
||||
id="timelionColCount"
|
||||
ng-change="opts.search()"
|
||||
|
@ -14,7 +20,11 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="timelionRowCount">Rows <small>This is a target based on the current window height</small></label>
|
||||
<label
|
||||
for="timelionRowCount"
|
||||
i18n-id="timelion.topNavMenu.options.rowsCountLabel"
|
||||
i18n-default-message="Rows (This is a target based on the current window height)"
|
||||
></label>
|
||||
<select class="form-control"
|
||||
id="timelionRowCount"
|
||||
ng-change="opts.search()"
|
||||
|
|
|
@ -22,12 +22,14 @@ import {
|
|||
FeatureCatalogueCategory,
|
||||
} from 'ui/registry/feature_catalogue';
|
||||
|
||||
FeatureCatalogueRegistryProvider.register(() => {
|
||||
FeatureCatalogueRegistryProvider.register(i18n => {
|
||||
return {
|
||||
id: 'timelion',
|
||||
title: 'Timelion',
|
||||
description:
|
||||
'Use an expression language to analyze time series data and visualize the results.',
|
||||
description: i18n('timelion.registerFeatureDescription', {
|
||||
defaultMessage:
|
||||
'Use an expression language to analyze time series data and visualize the results.',
|
||||
}),
|
||||
icon: 'timelionApp',
|
||||
path: '/app/timelion',
|
||||
showOnHomePage: false,
|
||||
|
|
|
@ -33,7 +33,7 @@ import editorConfigTemplate from './timelion_vis_params.html';
|
|||
// register the provider with the visTypes registry so that other know it exists
|
||||
VisTypesRegistryProvider.register(TimelionVisProvider);
|
||||
|
||||
export default function TimelionVisProvider(Private) {
|
||||
export default function TimelionVisProvider(Private, i18n) {
|
||||
const VisFactory = Private(VisFactoryProvider);
|
||||
const timelionRequestHandler = Private(TimelionRequestHandlerProvider);
|
||||
|
||||
|
@ -43,7 +43,9 @@ export default function TimelionVisProvider(Private) {
|
|||
name: 'timelion',
|
||||
title: 'Timelion',
|
||||
icon: 'visTimelion',
|
||||
description: 'Build time-series using functional expressions',
|
||||
description: i18n('timelion.timelionDescription', {
|
||||
defaultMessage: 'Build time-series using functional expressions',
|
||||
}),
|
||||
category: CATEGORY.TIME,
|
||||
visConfig: {
|
||||
defaults: {
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
<div class="kuiSideBarSection">
|
||||
<div class="form-group">
|
||||
<label for="timelionInterval">Interval</label>
|
||||
<label
|
||||
for="timelionInterval"
|
||||
i18n-id="timelion.vis.intervalLabel"
|
||||
i18n-default-message="Interval"
|
||||
></label>
|
||||
<div class="form-group">
|
||||
<timelion-interval model="editorState.params.interval"></timelion-interval>
|
||||
</div>
|
||||
|
@ -8,7 +12,10 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<label>Timelion Expression</label>
|
||||
<label
|
||||
i18n-id="timelion.vis.expressionLabel"
|
||||
i18n-default-message="Timelion Expression"
|
||||
></label>
|
||||
</div>
|
||||
|
||||
<timelion-expression-input
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
// Upsampling of non-cumulative sets
|
||||
// Good: average, min, max
|
||||
|
@ -27,7 +28,12 @@ import _ from 'lodash';
|
|||
export default function carry(dataTuples, targetTuples) {
|
||||
|
||||
if (dataTuples.length > targetTuples.length) {
|
||||
throw new Error (`Don't use the 'carry' fit method to down sample, use 'scale' or 'average'`);
|
||||
throw new Error (
|
||||
i18n.translate('timelion.fitFunctions.carry.downSampleErrorMessage', {
|
||||
defaultMessage: `Don't use the 'carry' fit method to down sample, use 'scale' or 'average'`,
|
||||
description: '"carry", "scale" and "average" are parameter values that must not be translated.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
let currentCarry = dataTuples[0][1];
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
import _ from 'lodash';
|
||||
import Promise from 'bluebird';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import parseSheet from './lib/parse_sheet.js';
|
||||
import parseDateMath from '../lib/date_math.js';
|
||||
import repositionArguments from './lib/reposition_arguments.js';
|
||||
|
@ -79,7 +81,14 @@ export default function chainRunner(tlConfig) {
|
|||
case 'seriesList':
|
||||
return item;
|
||||
}
|
||||
throw new Error ('Argument type not supported: ' + JSON.stringify(item));
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.unknownArgumentTypeErrorMessage', {
|
||||
defaultMessage: 'Argument type not supported: {argument}',
|
||||
values: {
|
||||
argument: JSON.stringify(item),
|
||||
},
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
// Only applies to already resolved arguments
|
||||
export default function indexArguments(functionDef, orderedArgs) {
|
||||
|
@ -26,7 +27,16 @@ export default function indexArguments(functionDef, orderedArgs) {
|
|||
|
||||
// This almost certainly is not required
|
||||
const allowedLength = functionDef.extended ? functionDef.args.length + 2 : functionDef.args.length;
|
||||
if (orderedArgs.length > allowedLength) throw new Error ('Too many arguments passed to: ' + functionDef.name);
|
||||
if (orderedArgs.length > allowedLength) {
|
||||
throw new Error (
|
||||
i18n.translate('timelion.serverSideErrors.argumentsOverflowErrorMessage', {
|
||||
defaultMessage: 'Too many arguments passed to: {functionName}',
|
||||
values: {
|
||||
functionName: functionDef.name,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const indexedArgs = {};
|
||||
// Check and index each known argument
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import _ from 'lodash';
|
||||
|
@ -30,7 +31,15 @@ export default function parseSheet(sheet) {
|
|||
return Parser.parse(plot).tree;
|
||||
} catch (e) {
|
||||
if (e.expected) {
|
||||
throw new Error('Expected: ' + e.expected[0].description + ' @ character ' + e.column);
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.sheetParseErrorMessage', {
|
||||
defaultMessage: 'Expected: {expectedDescription} @ character {column}',
|
||||
values: {
|
||||
expectedDescription: e.expected[0].description,
|
||||
column: e.column,
|
||||
},
|
||||
})
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
// Applies to unresolved arguments in the AST
|
||||
export default function repositionArguments(functionDef, unorderedArgs) {
|
||||
|
@ -58,7 +59,17 @@ export default function repositionArguments(functionDef, unorderedArgs) {
|
|||
value = unorderedArg;
|
||||
}
|
||||
|
||||
if (!argDef) throw new Error('Unknown argument to ' + functionDef.name + ': ' + (unorderedArg.name || ('#' + i)));
|
||||
if (!argDef) {
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.unknownArgumentErrorMessage', {
|
||||
defaultMessage: 'Unknown argument to {functionName}: {argumentName}',
|
||||
values: {
|
||||
functionName: functionDef.name,
|
||||
argumentName: (unorderedArg.name || ('#' + i)),
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (storeAsArray) {
|
||||
args[targetIndex] = args[targetIndex] || [];
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
import argType from './arg_type';
|
||||
import _ from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export default function validateArgFn(functionDef) {
|
||||
return function validateArg(value, name, argDef) {
|
||||
|
@ -36,7 +37,17 @@ export default function validateArgFn(functionDef) {
|
|||
else return false;
|
||||
|
||||
if (!isCorrectType) {
|
||||
throw new Error (functionDef.name + '(' + name + ') must be one of ' + JSON.stringify(required) + '. Got: ' + type);
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.wrongFunctionArgumentTypeErrorMessage', {
|
||||
defaultMessage: '{functionName}({argumentName}) must be one of {requiredTypes}. Got: {actualType}',
|
||||
values: {
|
||||
functionName: functionDef.name,
|
||||
argumentName: name,
|
||||
requiredTypes: JSON.stringify(required),
|
||||
actualType: type,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import parseDateMath from '../../lib/date_math.js';
|
||||
import toMS from '../../lib/to_milliseconds.js';
|
||||
|
||||
|
@ -26,9 +28,17 @@ export default function validateTime(time, tlConfig) {
|
|||
const bucketCount = span / interval;
|
||||
const maxBuckets = tlConfig.settings['timelion:max_buckets'];
|
||||
if (bucketCount > maxBuckets) {
|
||||
throw new Error('Max buckets exceeded: ' +
|
||||
Math.round(bucketCount) + ' of ' + maxBuckets + ' allowed. ' +
|
||||
'Choose a larger interval or a shorter time span');
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.bucketsOverflowErrorMessage', {
|
||||
defaultMessage:
|
||||
'Max buckets exceeded: {bucketCount} of {maxBuckets} allowed. ' +
|
||||
'Choose a larger interval or a shorter time span',
|
||||
values: {
|
||||
bucketCount: Math.round(bucketCount),
|
||||
maxBuckets,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import loadFunctions from '../load_functions.js';
|
||||
const fitFunctions = loadFunctions('fit_functions');
|
||||
import TimelionFunction from './timelion_function';
|
||||
|
@ -40,16 +41,25 @@ export default class Datasource extends TimelionFunction {
|
|||
config.args.push({
|
||||
name: 'offset',
|
||||
types: ['string', 'null'],
|
||||
help: 'Offset the series retrieval by a date expression, ' +
|
||||
'e.g., -1M to make events from one month ago appear as if they are happening now. ' +
|
||||
'Offset the series relative to the charts overall time range, by using the value "timerange", ' +
|
||||
'e.g. "timerange:-2" will specify an offset that is twice the overall chart time range to the past.'
|
||||
help: i18n.translate('timelion.help.functions.common.args.offsetHelpText', {
|
||||
defaultMessage:
|
||||
'Offset the series retrieval by a date expression, e.g., -1M to make events from ' +
|
||||
'one month ago appear as if they are happening now. Offset the series relative to the charts ' +
|
||||
'overall time range, by using the value "timerange", e.g. "timerange:-2" will specify an offset ' +
|
||||
'that is twice the overall chart time range to the past.',
|
||||
}),
|
||||
});
|
||||
|
||||
config.args.push({
|
||||
name: 'fit',
|
||||
types: ['string', 'null'],
|
||||
help: 'Algorithm to use for fitting series to the target time span and interval. Available: ' + _.keys(fitFunctions).join(', ')
|
||||
help: i18n.translate('timelion.help.functions.common.args.fitHelpText', {
|
||||
defaultMessage:
|
||||
'Algorithm to use for fitting series to the target time span and interval. Available: {fitFunctions}',
|
||||
values: {
|
||||
fitFunctions: _.keys(fitFunctions).join(', '),
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
// Wrap the original function so we can modify inputs/outputs with offset & fit
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import _ from 'lodash';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
@ -28,7 +29,9 @@ export default new Chainable('abs', {
|
|||
types: ['seriesList']
|
||||
}
|
||||
],
|
||||
help: 'Return the absolute value of each value in the series list',
|
||||
help: i18n.translate('timelion.help.functions.absHelpText', {
|
||||
defaultMessage: 'Return the absolute value of each value in the series list',
|
||||
}),
|
||||
fn: function absFn(args) {
|
||||
return alter(args, function (eachSeries) {
|
||||
const data = _.map(eachSeries.data, function (point) {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../../lib/alter.js';
|
||||
import Chainable from '../../lib/classes/chainable';
|
||||
import _ from 'lodash';
|
||||
|
@ -40,11 +41,21 @@ export default new Chainable('aggregate', {
|
|||
{
|
||||
name: 'function',
|
||||
types: ['string'],
|
||||
help: 'One of ' + _.keys(functions).join(', ')
|
||||
help: i18n.translate('timelion.help.functions.aggregate.args.functionHelpText', {
|
||||
defaultMessage: 'One of {functions}',
|
||||
values: {
|
||||
functions: _.keys(functions).join(', '),
|
||||
},
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Creates a static line based on result of processing all points in the series.' +
|
||||
' Available functions: ' + _.keys(functions).join(', '),
|
||||
help: i18n.translate('timelion.help.functions.aggregateHelpText', {
|
||||
defaultMessage:
|
||||
'Creates a static line based on result of processing all points in the series. Available functions: {functions}',
|
||||
values: {
|
||||
functions: _.keys(functions).join(', '),
|
||||
},
|
||||
}),
|
||||
fn: function aggregateFn(args) {
|
||||
const fn = functions[args.byName.function];
|
||||
if (!fn) throw new Error('.aggregate() function must be one of: ' + _.keys(functions).join(', '));
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
||||
|
@ -29,15 +30,21 @@ export default new Chainable('bars', {
|
|||
{
|
||||
name: 'width',
|
||||
types: ['number', 'null'],
|
||||
help: 'Width of bars in pixels'
|
||||
help: i18n.translate('timelion.help.functions.bars.args.widthHelpText', {
|
||||
defaultMessage: 'Width of bars in pixels',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'stack',
|
||||
types: ['boolean', 'null'],
|
||||
help: 'Should bars be stacked, true by default'
|
||||
help: i18n.translate('timelion.help.functions.bars.args.stackHelpText', {
|
||||
defaultMessage: 'Should bars be stacked, true by default',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Show the seriesList as bars',
|
||||
help: i18n.translate('timelion.help.functions.barsHelpText', {
|
||||
defaultMessage: 'Show the seriesList as bars',
|
||||
}),
|
||||
fn: function barsFn(args) {
|
||||
return alter(args, function (eachSeries, width, stack) {
|
||||
eachSeries.bars = eachSeries.bars || {};
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
import tinygradient from 'tinygradient';
|
||||
|
@ -30,11 +31,16 @@ export default new Chainable('color', {
|
|||
{
|
||||
name: 'color',
|
||||
types: ['string'],
|
||||
help: 'Color of series, as hex, e.g., #c6c6c6 is a lovely light grey. If you specify multiple colors, and have ' +
|
||||
'multiple series, you will get a gradient, e.g., "#00B1CC:#00FF94:#FF3A39:#CC1A6F"'
|
||||
help: i18n.translate('timelion.help.functions.color.args.colorHelpText', {
|
||||
defaultMessage:
|
||||
'Color of series, as hex, e.g., #c6c6c6 is a lovely light grey. If you specify multiple \
|
||||
colors, and have multiple series, you will get a gradient, e.g., "#00B1CC:#00FF94:#FF3A39:#CC1A6F"',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Change the color of the series',
|
||||
help: i18n.translate('timelion.help.functions.colorHelpText', {
|
||||
defaultMessage: 'Change the color of the series',
|
||||
}),
|
||||
fn: function colorFn(args) {
|
||||
const colors = args.byName.color.split(':');
|
||||
const gradientStops = args.byName.inputSeries.list.length;
|
||||
|
@ -55,7 +61,11 @@ export default new Chainable('color', {
|
|||
} else if (colors.length === 1) {
|
||||
eachSeries.color = colors[0];
|
||||
} else {
|
||||
throw new Error('color not provided');
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.colorFunction.colorNotProvidedErrorMessage', {
|
||||
defaultMessage: 'color not provided',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return eachSeries;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import _ from 'lodash';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
@ -31,53 +32,80 @@ export default new Chainable('condition', {
|
|||
{
|
||||
name: 'operator', // <, <=, >, >=, ==, !=
|
||||
types: ['string'],
|
||||
help: 'comparison operator to use for comparison, valid operators are eq (equal), ne (not equal), lt (less than), lte ' +
|
||||
'(less than equal), gt (greater than), gte (greater than equal)',
|
||||
help: i18n.translate('timelion.help.functions.condition.args.operatorHelpText', {
|
||||
defaultMessage:
|
||||
'comparison operator to use for comparison, valid operators are eq (equal), ' +
|
||||
'ne (not equal), lt (less than), lte (less than equal), gt (greater than), gte (greater than equal)',
|
||||
}),
|
||||
suggestions: [
|
||||
{
|
||||
name: 'eq',
|
||||
help: 'equal',
|
||||
help: i18n.translate('timelion.help.functions.condition.args.operator.suggestions.eqHelpText', {
|
||||
defaultMessage: 'equal',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'ne',
|
||||
help: 'not equal'
|
||||
help: i18n.translate('timelion.help.functions.condition.args.operator.suggestions.neHelpText', {
|
||||
defaultMessage: 'not equal',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'lt',
|
||||
help: 'less than'
|
||||
help: i18n.translate('timelion.help.functions.condition.args.operator.suggestions.ltHelpText', {
|
||||
defaultMessage: 'less than',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'lte',
|
||||
help: 'less than equal'
|
||||
help: i18n.translate('timelion.help.functions.condition.args.operator.suggestions.lteHelpText', {
|
||||
defaultMessage: 'less than equal',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'gt',
|
||||
help: 'greater than'
|
||||
help: i18n.translate('timelion.help.functions.condition.args.operator.suggestions.gtHelpText', {
|
||||
defaultMessage: 'greater than',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'gte',
|
||||
help: 'greater than equal'
|
||||
}
|
||||
]
|
||||
help: i18n.translate('timelion.help.functions.condition.args.operator.suggestions.gteHelpText', {
|
||||
defaultMessage: 'greater than equal',
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'if',
|
||||
types: ['number', 'seriesList', 'null'],
|
||||
help: 'The value to which the point will be compared. If you pass a seriesList here the first series will be used'
|
||||
help: i18n.translate('timelion.help.functions.condition.args.ifHelpText', {
|
||||
defaultMessage:
|
||||
'The value to which the point will be compared. If you pass a seriesList here the first series will be used',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'then',
|
||||
types: ['number', 'seriesList', 'null'],
|
||||
help: 'The value the point will be set to if the comparison is true. If you pass a seriesList here the first series will be used'
|
||||
help: i18n.translate('timelion.help.functions.condition.args.thenHelpText', {
|
||||
defaultMessage:
|
||||
'The value the point will be set to if the comparison is true. If you pass a seriesList here the first series will be used',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'else',
|
||||
types: ['number', 'seriesList', 'null'],
|
||||
help: 'The value the point will be set to if the comparison is false. If you pass a seriesList here the first series will be used'
|
||||
help: i18n.translate('timelion.help.functions.condition.args.elseHelpText', {
|
||||
defaultMessage:
|
||||
'The value the point will be set to if the comparison is false. If you pass a seriesList here the first series will be used',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Compares each point to a number, or the same point in another series using an operator, then sets its value' +
|
||||
'to the result if the condition proves true, with an optional else.',
|
||||
help: i18n.translate('timelion.help.functions.conditionHelpText', {
|
||||
defaultMessage:
|
||||
'Compares each point to a number, or the same point in another series using an operator, ' +
|
||||
'then sets its value to the result if the condition proves true, with an optional else.',
|
||||
}),
|
||||
aliases: ['if'],
|
||||
fn: function conditionFn(args) {
|
||||
const config = args.byName;
|
||||
|
@ -87,7 +115,11 @@ export default new Chainable('condition', {
|
|||
if (argType(source) === 'number') return source;
|
||||
if (argType(source) === 'null') return null;
|
||||
if (argType(source) === 'seriesList') return source.list[0].data[i][1];
|
||||
throw new Error ('must be a number or a seriesList');
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.conditionFunction.wrongArgTypeErrorMessage', {
|
||||
defaultMessage: 'must be a number or a seriesList',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const ifVal = getNumber(config.if);
|
||||
|
@ -109,7 +141,11 @@ export default new Chainable('condition', {
|
|||
case 'ne':
|
||||
return point[1] !== ifVal ? thenVal : elseVal;
|
||||
default:
|
||||
throw new Error ('Unknown operator');
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.conditionFunction.unknownOperatorErrorMessage', {
|
||||
defaultMessage: 'Unknown operator',
|
||||
})
|
||||
);
|
||||
}
|
||||
}());
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import _ from 'lodash';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
@ -30,10 +31,14 @@ export default new Chainable('cusum', {
|
|||
{
|
||||
name: 'base',
|
||||
types: ['number'],
|
||||
help: 'Number to start at. Basically just adds this to the beginning of the series'
|
||||
help: i18n.translate('timelion.help.functions.cusum.args.baseHelpText', {
|
||||
defaultMessage: 'Number to start at. Basically just adds this to the beginning of the series',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Return the cumulative sum of a series, starting at a base.',
|
||||
help: i18n.translate('timelion.help.functions.cusumHelpText', {
|
||||
defaultMessage: 'Return the cumulative sum of a series, starting at a base.',
|
||||
}),
|
||||
fn: function cusumFn(args) {
|
||||
return alter(args, function (eachSeries, base) {
|
||||
const pairs = eachSeries.data;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import _ from 'lodash';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
@ -28,7 +29,9 @@ export default new Chainable('derivative', {
|
|||
types: ['seriesList']
|
||||
}
|
||||
],
|
||||
help: 'Plot the change in values over time.',
|
||||
help: i18n.translate('timelion.help.functions.derivativeHelpText', {
|
||||
defaultMessage: 'Plot the change in values over time.',
|
||||
}),
|
||||
fn: function derivativeFn(args) {
|
||||
return alter(args, function (eachSeries) {
|
||||
const pairs = eachSeries.data;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import reduce from '../lib/reduce.js';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
||||
|
@ -29,10 +30,16 @@ export default new Chainable('divide', {
|
|||
{
|
||||
name: 'divisor',
|
||||
types: ['seriesList', 'number'],
|
||||
help: 'Number or series to divide by. SeriesList with multiple series will be applied label-wise.'
|
||||
help: i18n.translate('timelion.help.functions.divide.args.divisorHelpText', {
|
||||
defaultMessage:
|
||||
'Number or series to divide by. SeriesList with multiple series will be applied label-wise.',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Divides the values of one or more series in a seriesList to each position, in each series, of the input seriesList',
|
||||
help: i18n.translate('timelion.help.functions.divideHelpText', {
|
||||
defaultMessage:
|
||||
'Divides the values of one or more series in a seriesList to each position, in each series, of the input seriesList',
|
||||
}),
|
||||
fn: function divideFn(args) {
|
||||
return reduce(args, function (a, b) {
|
||||
return a / b;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import _ from 'lodash';
|
||||
import Datasource from '../../lib/classes/datasource';
|
||||
import buildRequest from './lib/build_request';
|
||||
|
@ -28,44 +29,73 @@ export default new Datasource('es', {
|
|||
name: 'q',
|
||||
types: ['string', 'null'],
|
||||
multi: true,
|
||||
help: 'Query in lucene query string syntax'
|
||||
help: i18n.translate('timelion.help.functions.es.args.qHelpText', {
|
||||
defaultMessage: 'Query in lucene query string syntax',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'metric',
|
||||
types: ['string', 'null'],
|
||||
multi: true,
|
||||
help: 'An elasticsearch metric agg: avg, sum, min, max, percentiles or cardinality, followed by a field.' +
|
||||
' E.g., "sum:bytes", "percentiles:bytes:95,99,99.9" or just "count"'
|
||||
help: i18n.translate('timelion.help.functions.es.args.metricHelpText', {
|
||||
defaultMessage:
|
||||
'An elasticsearch metric agg: avg, sum, min, max, percentiles or cardinality, followed by a field. ' +
|
||||
'E.g., "sum:bytes", "percentiles:bytes:95,99,99.9" or just "count"',
|
||||
description:
|
||||
`avg, sum, min, max, percentiles and cardinality are keywords in the expression ` +
|
||||
`and must not be translated. Also don't translate the examples.`,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'split',
|
||||
types: ['string', 'null'],
|
||||
multi: true,
|
||||
help: 'An elasticsearch field to split the series on and a limit. E.g., "hostname:10" to get the top 10 hostnames'
|
||||
help: i18n.translate('timelion.help.functions.es.args.splitHelpText', {
|
||||
defaultMessage:
|
||||
'An elasticsearch field to split the series on and a limit. E.g., "{hostnameSplitArg}" to get the top 10 hostnames',
|
||||
values: {
|
||||
hostnameSplitArg: 'hostname:10',
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'index',
|
||||
types: ['string', 'null'],
|
||||
help: 'Index to query, wildcards accepted. Provide Index Pattern name for scripted fields and ' +
|
||||
'field name type ahead suggestions for metrics, split, and timefield arguments.'
|
||||
help: i18n.translate('timelion.help.functions.es.args.indexHelpText', {
|
||||
defaultMessage:
|
||||
'Index to query, wildcards accepted. Provide Index Pattern name for scripted fields and ' +
|
||||
'field name type ahead suggestions for metrics, split, and timefield arguments.',
|
||||
description: '"metrics", "split" and "timefield" are referring to parameter names and should not be translated.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'timefield',
|
||||
types: ['string', 'null'],
|
||||
help: 'Field of type "date" to use for x-axis'
|
||||
help: i18n.translate('timelion.help.functions.es.args.timefieldHelpText', {
|
||||
defaultMessage: 'Field of type "date" to use for x-axis',
|
||||
description: '"date" is a field type and should not be translated.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'kibana',
|
||||
types: ['boolean', 'null'],
|
||||
help: 'Respect filters on Kibana dashboards. Only has an effect when using on Kibana dashboards'
|
||||
help: i18n.translate('timelion.help.functions.es.args.kibanaHelpText', {
|
||||
defaultMessage:
|
||||
'Respect filters on Kibana dashboards. Only has an effect when using on Kibana dashboards',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'interval', // You really shouldn't use this, use the interval picker instead
|
||||
types: ['string', 'null'],
|
||||
help: '**DO NOT USE THIS**. Its fun for debugging fit functions, but you really should use the interval picker'
|
||||
help: i18n.translate('timelion.help.functions.es.args.intervalHelpText', {
|
||||
defaultMessage:
|
||||
`**DO NOT USE THIS**. It's fun for debugging fit functions, but you really should use the interval picker`,
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Pull data from an elasticsearch instance',
|
||||
help: i18n.translate('timelion.help.functions.esHelpText', {
|
||||
defaultMessage: 'Pull data from an elasticsearch instance',
|
||||
}),
|
||||
aliases: ['elasticsearch'],
|
||||
fn: async function esFn(args, tlConfig) {
|
||||
|
||||
|
@ -100,7 +130,16 @@ export default new Datasource('es', {
|
|||
|
||||
const { callWithRequest } = tlConfig.server.plugins.elasticsearch.getCluster('data');
|
||||
const resp = await callWithRequest(tlConfig.request, 'search', body);
|
||||
if (!resp._shards.total) throw new Error('Elasticsearch index not found: ' + config.index);
|
||||
if (!resp._shards.total) {
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.esFunction.indexNotFoundErrorMessage', {
|
||||
defaultMessage: 'Elasticsearch index not found: {index}',
|
||||
values: {
|
||||
index: config.index,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
return {
|
||||
type: 'seriesList',
|
||||
list: toSeriesList(resp.aggregations, config)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
||||
|
@ -27,10 +28,12 @@ export default new Chainable('first', {
|
|||
types: ['seriesList']
|
||||
}
|
||||
],
|
||||
help: 'This is an internal function that simply returns the input seriesList. Don\'t use this',
|
||||
help: i18n.translate('timelion.help.functions.firstHelpText', {
|
||||
defaultMessage: `This is an internal function that simply returns the input seriesList. Don't use this`,
|
||||
}),
|
||||
fn: function firstFn(args) {
|
||||
return alter(args, function (eachSeries) {
|
||||
return eachSeries;
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import _ from 'lodash';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
@ -32,13 +33,21 @@ export default new Chainable('fit', {
|
|||
{
|
||||
name: 'mode',
|
||||
types: ['string'],
|
||||
help: `The algorithm to use for fitting the series to the target. One of: ${_.keys(fitFunctions).join(', ')}`,
|
||||
help: i18n.translate('timelion.help.functions.fit.args.modeHelpText', {
|
||||
defaultMessage:
|
||||
'The algorithm to use for fitting the series to the target. One of: {fitFunctions}',
|
||||
values: {
|
||||
fitFunctions: _.keys(fitFunctions).join(', '),
|
||||
},
|
||||
}),
|
||||
suggestions: _.keys(fitFunctions).map(key => {
|
||||
return { name: key };
|
||||
})
|
||||
}
|
||||
],
|
||||
help: 'Fills null values using a defined fit function',
|
||||
help: i18n.translate('timelion.help.functions.fitHelpText', {
|
||||
defaultMessage: 'Fills null values using a defined fit function',
|
||||
}),
|
||||
fn: function absFn(args) {
|
||||
return alter(args, function (eachSeries, mode) {
|
||||
|
||||
|
|
|
@ -17,21 +17,29 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import _ from 'lodash';
|
||||
import fetch from 'node-fetch';
|
||||
import moment from 'moment';
|
||||
import Datasource from '../lib/classes/datasource';
|
||||
|
||||
|
||||
export default new Datasource ('graphite', {
|
||||
args: [
|
||||
{
|
||||
name: 'metric', // _test-data.users.*.data
|
||||
types: ['string'],
|
||||
help: 'Graphite metric to pull, e.g., _test-data.users.*.data'
|
||||
help: i18n.translate('timelion.help.functions.graphite.args.metricHelpText', {
|
||||
defaultMessage: 'Graphite metric to pull, e.g., {metricExample}',
|
||||
values: {
|
||||
metricExample: '_test-data.users.*.data',
|
||||
},
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: `[experimental] Pull data from graphite. Configure your graphite server in Kibana's Advanced Settings`,
|
||||
help: i18n.translate('timelion.help.functions.graphiteHelpText', {
|
||||
defaultMessage:
|
||||
`[experimental] Pull data from graphite. Configure your graphite server in Kibana's Advanced Settings`,
|
||||
}),
|
||||
fn: function graphite(args, tlConfig) {
|
||||
|
||||
const config = args.byName;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
||||
|
@ -29,10 +30,14 @@ export default new Chainable('hide', {
|
|||
{
|
||||
name: 'hide',
|
||||
types: ['boolean', 'null'],
|
||||
help: 'Hide or unhide the series'
|
||||
help: i18n.translate('timelion.help.functions.hide.args.hideHelpText', {
|
||||
defaultMessage: 'Hide or unhide the series',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Hide the series by default',
|
||||
help: i18n.translate('timelion.help.functions.hideHelpText', {
|
||||
defaultMessage: 'Hide the series by default',
|
||||
}),
|
||||
fn: function hideFn(args) {
|
||||
return alter(args, function (eachSeries, hide) {
|
||||
eachSeries._hide = hide == null ? true : hide;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import _ from 'lodash';
|
||||
import Chainable from '../../lib/classes/chainable';
|
||||
import ses from './lib/ses';
|
||||
|
@ -33,46 +34,68 @@ export default new Chainable('holt', {
|
|||
{
|
||||
name: 'alpha',
|
||||
types: ['number'],
|
||||
help: `
|
||||
help: i18n.translate('timelion.help.functions.holt.args.alphaHelpText', {
|
||||
defaultMessage:
|
||||
`
|
||||
Smoothing weight from 0 to 1.
|
||||
Increasing alpha will make the new series more closely follow the original.
|
||||
Lowering it will make the series smoother`
|
||||
Lowering it will make the series smoother`,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'beta',
|
||||
types: ['number'],
|
||||
help: `
|
||||
help: i18n.translate('timelion.help.functions.holt.args.betaHelpText', {
|
||||
defaultMessage:
|
||||
`
|
||||
Trending weight from 0 to 1.
|
||||
Increasing beta will make rising/falling lines continue to rise/fall longer.
|
||||
Lowering it will make the function learn the new trend faster`
|
||||
Lowering it will make the function learn the new trend faster`,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'gamma',
|
||||
types: ['number'],
|
||||
help: `
|
||||
help: i18n.translate('timelion.help.functions.holt.args.gammaHelpText', {
|
||||
defaultMessage:
|
||||
`
|
||||
Seasonal weight from 0 to 1. Does your data look like a wave?
|
||||
Increasing this will give recent seasons more importance, thus changing the wave form faster.
|
||||
Lowering it will reduce the importance of new seasons, making history more important.
|
||||
`
|
||||
`,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'season',
|
||||
types: ['string'],
|
||||
help: 'How long is the season, e.g., 1w if you pattern repeats weekly. (Only useful with gamma)'
|
||||
help: i18n.translate('timelion.help.functions.holt.args.seasonHelpText', {
|
||||
defaultMessage:
|
||||
'How long is the season, e.g., 1w if your pattern repeats weekly. (Only useful with gamma)',
|
||||
description:
|
||||
'"1w" is an expression value and should not be translated. "gamma" is a parameter name and should not be translated.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'sample',
|
||||
types: ['number', 'null'],
|
||||
help: `
|
||||
help: i18n.translate('timelion.help.functions.holt.args.sampleHelpText', {
|
||||
defaultMessage:
|
||||
`
|
||||
The number of seasons to sample before starting to "predict" in a seasonal series.
|
||||
(Only useful with gamma, Default: all)`
|
||||
(Only useful with gamma, Default: all)`,
|
||||
description: '"gamma" and "all" are parameter names and values and must not be translated.',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: `
|
||||
help: i18n.translate('timelion.help.functions.holtHelpText', {
|
||||
defaultMessage:
|
||||
`
|
||||
Sample the beginning of a series and use it to forecast what should happen
|
||||
via several optional parameters. In general, like everything, this is crappy at predicting the
|
||||
future. You're much better off using it to predict what should be happening right now, for the
|
||||
purpose of anomaly detection. Note that nulls will be filled with forecasted values. Deal with it.`,
|
||||
via several optional parameters. In general, this doesn't really predict the
|
||||
future, but predicts what should be happening right now according to past data,
|
||||
which can be useful for anomaly detection. Note that nulls will be filled with forecasted values.`,
|
||||
description: '"null" is a data value here and must not be translated.',
|
||||
}),
|
||||
fn: function expsmoothFn(args, tlConfig) {
|
||||
|
||||
const newSeries = _.cloneDeep(args.byName.inputSeries);
|
||||
|
@ -103,7 +126,11 @@ export default new Chainable('holt', {
|
|||
|
||||
if (alpha != null && beta != null && gamma != null) {
|
||||
if (!sample || !args.byName.season || sample < 2) {
|
||||
throw new Error('Must specify a season length and a sample size >= 2');
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.holtFunction.missingParamsErrorMessage', {
|
||||
defaultMessage: 'Must specify a season length and a sample size >= 2',
|
||||
})
|
||||
);
|
||||
}
|
||||
const season = Math.round(toMilliseconds(args.byName.season) / toMilliseconds(tlConfig.time.interval));
|
||||
points = tes(points, alpha, beta, gamma, season, sample);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import _ from 'lodash';
|
||||
|
||||
export default function des(points, alpha, beta) {
|
||||
|
@ -27,7 +28,11 @@ export default function des(points, alpha, beta) {
|
|||
let unknownCount = 0;
|
||||
|
||||
if (points.length < 2) {
|
||||
throw new Error ('You need at least 2 points to use double exponential smoothing');
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.holtFunction.notEnoughPointsErrorMessage', {
|
||||
defaultMessage: 'You need at least 2 points to use double exponential smoothing',
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const smoothedPoints = _.map(points, (point, i) => {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
||||
|
@ -29,15 +30,23 @@ export default new Chainable('label', {
|
|||
{
|
||||
name: 'label',
|
||||
types: ['string'],
|
||||
help: 'Legend value for series. You can use $1, $2, etc, in the string to match up with the regex capture groups'
|
||||
help: i18n.translate('timelion.help.functions.label.args.labelHelpText', {
|
||||
defaultMessage:
|
||||
'Legend value for series. You can use $1, $2, etc, in the string to match up with the regex capture groups',
|
||||
description: '"$1" and "$2" are part of the expression and must not be translated.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'regex',
|
||||
types: ['string', 'null'],
|
||||
help: 'A regex with capture group support'
|
||||
help: i18n.translate('timelion.help.functions.label.args.regexHelpText', {
|
||||
defaultMessage: 'A regex with capture group support',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Change the label of the series. Use %s reference the existing label',
|
||||
help: i18n.translate('timelion.help.functions.labelHelpText', {
|
||||
defaultMessage: 'Change the label of the series. Use %s to reference the existing label',
|
||||
}),
|
||||
fn: function labelFn(args) {
|
||||
const config = args.byName;
|
||||
return alter(args, function (eachSeries) {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
import { DEFAULT_TIME_FORMAT } from '../../common/lib';
|
||||
|
@ -30,47 +31,87 @@ export default new Chainable('legend', {
|
|||
{
|
||||
name: 'position',
|
||||
types: ['string', 'boolean', 'null'],
|
||||
help: 'Corner to place the legend in: nw, ne, se, or sw. You can also pass false to disable the legend',
|
||||
help: i18n.translate('timelion.help.functions.legend.args.positionHelpText', {
|
||||
defaultMessage:
|
||||
'Corner to place the legend in: nw, ne, se, or sw. You can also pass false to disable the legend',
|
||||
description: '"nw", "ne", "se", "sw" and "false" are keywords and must not be translated.',
|
||||
}),
|
||||
suggestions: [
|
||||
{
|
||||
name: 'false',
|
||||
help: 'disable legend',
|
||||
help: i18n.translate(
|
||||
'timelion.help.functions.legend.args.position.suggestions.falseHelpText',
|
||||
{
|
||||
defaultMessage: 'disable legend',
|
||||
}
|
||||
),
|
||||
},
|
||||
{
|
||||
name: 'nw',
|
||||
help: 'place legend in north west corner'
|
||||
help: i18n.translate(
|
||||
'timelion.help.functions.legend.args.position.suggestions.nwHelpText',
|
||||
{
|
||||
defaultMessage: 'place legend in north west corner',
|
||||
}
|
||||
),
|
||||
},
|
||||
{
|
||||
name: 'ne',
|
||||
help: 'place legend in north east corner'
|
||||
help: i18n.translate(
|
||||
'timelion.help.functions.legend.args.position.suggestions.neHelpText',
|
||||
{
|
||||
defaultMessage: 'place legend in north east corner',
|
||||
}
|
||||
),
|
||||
},
|
||||
{
|
||||
name: 'se',
|
||||
help: 'place legend in south east corner'
|
||||
help: i18n.translate(
|
||||
'timelion.help.functions.legend.args.position.suggestions.seHelpText',
|
||||
{
|
||||
defaultMessage: 'place legend in south east corner',
|
||||
}
|
||||
),
|
||||
},
|
||||
{
|
||||
name: 'sw',
|
||||
help: 'place legend in south west corner'
|
||||
help: i18n.translate(
|
||||
'timelion.help.functions.legend.args.position.suggestions.swHelpText',
|
||||
{
|
||||
defaultMessage: 'place legend in south west corner',
|
||||
}
|
||||
),
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'columns',
|
||||
types: ['number', 'null'],
|
||||
help: 'Number of columns to divide the legend into'
|
||||
help: i18n.translate('timelion.help.functions.legend.args.columnsHelpText', {
|
||||
defaultMessage: 'Number of columns to divide the legend into',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'showTime',
|
||||
types: ['boolean'],
|
||||
help: 'Show time value in legend when hovering over graph. Default: true'
|
||||
help: i18n.translate('timelion.help.functions.legend.args.showTimeHelpText', {
|
||||
defaultMessage: 'Show time value in legend when hovering over graph. Default: true',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'timeFormat',
|
||||
types: ['string'],
|
||||
help: `moment.js format pattern. Default: ${DEFAULT_TIME_FORMAT}`
|
||||
help: i18n.translate('timelion.help.functions.legend.args.timeFormatHelpText', {
|
||||
defaultMessage: 'moment.js format pattern. Default: {defaultTimeFormat}',
|
||||
values: {
|
||||
defaultTimeFormat: DEFAULT_TIME_FORMAT,
|
||||
},
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Set the position and style of the legend on the plot',
|
||||
help: i18n.translate('timelion.help.functions.legendHelpText', {
|
||||
defaultMessage: 'Set the position and style of the legend on the plot',
|
||||
}),
|
||||
fn: function legendFn(args) {
|
||||
return alter(args, function (eachSeries, position, columns, showTime = true, timeFormat = DEFAULT_TIME_FORMAT) {
|
||||
eachSeries._global = eachSeries._global || {};
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
||||
|
@ -29,30 +30,42 @@ export default new Chainable('lines', {
|
|||
{
|
||||
name: 'width',
|
||||
types: ['number', 'null'],
|
||||
help: 'Line thickness'
|
||||
help: i18n.translate('timelion.help.functions.lines.args.widthHelpText', {
|
||||
defaultMessage: 'Line thickness',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'fill',
|
||||
types: ['number', 'null'],
|
||||
help: 'Number between 0 and 10. Use for making area charts'
|
||||
help: i18n.translate('timelion.help.functions.lines.args.fillHelpText', {
|
||||
defaultMessage: 'Number between 0 and 10. Use for making area charts',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'stack',
|
||||
types: ['boolean', 'null'],
|
||||
help: 'Stack lines, often misleading. At least use some fill if you use this.'
|
||||
help: i18n.translate('timelion.help.functions.lines.args.stackHelpText', {
|
||||
defaultMessage: 'Stack lines, often misleading. At least use some fill if you use this.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'show',
|
||||
types: ['number', 'boolean', 'null'],
|
||||
help: 'Show or hide lines'
|
||||
help: i18n.translate('timelion.help.functions.lines.args.showHelpText', {
|
||||
defaultMessage: 'Show or hide lines',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'steps',
|
||||
types: ['number', 'boolean', 'null'],
|
||||
help: 'Show line as step, e.g., do not interpolate between points'
|
||||
help: i18n.translate('timelion.help.functions.lines.args.stepsHelpText', {
|
||||
defaultMessage: 'Show line as step, e.g., do not interpolate between points',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Show the seriesList as lines',
|
||||
help: i18n.translate('timelion.help.functions.linesHelpText', {
|
||||
defaultMessage: 'Show the seriesList as lines',
|
||||
}),
|
||||
fn: function linesFn(args) {
|
||||
return alter(args, function (eachSeries, width, fill, stack, show, steps) {
|
||||
eachSeries.lines = eachSeries.lines || {};
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import _ from 'lodash';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
@ -30,11 +31,15 @@ export default new Chainable('log', {
|
|||
{
|
||||
name: 'base',
|
||||
types: ['number'],
|
||||
help: 'Set logarithmic base, 10 by default'
|
||||
|
||||
help: i18n.translate('timelion.help.functions.log.args.baseHelpText', {
|
||||
defaultMessage: 'Set logarithmic base, 10 by default',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Return the logarithm value of each value in the series list (default base: 10)',
|
||||
help: i18n.translate('timelion.help.functions.logHelpText', {
|
||||
defaultMessage:
|
||||
'Return the logarithm value of each value in the series list (default base: 10)',
|
||||
}),
|
||||
fn: function logFn(args) {
|
||||
const config = args.byName;
|
||||
return alter(args, function (eachSeries) {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import reduce from '../lib/reduce.js';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
||||
|
@ -29,13 +30,18 @@ export default new Chainable('max', {
|
|||
{
|
||||
name: 'value',
|
||||
types: ['seriesList', 'number'],
|
||||
help: 'Sets the point to whichever is higher, the existing value, or the one passed.' +
|
||||
' If passing a seriesList it must contain exactly 1 series.'
|
||||
|
||||
help: i18n.translate('timelion.help.functions.max.args.valueHelpText', {
|
||||
defaultMessage:
|
||||
'Sets the point to whichever is higher, the existing value, or the one passed. ' +
|
||||
'If passing a seriesList it must contain exactly 1 series.',
|
||||
}),
|
||||
}
|
||||
|
||||
],
|
||||
help: 'Maximum values of one or more series in a seriesList to each position, in each series, of the input seriesList',
|
||||
help: i18n.translate('timelion.help.functions.maxHelpText', {
|
||||
defaultMessage:
|
||||
'Maximum values of one or more series in a seriesList to each position, in each series, of the input seriesList',
|
||||
}),
|
||||
fn: function maxFn(args) {
|
||||
return reduce(args, function (a, b) {
|
||||
return Math.max(a, b);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import reduce from '../lib/reduce.js';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
||||
|
@ -29,13 +30,18 @@ export default new Chainable('min', {
|
|||
{
|
||||
name: 'value',
|
||||
types: ['seriesList', 'number'],
|
||||
help: 'Sets the point to whichever is lower, the existing value, or the one passed.' +
|
||||
' If passing a seriesList it must contain exactly 1 series.'
|
||||
|
||||
help: i18n.translate('timelion.help.functions.min.args.valueHelpText', {
|
||||
defaultMessage:
|
||||
'Sets the point to whichever is lower, the existing value, or the one passed. ' +
|
||||
'If passing a seriesList it must contain exactly 1 series.',
|
||||
}),
|
||||
}
|
||||
|
||||
],
|
||||
help: 'Minimum values of one or more series in a seriesList to each position, in each series, of the input seriesList',
|
||||
help: i18n.translate('timelion.help.functions.minHelpText', {
|
||||
defaultMessage:
|
||||
'Minimum values of one or more series in a seriesList to each position, in each series, of the input seriesList',
|
||||
}),
|
||||
fn: function minFn(args) {
|
||||
return reduce(args, function (a, b) {
|
||||
return Math.min(a, b);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import _ from 'lodash';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
@ -34,14 +35,23 @@ export default new Chainable('movingaverage', {
|
|||
{
|
||||
name: 'window',
|
||||
types: ['number', 'string'],
|
||||
help: 'Number of points, or a date math expression (eg 1d, 1M) to average over. ' +
|
||||
'If a date math expression is specified, the function will get as close as possible given the currently select interval' +
|
||||
'If the date math expression is not evenly divisible by the interval the results may appear abnormal.'
|
||||
help: i18n.translate('timelion.help.functions.movingaverage.args.windowHelpText', {
|
||||
defaultMessage:
|
||||
'Number of points, or a date math expression (eg 1d, 1M) to average over. If a date math expression ' +
|
||||
'is specified, the function will get as close as possible given the currently select interval. ' +
|
||||
'If the date math expression is not evenly divisible by the interval the results may appear abnormal.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'position',
|
||||
types: ['string', 'null'],
|
||||
help: `Position of the averaged points relative to the result time. One of: ${validPositions.join(', ')}`,
|
||||
help: i18n.translate('timelion.help.functions.movingaverage.args.positionHelpText', {
|
||||
defaultMessage:
|
||||
'Position of the averaged points relative to the result time. One of: {validPositions}',
|
||||
values: {
|
||||
validPositions: validPositions.join(', '),
|
||||
},
|
||||
}),
|
||||
suggestions: validPositions.map(position => {
|
||||
const suggestion = { name: position };
|
||||
if (position === defaultPosition) {
|
||||
|
@ -52,7 +62,10 @@ export default new Chainable('movingaverage', {
|
|||
}
|
||||
],
|
||||
aliases: ['mvavg'],
|
||||
help: 'Calculate the moving average over a given window. Nice for smoothing noisy series',
|
||||
help: i18n.translate('timelion.help.functions.movingaverageHelpText', {
|
||||
defaultMessage:
|
||||
'Calculate the moving average over a given window. Nice for smoothing noisy series',
|
||||
}),
|
||||
fn: function movingaverageFn(args, tlConfig) {
|
||||
return alter(args, function (eachSeries, _window, _position) {
|
||||
|
||||
|
@ -69,7 +82,16 @@ export default new Chainable('movingaverage', {
|
|||
}
|
||||
|
||||
_position = _position || defaultPosition;
|
||||
if (!_.contains(validPositions, _position)) throw new Error('Valid positions are: ' + validPositions.join(', '));
|
||||
if (!_.contains(validPositions, _position)) {
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.movingaverageFunction.notValidPositionErrorMessage', {
|
||||
defaultMessage: 'Valid positions are: {validPositions}',
|
||||
values: {
|
||||
validPositions: validPositions.join(', '),
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const pairs = eachSeries.data;
|
||||
const pairsLen = pairs.length;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import _ from 'lodash';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
@ -33,23 +34,44 @@ export default new Chainable('movingstd', {
|
|||
{
|
||||
name: 'window',
|
||||
types: ['number'],
|
||||
help: 'Number of points to compute the standard deviation over.'
|
||||
help: i18n.translate('timelion.help.functions.movingstd.args.windowHelpText', {
|
||||
defaultMessage: 'Number of points to compute the standard deviation over.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'position',
|
||||
types: ['string', 'null'],
|
||||
help: `Position of the window slice relative to the result time. Options are ${positions.join(', ')}. Default: ${defaultPosition}`
|
||||
help: i18n.translate('timelion.help.functions.movingstd.args.positionHelpText', {
|
||||
defaultMessage:
|
||||
'Position of the window slice relative to the result time. Options are {positions}. Default: {defaultPosition}',
|
||||
values: {
|
||||
positions: positions.join(', '),
|
||||
defaultPosition,
|
||||
},
|
||||
}),
|
||||
}
|
||||
],
|
||||
aliases: ['mvstd'],
|
||||
help: 'Calculate the moving standard deviation over a given window. Uses naive two-pass algorithm. Rounding errors ' +
|
||||
'may become more noticeable with very long series, or series with very large numbers.',
|
||||
help: i18n.translate('timelion.help.functions.movingstdHelpText', {
|
||||
defaultMessage:
|
||||
'Calculate the moving standard deviation over a given window. Uses naive two-pass algorithm. ' +
|
||||
'Rounding errors may become more noticeable with very long series, or series with very large numbers.',
|
||||
}),
|
||||
fn: function movingstdFn(args) {
|
||||
return alter(args, function (eachSeries, _window, _position) {
|
||||
|
||||
_position = _position || defaultPosition;
|
||||
|
||||
if (!_.contains(positions, _position)) throw new Error('Valid positions are: ' + positions.join(', '));
|
||||
if (!_.contains(positions, _position)) {
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.movingstdFunction.notValidPositionErrorMessage', {
|
||||
defaultMessage: 'Valid positions are: {validPositions}',
|
||||
values: {
|
||||
validPositions: positions.join(', '),
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const pairs = eachSeries.data;
|
||||
const pairsLen = pairs.length;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import reduce from '../lib/reduce.js';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
||||
|
@ -29,10 +30,16 @@ export default new Chainable('multiply', {
|
|||
{
|
||||
name: 'multiplier',
|
||||
types: ['seriesList', 'number'],
|
||||
help: 'Number or series by which to multiply. SeriesList with multiple series will be applied label-wise.'
|
||||
help: i18n.translate('timelion.help.functions.multiply.args.multiplierHelpText', {
|
||||
defaultMessage:
|
||||
'Number or series by which to multiply. SeriesList with multiple series will be applied label-wise.',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Multiply the values of one or more series in a seriesList to each position, in each series, of the input seriesList',
|
||||
help: i18n.translate('timelion.help.functions.multiplyHelpText', {
|
||||
defaultMessage:
|
||||
'Multiply the values of one or more series in a seriesList to each position, in each series, of the input seriesList',
|
||||
}),
|
||||
fn: function multiplyFn(args) {
|
||||
return reduce(args, function (a, b) {
|
||||
return a * b;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import _ from 'lodash';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
@ -33,26 +34,39 @@ export default new Chainable('points', {
|
|||
{
|
||||
name: 'radius',
|
||||
types: ['number', 'null'],
|
||||
help: 'Size of points'
|
||||
help: i18n.translate('timelion.help.functions.points.args.radiusHelpText', {
|
||||
defaultMessage: 'Size of points',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'weight',
|
||||
types: ['number', 'null'],
|
||||
help: 'Thickness of line around point'
|
||||
help: i18n.translate('timelion.help.functions.points.args.weightHelpText', {
|
||||
defaultMessage: 'Thickness of line around point',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'fill',
|
||||
types: ['number', 'null'],
|
||||
help: 'Number between 0 and 10 representing opacity of fill'
|
||||
help: i18n.translate('timelion.help.functions.points.args.fillHelpText', {
|
||||
defaultMessage: 'Number between 0 and 10 representing opacity of fill',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'fillColor',
|
||||
types: ['string', 'null'],
|
||||
help: 'Color with which to fill point'
|
||||
help: i18n.translate('timelion.help.functions.points.args.fillColorHelpText', {
|
||||
defaultMessage: 'Color with which to fill point',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'symbol',
|
||||
help: `point symbol. One of: ${validSymbols.join(', ')}`,
|
||||
help: i18n.translate('timelion.help.functions.points.args.symbolHelpText', {
|
||||
defaultMessage: 'point symbol. One of: {validSymbols}',
|
||||
values: {
|
||||
validSymbols: validSymbols.join(', '),
|
||||
},
|
||||
}),
|
||||
types: ['string', 'null'],
|
||||
suggestions: validSymbols.map(symbol => {
|
||||
const suggestion = { name: symbol };
|
||||
|
@ -65,10 +79,14 @@ export default new Chainable('points', {
|
|||
{
|
||||
name: 'show',
|
||||
types: ['boolean', 'null'],
|
||||
help: 'Show points or not'
|
||||
help: i18n.translate('timelion.help.functions.points.args.showHelpText', {
|
||||
defaultMessage: 'Show points or not',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Show the series as points',
|
||||
help: i18n.translate('timelion.help.functions.pointsHelpText', {
|
||||
defaultMessage: 'Show the series as points',
|
||||
}),
|
||||
fn: function pointsFn(args) {
|
||||
return alter(args, function (eachSeries, radius, weight, fill, fillColor, symbol, show) {
|
||||
eachSeries.points = eachSeries.points || {};
|
||||
|
@ -88,7 +106,14 @@ export default new Chainable('points', {
|
|||
|
||||
symbol = symbol || defaultSymbol;
|
||||
if (!_.contains(validSymbols, symbol)) {
|
||||
throw new Error('Valid symbols are: ' + validSymbols.join(', '));
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.pointsFunction.notValidSymbolErrorMessage', {
|
||||
defaultMessage: 'Valid symbols are: {validSymbols}',
|
||||
values: {
|
||||
validSymbols: validSymbols.join(', '),
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
eachSeries.points.symbol = symbol;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import reduce from '../lib/reduce.js';
|
||||
import alter from '../lib/alter.js';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
@ -30,10 +31,14 @@ export default new Chainable('precision', {
|
|||
{
|
||||
name: 'precision',
|
||||
types: ['number'],
|
||||
help: 'Number of digits to round each value to'
|
||||
help: i18n.translate('timelion.help.functions.precision.args.precisionHelpText', {
|
||||
defaultMessage: 'Number of digits to round each value to',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'number of digits to round the decimal portion of the value to',
|
||||
help: i18n.translate('timelion.help.functions.precisionHelpText', {
|
||||
defaultMessage: 'number of digits to round the decimal portion of the value to',
|
||||
}),
|
||||
fn: async function precisionFn(args) {
|
||||
await alter(args, function (eachSeries, precision) {
|
||||
eachSeries._meta = eachSeries._meta || {};
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
import _ from 'lodash';
|
||||
|
@ -49,7 +50,9 @@ export default new Chainable('props', {
|
|||
{
|
||||
name: 'global',
|
||||
types: ['boolean', 'null'],
|
||||
help: 'Set props on the seriesList vs on each series'
|
||||
help: i18n.translate('timelion.help.functions.props.args.globalHelpText', {
|
||||
defaultMessage: 'Set props on the seriesList vs on each series',
|
||||
}),
|
||||
}
|
||||
],
|
||||
extended: {
|
||||
|
@ -60,7 +63,13 @@ export default new Chainable('props', {
|
|||
},
|
||||
// extended means you can pass arguments that aren't listed. They just won't be in the ordered array
|
||||
// They will be passed as args._extended:{}
|
||||
help: 'Use at your own risk, sets arbitrary properties on the series. For example .props(label=bears!)',
|
||||
help: i18n.translate('timelion.help.functions.propsHelpText', {
|
||||
defaultMessage:
|
||||
'Use at your own risk, sets arbitrary properties on the series. For example {example}',
|
||||
values: {
|
||||
example: '.props(label=bears!)',
|
||||
},
|
||||
}),
|
||||
fn: function firstFn(args) {
|
||||
const properties = unflatten(_.omit(args.byName, 'inputSeries', 'global'));
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import _ from 'lodash';
|
||||
import fetch from 'node-fetch';
|
||||
import moment from 'moment';
|
||||
|
@ -24,28 +25,37 @@ fetch.Promise = require('bluebird');
|
|||
|
||||
//var parseDateMath = require('../utils/date_math.js');
|
||||
|
||||
|
||||
import Datasource from '../lib/classes/datasource';
|
||||
|
||||
|
||||
export default new Datasource ('quandl', {
|
||||
dataSource: true,
|
||||
args: [
|
||||
{
|
||||
name: 'code',
|
||||
types: ['string', 'null'],
|
||||
help: 'The quandl code to plot. You can find these on quandl.com.'
|
||||
help: i18n.translate('timelion.help.functions.quandl.args.codeHelpText', {
|
||||
defaultMessage: 'The quandl code to plot. You can find these on quandl.com.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'position',
|
||||
types: ['number', 'null'],
|
||||
help: 'Some quandl sources return multiple series, which one should I use? 1 based index.'
|
||||
help: i18n.translate('timelion.help.functions.quandl.args.positionHelpText', {
|
||||
defaultMessage:
|
||||
'Some quandl sources return multiple series, which one should I use? 1 based index.',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: `
|
||||
help: i18n.translate('timelion.help.functions.quandlHelpText', {
|
||||
defaultMessage:
|
||||
`
|
||||
[experimental]
|
||||
Pull data from quandl.com using the quandl code. Set "timelion:quandl.key" to your free API key in Kibana's
|
||||
Pull data from quandl.com using the quandl code. Set {quandlKeyField} to your free API key in Kibana's
|
||||
Advanced Settings. The API has a really low rate limit without a key.`,
|
||||
values: {
|
||||
quandlKeyField: '"timelion:quandl.key"',
|
||||
},
|
||||
}),
|
||||
fn: function quandlFn(args, tlConfig) {
|
||||
const intervalMap = {
|
||||
'1d': 'daily',
|
||||
|
@ -62,8 +72,15 @@ export default new Datasource ('quandl', {
|
|||
});
|
||||
|
||||
if (!config.interval) {
|
||||
throw new Error('quandl() unsupported interval: ' + tlConfig.time.interval +
|
||||
'. quandl() supports: ' + _.keys(intervalMap).join(', '));
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.quandlFunction.unsupportedIntervalErrorMessage', {
|
||||
defaultMessage: 'quandl() unsupported interval: {interval}. quandl() supports: {intervals}',
|
||||
values: {
|
||||
interval: tlConfig.time.interval,
|
||||
intervals: _.keys(intervalMap).join(', '),
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const time = {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import _ from 'lodash';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
@ -30,15 +31,21 @@ export default new Chainable('range', {
|
|||
{
|
||||
name: 'min',
|
||||
types: ['number'],
|
||||
help: 'New minimum value'
|
||||
help: i18n.translate('timelion.help.functions.range.args.minHelpText', {
|
||||
defaultMessage: 'New minimum value',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'max',
|
||||
types: ['number'],
|
||||
help: 'New maximum value'
|
||||
help: i18n.translate('timelion.help.functions.range.args.maxHelpText', {
|
||||
defaultMessage: 'New maximum value',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Changes the max and min of a series while keeping the same shape',
|
||||
help: i18n.translate('timelion.help.functions.rangeHelpText', {
|
||||
defaultMessage: 'Changes the max and min of a series while keeping the same shape',
|
||||
}),
|
||||
fn: function range(args) {
|
||||
return alter(args, function (eachSeries) {
|
||||
const values = _.map(eachSeries.data, 1);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import toMS from '../lib/to_milliseconds.js';
|
||||
import _ from 'lodash';
|
||||
|
@ -31,10 +32,16 @@ export default new Chainable('scale_interval', {
|
|||
{
|
||||
name: 'interval',
|
||||
types: ['string'],
|
||||
help: 'The new interval in date math notation, e.g., 1s for 1 second. 1m, 5m, 1M, 1w, 1y, etc.'
|
||||
help: i18n.translate('timelion.help.functions.scaleInterval.args.intervalHelpText', {
|
||||
defaultMessage:
|
||||
'The new interval in date math notation, e.g., 1s for 1 second. 1m, 5m, 1M, 1w, 1y, etc.',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Changes scales a value (usually a sum or a count) to a new interval. For example, as a per-second rate',
|
||||
help: i18n.translate('timelion.help.functions.scaleIntervalHelpText', {
|
||||
defaultMessage:
|
||||
'Changes scales a value (usually a sum or a count) to a new interval. For example, as a per-second rate',
|
||||
}),
|
||||
fn: function scaleIntervalFn(args, tlConfig) {
|
||||
const currentInterval = toMS(tlConfig.time.interval);
|
||||
const scaleInterval = toMS(args.byName.interval);
|
||||
|
|
|
@ -17,27 +17,34 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import _ from 'lodash';
|
||||
import Datasource from '../lib/classes/datasource';
|
||||
import Promise from 'bluebird';
|
||||
|
||||
|
||||
export default new Datasource ('static', {
|
||||
aliases: ['value'],
|
||||
args: [
|
||||
{
|
||||
name: 'value', // _test-data.users.*.data
|
||||
types: ['number', 'string'],
|
||||
help: 'The single value to to display, you can also pass several values and I will interpolate them evenly ' +
|
||||
'across your time range.'
|
||||
help: i18n.translate('timelion.help.functions.static.args.valueHelpText', {
|
||||
defaultMessage:
|
||||
'The single value to to display, you can also pass several values and I will interpolate them evenly across your time range.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'label',
|
||||
types: ['string', 'null'],
|
||||
help: 'A quick way to set the label for the series. You could also use the .label() function'
|
||||
help: i18n.translate('timelion.help.functions.static.args.labelHelpText', {
|
||||
defaultMessage:
|
||||
'A quick way to set the label for the series. You could also use the .label() function',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Draws a single value across the chart',
|
||||
help: i18n.translate('timelion.help.functions.staticHelpText', {
|
||||
defaultMessage: 'Draws a single value across the chart',
|
||||
}),
|
||||
fn: function staticFn(args, tlConfig) {
|
||||
|
||||
let data;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import reduce from '../lib/reduce.js';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
||||
|
@ -29,10 +30,16 @@ export default new Chainable('subtract', {
|
|||
{
|
||||
name: 'term',
|
||||
types: ['seriesList', 'number'],
|
||||
help: 'Number or series to subtract from input. SeriesList with multiple series will be applied label-wise.'
|
||||
help: i18n.translate('timelion.help.functions.subtract.args.termHelpText', {
|
||||
defaultMessage:
|
||||
'Number or series to subtract from input. SeriesList with multiple series will be applied label-wise.',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Subtract the values of one or more series in a seriesList to each position, in each series, of the input seriesList',
|
||||
help: i18n.translate('timelion.help.functions.subtractHelpText', {
|
||||
defaultMessage:
|
||||
'Subtract the values of one or more series in a seriesList to each position, in each series, of the input seriesList',
|
||||
}),
|
||||
fn: function subtractFn(args) {
|
||||
return reduce(args, function (a, b) {
|
||||
return a - b;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import reduce from '../lib/reduce.js';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
||||
|
@ -29,11 +30,16 @@ export default new Chainable('sum', {
|
|||
{
|
||||
name: 'term',
|
||||
types: ['seriesList', 'number'],
|
||||
help: 'Number or series to sum with the input series. SeriesList with multiple series will be applied label-wise.'
|
||||
|
||||
help: i18n.translate('timelion.help.functions.sum.args.termHelpText', {
|
||||
defaultMessage:
|
||||
'Number or series to sum with the input series. SeriesList with multiple series will be applied label-wise.',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Adds the values of one or more series in a seriesList to each position, in each series, of the input seriesList',
|
||||
help: i18n.translate('timelion.help.functions.sumHelpText', {
|
||||
defaultMessage:
|
||||
'Adds the values of one or more series in a seriesList to each position, in each series, of the input seriesList',
|
||||
}),
|
||||
aliases: ['add', 'plus'],
|
||||
fn: function sumFn(args) {
|
||||
return reduce(args, function (a, b) {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
||||
|
@ -29,10 +30,15 @@ export default new Chainable('title', {
|
|||
{
|
||||
name: 'title',
|
||||
types: ['string', 'null'],
|
||||
help: 'Title for the plot.'
|
||||
help: i18n.translate('timelion.help.functions.title.args.titleHelpText', {
|
||||
defaultMessage: 'Title for the plot.',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Adds a title to the top of the plot. If called on more than 1 seriesList the last call will be used.',
|
||||
help: i18n.translate('timelion.help.functions.titleHelpText', {
|
||||
defaultMessage:
|
||||
'Adds a title to the top of the plot. If called on more than 1 seriesList the last call will be used.',
|
||||
}),
|
||||
fn: function hideFn(args) {
|
||||
return alter(args, function (eachSeries, title) {
|
||||
eachSeries._title = title;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import _ from 'lodash';
|
||||
import Chainable from '../../lib/classes/chainable';
|
||||
import { linear, log } from './lib/regress';
|
||||
|
@ -35,7 +36,13 @@ export default new Chainable('trend', {
|
|||
{
|
||||
name: 'mode',
|
||||
types: ['string'],
|
||||
help: `The algorithm to use for generating the trend line. One of: ${_.keys(validRegressions).join(', ')}`,
|
||||
help: i18n.translate('timelion.help.functions.trend.args.modeHelpText', {
|
||||
defaultMessage:
|
||||
'The algorithm to use for generating the trend line. One of: {validRegressions}',
|
||||
values: {
|
||||
validRegressions: _.keys(validRegressions).join(', '),
|
||||
},
|
||||
}),
|
||||
suggestions: _.keys(validRegressions).map(key => {
|
||||
return { name: key, help: validRegressions[key] };
|
||||
})
|
||||
|
@ -43,17 +50,25 @@ export default new Chainable('trend', {
|
|||
{
|
||||
name: 'start',
|
||||
types: ['number', 'null'],
|
||||
help: 'Where to start calculating from the beginning or end. For example -10 would start calculating 10 points from' +
|
||||
' the end, +15 would start 15 points from the beginning. Default: 0',
|
||||
help: i18n.translate('timelion.help.functions.trend.args.startHelpText', {
|
||||
defaultMessage:
|
||||
'Where to start calculating from the beginning or end. For example -10 would start ' +
|
||||
'calculating 10 points from the end, +15 would start 15 points from the beginning. Default: 0',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'end',
|
||||
types: ['number', 'null'],
|
||||
help: 'Where to stop calculating from the beginning or end. For example -10 would stop calculating 10 points from' +
|
||||
' the end, +15 would stop 15 points from the beginning. Default: 0',
|
||||
help: i18n.translate('timelion.help.functions.trend.args.endHelpText', {
|
||||
defaultMessage:
|
||||
'Where to stop calculating from the beginning or end. For example -10 would stop ' +
|
||||
'calculating 10 points from the end, +15 would stop 15 points from the beginning. Default: 0',
|
||||
}),
|
||||
},
|
||||
],
|
||||
help: 'Draws a trend line using a specified regression algorithm',
|
||||
help: i18n.translate('timelion.help.functions.trendHelpText', {
|
||||
defaultMessage: 'Draws a trend line using a specified regression algorithm',
|
||||
}),
|
||||
fn: function absFn(args) {
|
||||
const newSeries = _.cloneDeep(args.byName.inputSeries);
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import alter from '../lib/alter.js';
|
||||
import _ from 'lodash';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
@ -30,15 +31,22 @@ export default new Chainable('trim', {
|
|||
{
|
||||
name: 'start',
|
||||
types: ['number', 'null'],
|
||||
help: 'Buckets to trim from the beginning of the series. Default: 1'
|
||||
help: i18n.translate('timelion.help.functions.trim.args.startHelpText', {
|
||||
defaultMessage: 'Buckets to trim from the beginning of the series. Default: 1',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'end',
|
||||
types: ['number', 'null'],
|
||||
help: 'Buckets to trim from the end of the series. Default: 1'
|
||||
help: i18n.translate('timelion.help.functions.trim.args.endHelpText', {
|
||||
defaultMessage: 'Buckets to trim from the end of the series. Default: 1',
|
||||
}),
|
||||
}
|
||||
],
|
||||
help: 'Set N buckets at the start or end of a series to null to fit the "partial bucket issue"',
|
||||
help: i18n.translate('timelion.help.functions.trimHelpText', {
|
||||
defaultMessage:
|
||||
'Set N buckets at the start or end of a series to null to fit the "partial bucket issue"',
|
||||
}),
|
||||
fn: function conditionFn(args) {
|
||||
const config = args.byName;
|
||||
if (config.start == null) config.start = 1;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import _ from 'lodash';
|
||||
import fetch from 'node-fetch';
|
||||
import moment from 'moment';
|
||||
|
@ -27,17 +28,28 @@ export default new Datasource ('worldbank', {
|
|||
{
|
||||
name: 'code', // countries/all/indicators/SP.POP.TOTL
|
||||
types: ['string', 'null'],
|
||||
help: 'Worldbank API path.' +
|
||||
' This is usually everything after the domain, before the querystring. E.g.: ' +
|
||||
'/en/countries/ind;chn/indicators/DPANUSSPF.'
|
||||
help: i18n.translate('timelion.help.functions.worldbank.args.codeHelpText', {
|
||||
defaultMessage:
|
||||
'Worldbank API path. This is usually everything after the domain, before the querystring. E.g.: {apiPathExample}.',
|
||||
values: {
|
||||
apiPathExample: '/en/countries/ind;chn/indicators/DPANUSSPF',
|
||||
},
|
||||
}),
|
||||
}
|
||||
],
|
||||
aliases: ['wb'],
|
||||
help: `
|
||||
help: i18n.translate('timelion.help.functions.worldbankHelpText', {
|
||||
defaultMessage:
|
||||
`
|
||||
[experimental]
|
||||
Pull data from http://data.worldbank.org/ using path to series.
|
||||
Pull data from {worldbankUrl} using path to series.
|
||||
The worldbank provides mostly yearly data, and often has no data for the current year.
|
||||
Try offset=-1y if you get no data for recent time ranges.`,
|
||||
Try {offsetQuery} if you get no data for recent time ranges.`,
|
||||
values: {
|
||||
worldbankUrl: 'http://data.worldbank.org/',
|
||||
offsetQuery: 'offset=-1y',
|
||||
},
|
||||
}),
|
||||
fn: function worldbank(args, tlConfig) {
|
||||
// http://api.worldbank.org/en/countries/ind;chn/indicators/DPANUSSPF?date=2000:2006&MRV=5
|
||||
|
||||
|
@ -74,7 +86,16 @@ export default new Datasource ('worldbank', {
|
|||
return [moment(date, 'YYYY').valueOf(), Number(val)];
|
||||
}));
|
||||
|
||||
if (!hasData) throw new Error('Worldbank request succeeded, but there was no data for ' + config.code);
|
||||
if (!hasData) {
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.worldbankFunction.noDataErrorMessage', {
|
||||
defaultMessage: 'Worldbank request succeeded, but there was no data for {code}',
|
||||
values: {
|
||||
code: config.code,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'seriesList',
|
||||
|
|
|
@ -17,32 +17,48 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import _ from 'lodash';
|
||||
import worldbank from './worldbank.js';
|
||||
import Promise from 'bluebird';
|
||||
import Datasource from '../lib/classes/datasource';
|
||||
|
||||
|
||||
export default new Datasource ('worldbank_indicators', {
|
||||
args: [
|
||||
{
|
||||
name: 'country', // countries/all/indicators/SP.POP.TOTL
|
||||
types: ['string', 'null'],
|
||||
help: 'Worldbank country identifier. Usually the country\'s 2 letter code'
|
||||
help: i18n.translate('timelion.help.functions.worldbankIndicators.args.countryHelpText', {
|
||||
defaultMessage: `Worldbank country identifier. Usually the country's 2 letter code`,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'indicator',
|
||||
types: ['string', 'null'],
|
||||
help: 'The indicator code to use. You\'ll have to look this up on data.worldbank.org.' +
|
||||
' Often pretty obtuse. E.g., SP.POP.TOTL is population'
|
||||
help: i18n.translate('timelion.help.functions.worldbankIndicators.args.indicatorHelpText', {
|
||||
defaultMessage:
|
||||
`The indicator code to use. You'll have to look this up on {worldbankUrl}. ` +
|
||||
'Often pretty obtuse. E.g., {indicatorExample} is population',
|
||||
values: {
|
||||
worldbankUrl: 'data.worldbank.org',
|
||||
indicatorExample: 'SP.POP.TOTL',
|
||||
},
|
||||
}),
|
||||
}
|
||||
],
|
||||
aliases: ['wbi'],
|
||||
help: `
|
||||
help: i18n.translate('timelion.help.functions.worldbankIndicatorsHelpText', {
|
||||
defaultMessage:
|
||||
`
|
||||
[experimental]
|
||||
Pull data from http://data.worldbank.org/ using the country name and indicator. The worldbank provides
|
||||
mostly yearly data, and often has no data for the current year. Try offset=-1y if you get no data for recent
|
||||
Pull data from {worldbankUrl} using the country name and indicator. The worldbank provides
|
||||
mostly yearly data, and often has no data for the current year. Try {offsetQuery} if you get no data for recent
|
||||
time ranges.`,
|
||||
values: {
|
||||
worldbankUrl: 'http://data.worldbank.org/',
|
||||
offsetQuery: 'offset=-1y',
|
||||
},
|
||||
}),
|
||||
fn: function worldbankIndicators(args, tlConfig) {
|
||||
const config = _.defaults(args.byName, {
|
||||
country: 'wld',
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import _ from 'lodash';
|
||||
import alter from '../lib/alter.js';
|
||||
import Chainable from '../lib/classes/chainable';
|
||||
|
@ -39,37 +40,55 @@ export default new Chainable('yaxis', {
|
|||
{
|
||||
name: 'yaxis',
|
||||
types: ['number', 'null'],
|
||||
help: 'The numbered y-axis to plot this series on, e.g., .yaxis(2) for a 2nd y-axis.'
|
||||
help: i18n.translate('timelion.help.functions.yaxis.args.yaxisHelpText', {
|
||||
defaultMessage:
|
||||
'The numbered y-axis to plot this series on, e.g., .yaxis(2) for a 2nd y-axis.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'min',
|
||||
types: ['number', 'null'],
|
||||
help: 'Min value'
|
||||
help: i18n.translate('timelion.help.functions.yaxis.args.minHelpText', {
|
||||
defaultMessage: 'Min value',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'max',
|
||||
types: ['number', 'null'],
|
||||
help: 'Max value'
|
||||
help: i18n.translate('timelion.help.functions.yaxis.args.maxHelpText', {
|
||||
defaultMessage: 'Max value',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'position',
|
||||
types: ['string', 'null'],
|
||||
help: 'left or right'
|
||||
help: i18n.translate('timelion.help.functions.yaxis.args.positionHelpText', {
|
||||
defaultMessage: 'left or right',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'label',
|
||||
types: ['string', 'null'],
|
||||
help: 'Label for axis'
|
||||
help: i18n.translate('timelion.help.functions.yaxis.args.labelHelpText', {
|
||||
defaultMessage: 'Label for axis',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'color',
|
||||
types: ['string', 'null'],
|
||||
help: 'Color of axis label'
|
||||
help: i18n.translate('timelion.help.functions.yaxis.args.colorHelpText', {
|
||||
defaultMessage: 'Color of axis label',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'units',
|
||||
types: ['string', 'null'],
|
||||
help: `The function to use for formatting y-axis labels. One of: ${_.values(tickFormatters).join(', ')}`,
|
||||
help: i18n.translate('timelion.help.functions.yaxis.args.unitsHelpText', {
|
||||
defaultMessage: 'The function to use for formatting y-axis labels. One of: {formatters}',
|
||||
values: {
|
||||
formatters: _.values(tickFormatters).join(', '),
|
||||
},
|
||||
}),
|
||||
suggestions: _.keys(tickFormatters).map(key => {
|
||||
return { name: key, help: tickFormatters[key] };
|
||||
})
|
||||
|
@ -77,10 +96,15 @@ export default new Chainable('yaxis', {
|
|||
{
|
||||
name: 'tickDecimals',
|
||||
types: ['number', 'null'],
|
||||
help: 'tick decimal precision'
|
||||
help: i18n.translate('timelion.help.functions.yaxis.args.tickDecimalsHelpText', {
|
||||
defaultMessage: 'tick decimal precision',
|
||||
}),
|
||||
},
|
||||
],
|
||||
help: 'Configures a variety of y-axis options, the most important likely being the ability to add an Nth (eg 2nd) y-axis',
|
||||
help: i18n.translate('timelion.help.functions.yaxisHelpText', {
|
||||
defaultMessage:
|
||||
'Configures a variety of y-axis options, the most important likely being the ability to add an Nth (eg 2nd) y-axis',
|
||||
}),
|
||||
fn: function yaxisFn(args) {
|
||||
return alter(args, function (eachSeries, yaxis, min, max, position, label, color, units, tickDecimals) {
|
||||
yaxis = yaxis || 1;
|
||||
|
@ -108,13 +132,24 @@ export default new Chainable('yaxis', {
|
|||
const unitTokens = units.split(':');
|
||||
const unitType = unitTokens[0];
|
||||
if (!tickFormatters[unitType]) {
|
||||
throw new Error (`${units} is not a supported unit type.`);
|
||||
throw new Error (
|
||||
i18n.translate(
|
||||
'timelion.serverSideErrors.yaxisFunction.notSupportedUnitTypeErrorMessage',
|
||||
{
|
||||
defaultMessage: '{units} is not a supported unit type.',
|
||||
values: { units },
|
||||
})
|
||||
);
|
||||
}
|
||||
if (unitType === 'currency') {
|
||||
const threeLetterCode = /^[A-Za-z]{3}$/;
|
||||
const currency = unitTokens[1];
|
||||
if (currency && !threeLetterCode.test(currency)) {
|
||||
throw new Error('Currency must be a three letter code');
|
||||
throw new Error(
|
||||
i18n.translate('timelion.serverSideErrors.yaxisFunction.notValidCurrencyFormatErrorMessage', {
|
||||
defaultMessage: 'Currency must be a three letter code',
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue