mirror of https://github.com/apache/superset.git
Improve the calendar heatmap (#4800)
* Improve xAxis ticks, thinner bottom margin (#4756) * Improve xAxis ticks, thinner bottom margin * Moving utils folder * Add isTruthy * Addressing comments * Set cell_padding to 0 * merging db migrations
This commit is contained in:
parent
b04359003e
commit
6fd4ff45ea
|
@ -153,6 +153,10 @@ class Chart extends React.PureComponent {
|
||||||
this.props.actions.chartRenderingFailed(e, this.props.chartKey);
|
this.props.actions.chartRenderingFailed(e, this.props.chartKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
verboseMetricName(metric) {
|
||||||
|
return this.props.datasource.verbose_map[metric] || metric;
|
||||||
|
}
|
||||||
|
|
||||||
render_template(s) {
|
render_template(s) {
|
||||||
const context = {
|
const context = {
|
||||||
width: this.width(),
|
width: this.width(),
|
||||||
|
|
|
@ -230,6 +230,7 @@ export const controls = {
|
||||||
default: colorPrimary,
|
default: colorPrimary,
|
||||||
renderTrigger: true,
|
renderTrigger: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
legend_position: {
|
legend_position: {
|
||||||
label: t('Legend Position'),
|
label: t('Legend Position'),
|
||||||
description: t('Choose the position of the legend'),
|
description: t('Choose the position of the legend'),
|
||||||
|
@ -324,11 +325,17 @@ export const controls = {
|
||||||
label: t('Linear Color Scheme'),
|
label: t('Linear Color Scheme'),
|
||||||
choices: [
|
choices: [
|
||||||
['fire', 'fire'],
|
['fire', 'fire'],
|
||||||
['blue_white_yellow', 'blue/white/yellow'],
|
|
||||||
['white_black', 'white/black'],
|
['white_black', 'white/black'],
|
||||||
['black_white', 'black/white'],
|
['black_white', 'black/white'],
|
||||||
['dark_blue', 'light/dark blue'],
|
['dark_blue', 'light/dark blue'],
|
||||||
['pink_grey', 'pink/white/grey'],
|
['pink_grey', 'pink/white/grey'],
|
||||||
|
['greens', 'greens'],
|
||||||
|
['purples', 'purples'],
|
||||||
|
['oranges', 'oranges'],
|
||||||
|
['blue_white_yellow', 'blue/white/yellow'],
|
||||||
|
['red_yellow_blue', 'red/yellowish/blue'],
|
||||||
|
['brown_white_green', 'brown/white/green'],
|
||||||
|
['purple_white_green', 'purple/white/green'],
|
||||||
],
|
],
|
||||||
default: 'blue_white_yellow',
|
default: 'blue_white_yellow',
|
||||||
clearable: false,
|
clearable: false,
|
||||||
|
@ -1006,6 +1013,46 @@ export const controls = {
|
||||||
'relative to the time granularity selected'),
|
'relative to the time granularity selected'),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
cell_size: {
|
||||||
|
type: 'TextControl',
|
||||||
|
isInt: true,
|
||||||
|
default: 10,
|
||||||
|
validators: [v.integer],
|
||||||
|
renderTrigger: true,
|
||||||
|
label: t('Cell Size'),
|
||||||
|
description: t('The size of the square cell, in pixels'),
|
||||||
|
},
|
||||||
|
|
||||||
|
cell_padding: {
|
||||||
|
type: 'TextControl',
|
||||||
|
isInt: true,
|
||||||
|
validators: [v.integer],
|
||||||
|
renderTrigger: true,
|
||||||
|
default: 0,
|
||||||
|
label: t('Cell Padding'),
|
||||||
|
description: t('The distance between cells, in pixels'),
|
||||||
|
},
|
||||||
|
|
||||||
|
cell_radius: {
|
||||||
|
type: 'TextControl',
|
||||||
|
isInt: true,
|
||||||
|
validators: [v.integer],
|
||||||
|
renderTrigger: true,
|
||||||
|
default: 0,
|
||||||
|
label: t('Cell Radius'),
|
||||||
|
description: t('The pixel radius'),
|
||||||
|
},
|
||||||
|
|
||||||
|
steps: {
|
||||||
|
type: 'TextControl',
|
||||||
|
isInt: true,
|
||||||
|
validators: [v.integer],
|
||||||
|
renderTrigger: true,
|
||||||
|
default: 10,
|
||||||
|
label: t('Color Steps'),
|
||||||
|
description: t('The number color "steps"'),
|
||||||
|
},
|
||||||
|
|
||||||
grid_size: {
|
grid_size: {
|
||||||
type: 'TextControl',
|
type: 'TextControl',
|
||||||
label: t('Grid Size'),
|
label: t('Grid Size'),
|
||||||
|
@ -1462,6 +1509,14 @@ export const controls = {
|
||||||
description: t('Whether to display the numerical values within the cells'),
|
description: t('Whether to display the numerical values within the cells'),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
show_metric_name: {
|
||||||
|
type: 'CheckboxControl',
|
||||||
|
label: t('Show Metric Names'),
|
||||||
|
renderTrigger: true,
|
||||||
|
default: true,
|
||||||
|
description: t('Whether to display the metric name as a title'),
|
||||||
|
},
|
||||||
|
|
||||||
x_axis_showminmax: {
|
x_axis_showminmax: {
|
||||||
type: 'CheckboxControl',
|
type: 'CheckboxControl',
|
||||||
label: t('X bounds'),
|
label: t('X bounds'),
|
||||||
|
|
|
@ -977,17 +977,34 @@ export const visTypes = {
|
||||||
label: t('Query'),
|
label: t('Query'),
|
||||||
expanded: true,
|
expanded: true,
|
||||||
controlSetRows: [
|
controlSetRows: [
|
||||||
['metric'],
|
['domain_granularity', 'subdomain_granularity'],
|
||||||
|
['metrics'],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('Options'),
|
label: t('Chart Options'),
|
||||||
|
expanded: true,
|
||||||
controlSetRows: [
|
controlSetRows: [
|
||||||
['domain_granularity'],
|
['linear_color_scheme'],
|
||||||
['subdomain_granularity'],
|
['cell_size', 'cell_padding'],
|
||||||
|
['cell_radius', 'steps'],
|
||||||
|
['y_axis_format', 'x_axis_time_format'],
|
||||||
|
['show_legend', 'show_values'],
|
||||||
|
['show_metric_name', null],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
controlOverrides: {
|
||||||
|
y_axis_format: {
|
||||||
|
label: t('Number Format'),
|
||||||
|
},
|
||||||
|
x_axis_time_format: {
|
||||||
|
label: t('Time Format'),
|
||||||
|
},
|
||||||
|
show_values: {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
box_plot: {
|
box_plot: {
|
||||||
|
|
|
@ -122,6 +122,42 @@ export const spectrums = {
|
||||||
'#FAFAFA',
|
'#FAFAFA',
|
||||||
'#666666',
|
'#666666',
|
||||||
],
|
],
|
||||||
|
greens: [
|
||||||
|
'#ffffcc',
|
||||||
|
'#78c679',
|
||||||
|
'#006837',
|
||||||
|
],
|
||||||
|
purples: [
|
||||||
|
'#f2f0f7',
|
||||||
|
'#9e9ac8',
|
||||||
|
'#54278f',
|
||||||
|
],
|
||||||
|
oranges: [
|
||||||
|
'#fef0d9',
|
||||||
|
'#fc8d59',
|
||||||
|
'#b30000',
|
||||||
|
],
|
||||||
|
red_yellow_blue: [
|
||||||
|
'#d7191c',
|
||||||
|
'#fdae61',
|
||||||
|
'#ffffbf',
|
||||||
|
'#abd9e9',
|
||||||
|
'#2c7bb6',
|
||||||
|
],
|
||||||
|
brown_white_green: [
|
||||||
|
'#a6611a',
|
||||||
|
'#dfc27d',
|
||||||
|
'#f5f5f5',
|
||||||
|
'#80cdc1',
|
||||||
|
'#018571',
|
||||||
|
],
|
||||||
|
purple_white_green: [
|
||||||
|
'#7b3294',
|
||||||
|
'#c2a5cf',
|
||||||
|
'#f7f7f7',
|
||||||
|
'#a6dba0',
|
||||||
|
'#008837',
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -48,7 +48,6 @@
|
||||||
"bootstrap-slider": "^10.0.0",
|
"bootstrap-slider": "^10.0.0",
|
||||||
"brace": "^0.10.0",
|
"brace": "^0.10.0",
|
||||||
"brfs": "^1.4.3",
|
"brfs": "^1.4.3",
|
||||||
"cal-heatmap": "3.6.2",
|
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
"d3": "^3.5.17",
|
"d3": "^3.5.17",
|
||||||
"d3-cloud": "^1.2.1",
|
"d3-cloud": "^1.2.1",
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
/* Cal-HeatMap CSS */
|
||||||
|
|
||||||
|
.cal-heatmap-container {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-heatmap-container .graph
|
||||||
|
{
|
||||||
|
font-family: "Lucida Grande", Lucida, Verdana, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-heatmap-container .graph-label
|
||||||
|
{
|
||||||
|
fill: #999;
|
||||||
|
font-size: 10px
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-heatmap-container .graph, .cal-heatmap-container .graph-legend rect {
|
||||||
|
shape-rendering: crispedges
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-heatmap-container .graph-rect
|
||||||
|
{
|
||||||
|
fill: #ededed
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-heatmap-container .graph-subdomain-group rect:hover
|
||||||
|
{
|
||||||
|
stroke: #000;
|
||||||
|
stroke-width: 1px
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-heatmap-container .subdomain-text {
|
||||||
|
font-size: 8px;
|
||||||
|
fill: #999;
|
||||||
|
pointer-events: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-heatmap-container .hover_cursor:hover {
|
||||||
|
cursor: pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-heatmap-container .qi {
|
||||||
|
background-color: #999;
|
||||||
|
fill: #999
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Remove comment to apply this style to date with value equal to 0
|
||||||
|
.q0
|
||||||
|
{
|
||||||
|
background-color: #fff;
|
||||||
|
fill: #fff;
|
||||||
|
stroke: #ededed
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
.cal-heatmap-container .q1
|
||||||
|
{
|
||||||
|
background-color: #dae289;
|
||||||
|
fill: #dae289
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-heatmap-container .q2
|
||||||
|
{
|
||||||
|
background-color: #cedb9c;
|
||||||
|
fill: #9cc069
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-heatmap-container .q3
|
||||||
|
{
|
||||||
|
background-color: #b5cf6b;
|
||||||
|
fill: #669d45
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-heatmap-container .q4
|
||||||
|
{
|
||||||
|
background-color: #637939;
|
||||||
|
fill: #637939
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-heatmap-container .q5
|
||||||
|
{
|
||||||
|
background-color: #3b6427;
|
||||||
|
fill: #3b6427
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-heatmap-container rect.highlight
|
||||||
|
{
|
||||||
|
stroke:#444;
|
||||||
|
stroke-width:1
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-heatmap-container text.highlight
|
||||||
|
{
|
||||||
|
fill: #444
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-heatmap-container rect.highlight-now
|
||||||
|
{
|
||||||
|
stroke: red
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-heatmap-container text.highlight-now
|
||||||
|
{
|
||||||
|
fill: red;
|
||||||
|
font-weight: 800
|
||||||
|
}
|
||||||
|
|
||||||
|
.cal-heatmap-container .domain-background {
|
||||||
|
fill: none;
|
||||||
|
shape-rendering: crispedges
|
||||||
|
}
|
||||||
|
|
||||||
|
.ch-tooltip {
|
||||||
|
padding: 10px;
|
||||||
|
background: #222;
|
||||||
|
color: #bbb;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.4;
|
||||||
|
width: 140px;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 99999;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 2px;
|
||||||
|
box-shadow: 2px 2px 2px rgba(0,0,0,0.2);
|
||||||
|
display: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ch-tooltip::after{
|
||||||
|
position: absolute;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-color: transparent;
|
||||||
|
border-style: solid;
|
||||||
|
content: "";
|
||||||
|
padding: 0;
|
||||||
|
display: block;
|
||||||
|
bottom: -6px;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -6px;
|
||||||
|
border-width: 6px 6px 0;
|
||||||
|
border-top-color: #222;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,9 +1,14 @@
|
||||||
.cal_heatmap .slice_container {
|
.slice_container.cal_heatmap {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
position: static !important;
|
position: static !important;
|
||||||
|
overflow: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cal_heatmap .slice_container .ch-tooltip {
|
.cal_heatmap .slice_container .ch-tooltip {
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
.graph-legend rect {
|
||||||
|
stroke: #aaa;
|
||||||
|
stroke-location: inside;
|
||||||
|
}
|
||||||
|
|
|
@ -1,38 +1,83 @@
|
||||||
// JS
|
|
||||||
import d3 from 'd3';
|
import d3 from 'd3';
|
||||||
|
|
||||||
// CSS
|
import { colorScalerFactory } from '../javascripts/modules/colors';
|
||||||
require('./cal_heatmap.css');
|
import CalHeatMap from '../vendor/cal-heatmap/cal-heatmap';
|
||||||
require('../node_modules/cal-heatmap/cal-heatmap.css');
|
import '../vendor/cal-heatmap/cal-heatmap.css';
|
||||||
|
import { d3TimeFormatPreset, d3FormatPreset } from '../javascripts/modules/utils';
|
||||||
|
import './cal_heatmap.css';
|
||||||
|
import { UTC } from '../javascripts/modules/dates';
|
||||||
|
|
||||||
const CalHeatMap = require('cal-heatmap');
|
const UTCTS = uts => UTC(new Date(uts)).getTime();
|
||||||
|
|
||||||
function calHeatmap(slice, payload) {
|
function calHeatmap(slice, payload) {
|
||||||
const div = d3.select(slice.selector);
|
const fd = slice.formData;
|
||||||
|
const steps = fd.steps;
|
||||||
|
const valueFormatter = d3FormatPreset(fd.y_axis_format);
|
||||||
|
const timeFormatter = d3TimeFormatPreset(fd.x_axis_time_format);
|
||||||
|
|
||||||
|
const container = d3.select(slice.selector).style('height', slice.height());
|
||||||
|
container.selectAll('*').remove();
|
||||||
|
const div = container.append('div');
|
||||||
const data = payload.data;
|
const data = payload.data;
|
||||||
|
|
||||||
div.selectAll('*').remove();
|
const subDomainTextFormat = fd.show_values ? (date, value) => valueFormatter(value) : null;
|
||||||
|
const cellPadding = fd.cell_padding !== '' ? fd.cell_padding : 2;
|
||||||
|
const cellRadius = fd.cell_radius || 0;
|
||||||
|
const cellSize = fd.cell_size || 10;
|
||||||
|
|
||||||
|
// Trick to convert all timestamps to UTC
|
||||||
|
const metricsData = {};
|
||||||
|
Object.keys(data.data).forEach((metric) => {
|
||||||
|
metricsData[metric] = {};
|
||||||
|
Object.keys(data.data[metric]).forEach((ts) => {
|
||||||
|
metricsData[metric][UTCTS(ts * 1000) / 1000] = data.data[metric][ts];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(metricsData).forEach((metric) => {
|
||||||
|
const calContainer = div.append('div');
|
||||||
|
if (fd.show_metric_name) {
|
||||||
|
calContainer.append('h4').text(slice.verboseMetricName(metric));
|
||||||
|
}
|
||||||
|
const timestamps = metricsData[metric];
|
||||||
|
const extents = d3.extent(Object.keys(timestamps), key => timestamps[key]);
|
||||||
|
const step = (extents[1] - extents[0]) / (steps - 1);
|
||||||
|
const colorScale = colorScalerFactory(fd.linear_color_scheme, null, null, extents);
|
||||||
|
|
||||||
|
const legend = d3.range(steps).map(i => extents[0] + (step * i));
|
||||||
|
const legendColors = legend.map(colorScale);
|
||||||
|
|
||||||
const cal = new CalHeatMap();
|
const cal = new CalHeatMap();
|
||||||
|
|
||||||
const timestamps = data.timestamps;
|
|
||||||
const extents = d3.extent(Object.keys(timestamps), key => timestamps[key]);
|
|
||||||
const step = (extents[1] - extents[0]) / 5;
|
|
||||||
|
|
||||||
try {
|
|
||||||
cal.init({
|
cal.init({
|
||||||
start: data.start,
|
start: UTCTS(data.start),
|
||||||
data: timestamps,
|
data: timestamps,
|
||||||
itemSelector: slice.selector,
|
itemSelector: calContainer[0][0],
|
||||||
|
legendVerticalPosition: 'top',
|
||||||
|
cellSize,
|
||||||
|
cellPadding,
|
||||||
|
cellRadius,
|
||||||
|
legendCellSize: cellSize,
|
||||||
|
legendCellPadding: 2,
|
||||||
|
legendCellRadius: cellRadius,
|
||||||
tooltip: true,
|
tooltip: true,
|
||||||
domain: data.domain,
|
domain: data.domain,
|
||||||
subDomain: data.subdomain,
|
subDomain: data.subdomain,
|
||||||
range: data.range,
|
range: data.range,
|
||||||
browsing: true,
|
browsing: true,
|
||||||
legend: [extents[0], extents[0] + step, extents[0] + (step * 2), extents[0] + (step * 3)],
|
legend,
|
||||||
|
legendColors: {
|
||||||
|
colorScale,
|
||||||
|
min: legendColors[0],
|
||||||
|
max: legendColors[legendColors.length - 1],
|
||||||
|
empty: 'white',
|
||||||
|
},
|
||||||
|
displayLegend: fd.show_legend,
|
||||||
|
itemName: '',
|
||||||
|
valueFormatter,
|
||||||
|
timeFormatter,
|
||||||
|
subDomainTextFormat,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
} catch (e) {
|
|
||||||
slice.error(e);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = calHeatmap;
|
module.exports = calHeatmap;
|
||||||
|
|
|
@ -1761,12 +1761,6 @@ cacache@^10.0.1:
|
||||||
unique-filename "^1.1.0"
|
unique-filename "^1.1.0"
|
||||||
y18n "^3.2.1"
|
y18n "^3.2.1"
|
||||||
|
|
||||||
cal-heatmap@3.6.2:
|
|
||||||
version "3.6.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/cal-heatmap/-/cal-heatmap-3.6.2.tgz#961a7f4686b3bdcf7104d951b6ff1dd58c0c62d1"
|
|
||||||
dependencies:
|
|
||||||
d3 "^3.0.6"
|
|
||||||
|
|
||||||
call-matcher@^1.0.1:
|
call-matcher@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/call-matcher/-/call-matcher-1.0.1.tgz#5134d077984f712a54dad3cbf62de28dce416ca8"
|
resolved "https://registry.yarnpkg.com/call-matcher/-/call-matcher-1.0.1.tgz#5134d077984f712a54dad3cbf62de28dce416ca8"
|
||||||
|
@ -2652,7 +2646,7 @@ d3-zoom@^1.3.0:
|
||||||
d3-selection "1"
|
d3-selection "1"
|
||||||
d3-transition "1"
|
d3-transition "1"
|
||||||
|
|
||||||
d3@3, d3@^3.0.6, d3@^3.5.17, d3@^3.5.5, d3@^3.5.6:
|
d3@3, d3@^3.5.17, d3@^3.5.5, d3@^3.5.6:
|
||||||
version "3.5.17"
|
version "3.5.17"
|
||||||
resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8"
|
resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 5ccf602336a0
|
||||||
|
Revises: ('130915240929', 'c9495751e314')
|
||||||
|
Create Date: 2018-04-12 16:00:47.639218
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '5ccf602336a0'
|
||||||
|
down_revision = ('130915240929', 'c9495751e314')
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
pass
|
|
@ -0,0 +1,56 @@
|
||||||
|
"""cal_heatmap_metric_to_metrics
|
||||||
|
|
||||||
|
Revision ID: bf706ae5eb46
|
||||||
|
Revises: f231d82b9b26
|
||||||
|
Create Date: 2018-04-10 11:19:47.621878
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
import json
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy import Column, Integer, String, Text
|
||||||
|
|
||||||
|
from superset import db
|
||||||
|
from superset.legacy import cast_form_data
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'bf706ae5eb46'
|
||||||
|
down_revision = 'f231d82b9b26'
|
||||||
|
|
||||||
|
|
||||||
|
class Slice(Base):
|
||||||
|
"""Declarative class to do query in upgrade"""
|
||||||
|
__tablename__ = 'slices'
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
datasource_type = Column(String(200))
|
||||||
|
viz_type = Column(String(200))
|
||||||
|
slice_name = Column(String(200))
|
||||||
|
params = Column(Text)
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
bind = op.get_bind()
|
||||||
|
session = db.Session(bind=bind)
|
||||||
|
|
||||||
|
slices = session.query(Slice).filter_by(viz_type='cal_heatmap').all()
|
||||||
|
slice_len = len(slices)
|
||||||
|
for i, slc in enumerate(slices):
|
||||||
|
try:
|
||||||
|
params = json.loads(slc.params or '{}')
|
||||||
|
params['metrics'] = [params.get('metric')]
|
||||||
|
del params['metric']
|
||||||
|
slc.params = json.dumps(params, indent=2, sort_keys=True)
|
||||||
|
session.merge(slc)
|
||||||
|
session.commit()
|
||||||
|
print('Upgraded ({}/{}): {}'.format(i, slice_len, slc.slice_name))
|
||||||
|
except Exception as e:
|
||||||
|
print(slc.slice_name + ' error: ' + str(e))
|
||||||
|
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
pass
|
|
@ -0,0 +1,22 @@
|
||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: c9495751e314
|
||||||
|
Revises: ('30bb17c0dc76', 'bf706ae5eb46')
|
||||||
|
Create Date: 2018-04-10 20:46:57.890773
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'c9495751e314'
|
||||||
|
down_revision = ('30bb17c0dc76', 'bf706ae5eb46')
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
pass
|
|
@ -5,16 +5,15 @@ Revises: e68c4473c581
|
||||||
Create Date: 2018-03-20 19:47:54.991259
|
Create Date: 2018-03-20 19:47:54.991259
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision = 'f231d82b9b26'
|
|
||||||
down_revision = 'e68c4473c581'
|
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from superset.utils import generic_find_uq_constraint_name
|
from superset.utils import generic_find_uq_constraint_name
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'f231d82b9b26'
|
||||||
|
down_revision = 'e68c4473c581'
|
||||||
|
|
||||||
conv = {
|
conv = {
|
||||||
'uq': 'uq_%(table_name)s_%(column_0_name)s',
|
'uq': 'uq_%(table_name)s_%(column_0_name)s',
|
||||||
}
|
}
|
||||||
|
@ -44,8 +43,12 @@ def upgrade():
|
||||||
[column, 'datasource_id'],
|
[column, 'datasource_id'],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def downgrade():
|
def downgrade():
|
||||||
|
|
||||||
|
bind = op.get_bind()
|
||||||
|
insp = sa.engine.reflection.Inspector.from_engine(bind)
|
||||||
|
|
||||||
# Restore the size of the metric_name column.
|
# Restore the size of the metric_name column.
|
||||||
with op.batch_alter_table('metrics', naming_convention=conv) as batch_op:
|
with op.batch_alter_table('metrics', naming_convention=conv) as batch_op:
|
||||||
batch_op.alter_column(
|
batch_op.alter_column(
|
||||||
|
|
|
@ -735,12 +735,18 @@ class CalHeatmapViz(BaseViz):
|
||||||
def get_data(self, df):
|
def get_data(self, df):
|
||||||
form_data = self.form_data
|
form_data = self.form_data
|
||||||
|
|
||||||
df.columns = ['timestamp', 'metric']
|
data = {}
|
||||||
timestamps = {str(obj['timestamp'].value / 10**9):
|
records = df.to_dict('records')
|
||||||
obj.get('metric') for obj in df.to_dict('records')}
|
for metric in self.metrics:
|
||||||
|
data[metric] = {
|
||||||
|
str(obj[DTTM_ALIAS].value / 10**9): obj.get(metric)
|
||||||
|
for obj in records
|
||||||
|
}
|
||||||
|
|
||||||
start = utils.parse_human_datetime(form_data.get('since'))
|
start = utils.parse_human_datetime(form_data.get('since'))
|
||||||
end = utils.parse_human_datetime(form_data.get('until'))
|
end = utils.parse_human_datetime(form_data.get('until'))
|
||||||
|
if not start or not end:
|
||||||
|
raise Exception("Please provide both time bounds (Since and Until)")
|
||||||
domain = form_data.get('domain_granularity')
|
domain = form_data.get('domain_granularity')
|
||||||
diff_delta = rdelta.relativedelta(end, start)
|
diff_delta = rdelta.relativedelta(end, start)
|
||||||
diff_secs = (end - start).total_seconds()
|
diff_secs = (end - start).total_seconds()
|
||||||
|
@ -757,7 +763,7 @@ class CalHeatmapViz(BaseViz):
|
||||||
range_ = diff_secs // (60 * 60) + 1
|
range_ = diff_secs // (60 * 60) + 1
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'timestamps': timestamps,
|
'data': data,
|
||||||
'start': start,
|
'start': start,
|
||||||
'domain': domain,
|
'domain': domain,
|
||||||
'subdomain': form_data.get('subdomain_granularity'),
|
'subdomain': form_data.get('subdomain_granularity'),
|
||||||
|
@ -765,9 +771,10 @@ class CalHeatmapViz(BaseViz):
|
||||||
}
|
}
|
||||||
|
|
||||||
def query_obj(self):
|
def query_obj(self):
|
||||||
qry = super(CalHeatmapViz, self).query_obj()
|
d = super(CalHeatmapViz, self).query_obj()
|
||||||
qry['metrics'] = [self.form_data['metric']]
|
fd = self.form_data
|
||||||
return qry
|
d['metrics'] = fd.get('metrics')
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
class NVD3Viz(BaseViz):
|
class NVD3Viz(BaseViz):
|
||||||
|
|
Loading…
Reference in New Issue