turnilo
turnilo copied to clipboard
I have a few questions.
version:1.26.0
- In the
table
view, select a time range of more than one week, and click on theW
in thetime shift
, and an error will be reported. The message is as follows:Shifted period overlaps with main period
. Why is it designed like this? - How to set the start time of each week to
Sunday
?
In the
table
view, select a time range of more than one week, and click on theW
in thetime shift
, and an error will be reported. The message is as follows:Shifted period overlaps with main period
. Why is it designed like this?
Because Turnilo can't calculate difference between periods if periods overlap.
How to set the start time of each week to
Sunday
?
There's no "week start" in Turnilo. All calculation on time buckets are done from selected date.
@adrianmroz-allegro The second question, when you select a period of time and split by week, the start time of the week is Monday by default
@adrianmroz-allegro The second question, when you select a period of time and split by week, the start time of the week is Monday by default
You're right! Sadly, plywood do not support origin
parameter and we can't change it. Issue in plywood for reference: https://github.com/implydata/plywood/issues/172
We'd like to support this but even simpler contributions to plywood are stuck in limbo :(
@adrianmroz-allegro We modify the value of origin
by setting the requestDecorator
attribute value in the config.yaml
file, and finally modify the start date of the week.
exports.version = 1;
/**
* object: 对象
* path: 输入的路径
* defaultVal: 默认值
**/
function get(object, path, defaultVal = 'undefined') {
// 先将path处理成统一格式
var newPath = [];
if (Array.isArray(path)) {
newPath = path;
} else {
// 先将字符串中的'['、']'去除替换为'.',split分割成数组形式
newPath = path.replace(/\[/g, '.').replace(/\]/g, '').split('.');
}
// 递归处理,返回最后结果
return (
newPath.reduce((o, k) => {
return (o || {})[k];
}, object) || defaultVal
);
}
/**
* https://github.com/metabase/metabase/issues/6047
* 周的时间是从周日到周六
* @param query
* @returns {*}
*/
function addGranularityOrigin(query) {
var externalGranularity = get(query, 'granularity');
if (typeof externalGranularity === 'object' && externalGranularity.period === 'P1W') {
query.granularity.origin = '1970-01-04T00Z';
return query;
}
var internalGranularity = get(query, 'dimension.extractionFn.granularity');
if (typeof internalGranularity === 'object' && internalGranularity.period === 'P1W') {
query.dimension.extractionFn.granularity.origin = '1970-01-04T00Z';
return query;
}
var dimensions = get(query, 'dimensions');
if (Array.isArray(dimensions)) {
dimensions = dimensions.map(function(dimension){
var dimensionGranularity = get(dimension, 'extractionFn.granularity');
if(typeof dimensionGranularity === 'object' && dimensionGranularity.period === 'P1W') {
var newDimension = {...dimension}
newDimension.extractionFn.granularity.origin = '1970-01-04T00Z';
return newDimension
}
return dimension
})
query.dimensions = dimensions
return query;
}
return query;
}
// logger - is just a collection of functions that you should use instead of console to have your logs included with the Swiv logs
// params - is an object with the following keys:
// * options: the decoratorOptions part of the cluster object
// * cluster: Cluster - the cluster object
exports.druidRequestDecoratorFactory = function (logger, params) {
// decoratorRequest: DecoratorRequest - is an object that has the following keys:
// * method: string - the method that is used (POST or GET)
// * url: string -
// * query: Druid.Query -
return function (decoratorRequest) {
var query = decoratorRequest.query;
query = addGranularityOrigin(query);
// 可以二次修改传递给druid的参数
var decoration = {
headers: {},
query: query,
};
// This can also be async if instead of a value of a promise is returned.
return decoration;
};
};
Hey, interesting take on this. I don't think that we would add something like that to turnilo, though.
Maybe we should add some documentation/wiki how to add such decorator for interested users?
@adrianmroz-allegro How to add custom header information when sending a request to Druid?
-
I added a custom attribute userAgent
......
requestOptionsWithDecoration({
query,
context,
options: {
method: "POST",
url: url + "/druid/v2/" + (queryType === 'sql' ? 'sql/' : '') + (context['pretty'] ? '?pretty': ''),
body: JSON.stringify(query),
headers: {
"Content-type": "application/json",
"User-Agent": JSON.stringify(userAgent)
},
timeout: timeout
}
})
.......
-
src/server/utils/requester/requester.ts
...
function createDruidRequester(cluster: Cluster, requestDecorator: DruidRequestDecorator, userAgent: CustomUserAgent): PlywoodRequester<any> {
const { host, protocol } = getHostAndProtocol(new URL(cluster.url));
const timeout = cluster.getTimeout();
return druidRequesterFactory({ host, timeout, requestDecorator, protocol, userAgent });
}
...
-
src/server/config.ts
...
export const SETTINGS_MANAGER = new SettingsManager(settingsStore, {
logger,
userAgent: { uid: null, hash: null },
verbose: VERBOSE,
anchorPath,
initialLoadTimeout: SERVER_SETTINGS.getPageMustLoadTimeout(),
});
...
-
src/server/app.ts
...
attachRouter('/plywood', plywoodRouter(settingsGetter, SETTINGS_MANAGER));
...
-
src/server/routes/plywood/plywood.ts
let ex: Expression = null;
try {
ex = Expression.fromJS(expression);
} catch (e) {
res.status(400).send({
error: 'bad expression',
message: e.message,
});
return;
}
let settings;
try {
settingManager.setUserAgents({ uid: 'UIDUID', hash: '' });
settings = await getSettings();
} catch (e) {
res.status(400).send({ error: 'failed to get settings' });
return;
}
const myDataCube = settings.getDataCube(dataCube);
if (!myDataCube) {
res.status(400).send({ error: 'unknown data cube' });
return;
}
if (!myDataCube.executor) {
res.status(400).send({ error: 'un queryable data cube' });
return;
}
if (!checkAccess(myDataCube, req.headers)) {
res.status(403).send({ error: 'access denied' });
return null;
}
// "native" clusters are not defined, maybe they should be defined as some stub object
if (myDataCube.cluster) {
req.setTimeout(myDataCube.cluster.getTimeout(), null);
}
const maxQueries = myDataCube.getMaxQueries();
try {
const data = await myDataCube.executor(ex, { maxQueries, timezone: queryTimezone });
const reply: any = {
result: Dataset.isDataset(data) ? data.toJS() : data,
};
res.json(reply);
} catch (error) {
console.log('error:', error.message);
if (error.hasOwnProperty('stack')) {
console.log(error.stack);
}
res.status(500).send({
error: 'could not compute',
message: error.message,
});
}
Druid request decorator takes only these props: https://github.com/implydata/plywood-druid-requester/blob/master/src/druidRequester.ts#L59...L63 It does not have access to any dynamic values which are under turnip control besides query. I don't think it's Turnilo responsibility to add something like that.
@adrianmroz How to ignore case
, when typing in the search
box?
You can't.
I think Discussions would be better suited for such questions. Keep issues for bug reports and feature requests.