mistercrunch closed pull request #3375: Improved Datatables View (and some
other minor improvements as well)
URL: https://github.com/apache/incubator-superset/pull/3375
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/setup.py b/setup.py
index 0133596835..fa64677de6 100644
--- a/setup.py
+++ b/setup.py
@@ -59,7 +59,7 @@ def get_git_sha():
'humanize==0.5.1',
'gunicorn==19.7.1',
'markdown==2.6.8',
- 'pandas==0.20.2',
+ 'pandas==0.20.3',
'parsedatetime==2.0.0',
'pydruid==0.3.1',
'PyHive>=0.4.0',
diff --git a/superset/assets/backendSync.json b/superset/assets/backendSync.json
index 71e7130328..e1285c6fa2 100644
--- a/superset/assets/backendSync.json
+++ b/superset/assets/backendSync.json
@@ -2006,6 +2006,18 @@
"default": false,
"description": "Whether to include a time filter"
},
+ "filter_label": {
+ "type": "CheckboxControl",
+ "label": "Display Label",
+ "default": true,
+ "description": "Whether to display a label above each filter"
+ },
+ "display_metric": {
+ "type": "CheckboxControl",
+ "label": "Display Metric",
+ "default": false,
+ "description": "Whether to display the metric number next to the filter
option"
+ },
"show_datatable": {
"type": "CheckboxControl",
"label": "Data Table",
@@ -2019,6 +2031,12 @@
"default": false,
"description": "Whether to include a client side search box"
},
+ "csv_button": {
+ "type": "CheckboxControl",
+ "label": "CSV Button",
+ "default": false,
+ "description": "Whether to include a button to export filtered table to
csv"
+ },
"table_filter": {
"type": "CheckboxControl",
"label": "Table Filter",
@@ -2371,4 +2389,4 @@
"description": "The number of seconds before expiring the cache"
}
}
-}
\ No newline at end of file
+}
diff --git a/superset/assets/javascripts/explore/stores/controls.jsx
b/superset/assets/javascripts/explore/stores/controls.jsx
index ce5ee43fb4..a899399ad2 100644
--- a/superset/assets/javascripts/explore/stores/controls.jsx
+++ b/superset/assets/javascripts/explore/stores/controls.jsx
@@ -971,6 +971,20 @@ export const controls = {
description: 'Whether to include a time filter',
},
+ filter_label: {
+ type: 'CheckboxControl',
+ label: 'Display Label',
+ default: true,
+ description: 'Whether to display a label above each filter',
+ },
+
+ display_metric: {
+ type: 'CheckboxControl',
+ label: 'Display Metric',
+ default: false,
+ description: 'Whether to display the metric number next to the filter
option',
+ },
+
show_datatable: {
type: 'CheckboxControl',
label: 'Data Table',
@@ -986,6 +1000,13 @@ export const controls = {
description: 'Whether to include a client side search box',
},
+ csv_button: {
+ type: 'CheckboxControl',
+ label: 'CSV Button',
+ default: false,
+ description: 'Whether to include a button to export filtered table to csv',
+ },
+
table_filter: {
type: 'CheckboxControl',
label: 'Table Filter',
diff --git a/superset/assets/javascripts/explore/stores/visTypes.js
b/superset/assets/javascripts/explore/stores/visTypes.js
index d3ee320f07..964bcbab2e 100644
--- a/superset/assets/javascripts/explore/stores/visTypes.js
+++ b/superset/assets/javascripts/explore/stores/visTypes.js
@@ -342,6 +342,7 @@ export const visTypes = {
['table_timestamp_format'],
['row_limit', 'page_length'],
['include_search', 'table_filter'],
+ ['csv_button'],
],
},
],
@@ -868,7 +869,9 @@ export const visTypes = {
expanded: true,
controlSetRows: [
['groupby'],
+ ['filter_label'],
['metric'],
+ ['display_metric'],
],
},
{
diff --git a/superset/assets/javascripts/modules/utils.js
b/superset/assets/javascripts/modules/utils.js
index 61949c7e2b..d8e3accb7f 100644
--- a/superset/assets/javascripts/modules/utils.js
+++ b/superset/assets/javascripts/modules/utils.js
@@ -123,7 +123,12 @@ export function toggleCheckbox(apiUrlPrefix, selector) {
*/
export const fixDataTableBodyHeight = function ($tableDom, height) {
const headHeight = $tableDom.find('.dataTables_scrollHead').height();
- $tableDom.find('.dataTables_scrollBody').css('max-height', height -
headHeight);
+ const headerFooterHeight = $tableDom.find('.table-header').height() +
+ $tableDom.find('.table-footer').height();
+ $tableDom.find('.dataTables_scrollBody').css('max-height', height -
(headerFooterHeight + headHeight));
+ $tableDom.find('.table-body').css('max-height', height - headerFooterHeight);
+ $tableDom.find('.dataTables_scroll').css('max-height', height -
headerFooterHeight);
+ $tableDom.find('.table-data').css('max-height', height - headerFooterHeight);
};
export function d3format(format, number) {
diff --git a/superset/assets/package.json b/superset/assets/package.json
index f39b253781..1eb9338e22 100644
--- a/superset/assets/package.json
+++ b/superset/assets/package.json
@@ -54,6 +54,10 @@
"datatables-bootstrap3-plugin": "^0.5.0",
"datatables.net": "^1.10.13",
"datatables.net-bs": "^1.10.12",
+ "datatables.net-buttons": "^1.3.1",
+ "datatables.net-buttons-bs": "^1.3.1",
+ "gridster": "^0.5.6",
+ "immutability-helper": "^2.0.0",
"immutable": "^3.8.1",
"jquery": "^3.2.1",
"lodash.throttle": "^4.1.1",
diff --git a/superset/assets/stylesheets/buttons.dataTables.min.css
b/superset/assets/stylesheets/buttons.dataTables.min.css
new file mode 100644
index 0000000000..112078b3b7
--- /dev/null
+++ b/superset/assets/stylesheets/buttons.dataTables.min.css
@@ -0,0 +1 @@
+@keyframes dtb-spinner{100%{transform:rotate(360deg)}}@-o-keyframes
dtb-spinner{100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes
dtb-spinner{100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes
dtb-spinner{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes
dtb-spinner{100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}div.dt-button-info{position:fixed;top:50%;left:50%;width:400px;margin-top:-100px;margin-left:-200px;background-color:white;border:2px
solid #111;box-shadow:3px 3px 8px
rgba(0,0,0,0.3);border-radius:3px;text-align:center;z-index:21}div.dt-button-info
h2{padding:0.5em;margin:0;font-weight:normal;border-bottom:1px solid
#ddd;background-color:#f3f3f3}div.dt-button-info>div{padding:1em}button.dt-button,div.dt-button,a.dt-button{position:relative;display:inline-block;box-sizing:border-box;margin-right:0.333em;padding:0.5em
1em;border:1px solid
#999;border-radius:2px;cursor:pointer;font-size:0.88em;color:black;white-space:nowrap;overflow:hidden;background-color:#e9e9e9;background-image:-webkit-linear-gradient(top,
#fff 0%, #e9e9e9 100%);background-image:-moz-linear-gradient(top, #fff 0%,
#e9e9e9 100%);background-image:-ms-linear-gradient(top, #fff 0%, #e9e9e9
100%);background-image:-o-linear-gradient(top, #fff 0%, #e9e9e9
100%);background-image:linear-gradient(to bottom, #fff 0%, #e9e9e9
100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='white',
EndColorStr='#e9e9e9');-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-decoration:none;outline:none}button.dt-button.disabled,div.dt-button.disabled,a.dt-button.disabled{color:#999;border:1px
solid
#d0d0d0;cursor:default;background-color:#f9f9f9;background-image:-webkit-linear-gradient(top,
#fff 0%, #f9f9f9 100%);background-image:-moz-linear-gradient(top, #fff 0%,
#f9f9f9 100%);background-image:-ms-linear-gradient(top, #fff 0%, #f9f9f9
100%);background-image:-o-linear-gradient(top, #fff 0%, #f9f9f9
100%);background-image:linear-gradient(to bottom, #fff 0%, #f9f9f9
100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#ffffff',
EndColorStr='#f9f9f9')}button.dt-button:active:not(.disabled),button.dt-button.active:not(.disabled),div.dt-button:active:not(.disabled),div.dt-button.active:not(.disabled),a.dt-button:active:not(.disabled),a.dt-button.active:not(.disabled){background-color:#e2e2e2;background-image:-webkit-linear-gradient(top,
#f3f3f3 0%, #e2e2e2 100%);background-image:-moz-linear-gradient(top, #f3f3f3
0%, #e2e2e2 100%);background-image:-ms-linear-gradient(top, #f3f3f3 0%, #e2e2e2
100%);background-image:-o-linear-gradient(top, #f3f3f3 0%, #e2e2e2
100%);background-image:linear-gradient(to bottom, #f3f3f3 0%, #e2e2e2
100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#f3f3f3',
EndColorStr='#e2e2e2');box-shadow:inset 1px 1px 3px
#999999}button.dt-button:active:not(.disabled):hover:not(.disabled),button.dt-button.active:not(.disabled):hover:not(.disabled),div.dt-button:active:not(.disabled):hover:not(.disabled),div.dt-button.active:not(.disabled):hover:not(.disabled),a.dt-button:active:not(.disabled):hover:not(.disabled),a.dt-button.active:not(.disabled):hover:not(.disabled){box-shadow:inset
1px 1px 3px
#999999;background-color:#cccccc;background-image:-webkit-linear-gradient(top,
#eaeaea 0%, #ccc 100%);background-image:-moz-linear-gradient(top, #eaeaea 0%,
#ccc 100%);background-image:-ms-linear-gradient(top, #eaeaea 0%, #ccc
100%);background-image:-o-linear-gradient(top, #eaeaea 0%, #ccc
100%);background-image:linear-gradient(to bottom, #eaeaea 0%, #ccc
100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#eaeaea',
EndColorStr='#cccccc')}button.dt-button:hover,div.dt-button:hover,a.dt-button:hover{text-decoration:none}button.dt-button:hover:not(.disabled),div.dt-button:hover:not(.disabled),a.dt-button:hover:not(.disabled){border:1px
solid
#666;background-color:#e0e0e0;background-image:-webkit-linear-gradient(top,
#f9f9f9 0%, #e0e0e0 100%);background-image:-moz-linear-gradient(top, #f9f9f9
0%, #e0e0e0 100%);background-image:-ms-linear-gradient(top, #f9f9f9 0%, #e0e0e0
100%);background-image:-o-linear-gradient(top, #f9f9f9 0%, #e0e0e0
100%);background-image:linear-gradient(to bottom, #f9f9f9 0%, #e0e0e0
100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#f9f9f9',
EndColorStr='#e0e0e0')}button.dt-button:focus:not(.disabled),div.dt-button:focus:not(.disabled),a.dt-button:focus:not(.disabled){border:1px
solid #426c9e;text-shadow:0 1px 0
#c4def1;outline:none;background-color:#79ace9;background-image:-webkit-linear-gradient(top,
#bddef4 0%, #79ace9 100%);background-image:-moz-linear-gradient(top, #bddef4
0%, #79ace9 100%);background-image:-ms-linear-gradient(top, #bddef4 0%, #79ace9
100%);background-image:-o-linear-gradient(top, #bddef4 0%, #79ace9
100%);background-image:linear-gradient(to bottom, #bddef4 0%, #79ace9
100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#bddef4',
EndColorStr='#79ace9')}.dt-button
embed{outline:none}div.dt-buttons{position:relative;float:left}div.dt-buttons.buttons-right{float:right}div.dt-button-collection{position:absolute;top:0;left:0;width:150px;margin-top:3px;padding:8px
8px 4px 8px;border:1px solid #ccc;border:1px solid
rgba(0,0,0,0.4);background-color:white;overflow:hidden;z-index:2002;border-radius:5px;box-shadow:3px
3px 5px
rgba(0,0,0,0.3);z-index:2002;-webkit-column-gap:8px;-moz-column-gap:8px;-ms-column-gap:8px;-o-column-gap:8px;column-gap:8px}div.dt-button-collection
button.dt-button,div.dt-button-collection
div.dt-button,div.dt-button-collection
a.dt-button{position:relative;left:0;right:0;display:block;float:none;margin-bottom:4px;margin-right:0}div.dt-button-collection
button.dt-button:active:not(.disabled),div.dt-button-collection
button.dt-button.active:not(.disabled),div.dt-button-collection
div.dt-button:active:not(.disabled),div.dt-button-collection
div.dt-button.active:not(.disabled),div.dt-button-collection
a.dt-button:active:not(.disabled),div.dt-button-collection
a.dt-button.active:not(.disabled){background-color:#dadada;background-image:-webkit-linear-gradient(top,
#f0f0f0 0%, #dadada 100%);background-image:-moz-linear-gradient(top, #f0f0f0
0%, #dadada 100%);background-image:-ms-linear-gradient(top, #f0f0f0 0%, #dadada
100%);background-image:-o-linear-gradient(top, #f0f0f0 0%, #dadada
100%);background-image:linear-gradient(to bottom, #f0f0f0 0%, #dadada
100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#f0f0f0',
EndColorStr='#dadada');box-shadow:inset 1px 1px 3px
#666}div.dt-button-collection.fixed{position:fixed;top:50%;left:50%;margin-left:-75px;border-radius:0}div.dt-button-collection.fixed.two-column{margin-left:-150px}div.dt-button-collection.fixed.three-column{margin-left:-225px}div.dt-button-collection.fixed.four-column{margin-left:-300px}div.dt-button-collection>*{-webkit-column-break-inside:avoid;break-inside:avoid}div.dt-button-collection.two-column{width:300px;padding-bottom:1px;-webkit-column-count:2;-moz-column-count:2;-ms-column-count:2;-o-column-count:2;column-count:2}div.dt-button-collection.three-column{width:450px;padding-bottom:1px;-webkit-column-count:3;-moz-column-count:3;-ms-column-count:3;-o-column-count:3;column-count:3}div.dt-button-collection.four-column{width:600px;padding-bottom:1px;-webkit-column-count:4;-moz-column-count:4;-ms-column-count:4;-o-column-count:4;column-count:4}div.dt-button-background{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);background:-ms-radial-gradient(center,
ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7)
100%);background:-moz-radial-gradient(center, ellipse farthest-corner,
rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-o-radial-gradient(center,
ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7)
100%);background:-webkit-gradient(radial, center center, 0, center center, 497,
color-stop(0, rgba(0,0,0,0.3)), color-stop(1,
rgba(0,0,0,0.7)));background:-webkit-radial-gradient(center, ellipse
farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7)
100%);background:radial-gradient(ellipse farthest-corner at center,
rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);z-index:2001}@media screen and
(max-width: 640px){div.dt-buttons{float:none
!important;text-align:center}}button.dt-button.processing,div.dt-button.processing,a.dt-button.processing{color:rgba(0,0,0,0.2)}button.dt-button.processing:after,div.dt-button.processing:after,a.dt-button.processing:after{position:absolute;top:50%;left:50%;width:16px;height:16px;margin:-8px
0 0 -8px;box-sizing:border-box;display:block;content:' ';border:2px solid
#282828;border-radius:50%;border-left-color:transparent;border-right-color:transparent;animation:dtb-spinner
1500ms infinite linear;-o-animation:dtb-spinner 1500ms infinite
linear;-ms-animation:dtb-spinner 1500ms infinite
linear;-webkit-animation:dtb-spinner 1500ms infinite
linear;-moz-animation:dtb-spinner 1500ms infinite linear}
diff --git a/superset/assets/stylesheets/dashboard.css
b/superset/assets/stylesheets/dashboard.css
index ad84807aa1..bbd9537c50 100644
--- a/superset/assets/stylesheets/dashboard.css
+++ b/superset/assets/stylesheets/dashboard.css
@@ -130,3 +130,8 @@ div.widget .chart-controls {
.slice_container .alert {
margin: 10px;
}
+
+.row {
+ margin-left: 0px;
+ margin-right: 0px;
+}
diff --git a/superset/assets/stylesheets/superset.less
b/superset/assets/stylesheets/superset.less
index 5c0c4d619c..8d9fc14b86 100644
--- a/superset/assets/stylesheets/superset.less
+++ b/superset/assets/stylesheets/superset.less
@@ -195,6 +195,10 @@ div.widget .slice_container {
margin-bottom: 0;
}
+.navbar-brand-image {
+ height: 100%;
+}
+
.table-condensed {
font-size: 12px;
}
diff --git a/superset/assets/visualizations/filter_box.jsx
b/superset/assets/visualizations/filter_box.jsx
index a5de26a810..60a48d7d0a 100644
--- a/superset/assets/visualizations/filter_box.jsx
+++ b/superset/assets/visualizations/filter_box.jsx
@@ -10,18 +10,22 @@ import { TIME_CHOICES } from './constants';
import './filter_box.css';
const propTypes = {
- origSelectedValues: PropTypes.object,
- instantFiltering: PropTypes.bool,
filtersChoices: PropTypes.object,
onChange: PropTypes.func,
showDateFilter: PropTypes.bool,
datasource: PropTypes.object.isRequired,
+ showLabel: PropTypes.bool,
+ showMetricNumber: PropTypes.bool,
+ origSelectedValues: PropTypes.object,
+ instantFiltering: PropTypes.bool,
};
const defaultProps = {
- origSelectedValues: {},
onChange: () => {},
showDateFilter: false,
+ showLabel: true,
+ showMetricNumber: false,
+ origSelectedValues: {},
instantFiltering: true,
};
@@ -100,7 +104,7 @@ class FilterBox extends React.Component {
});
return (
<div key={filter} className="m-b-5">
- {this.props.datasource.verbose_map[filter] || filter}
+ {this.props.showLabel && (this.props.datasource.verbose_map[filter]
|| filter)}
<Select.Creatable
placeholder={`Select [${filter}]`}
key={filter}
@@ -116,7 +120,13 @@ class FilterBox extends React.Component {
backgroundImage,
padding: '2px 5px',
};
- return { value: opt.id, label: opt.id, style };
+ let label;
+ if (this.props.showMetricNumber) {
+ label = opt.id.concat(' [', opt.metric, ']');
+ } else {
+ label = opt.id;
+ }
+ return { value: opt.id, label, style };
})}
onChange={this.changeFilter.bind(this, filter)}
/>
@@ -163,6 +173,8 @@ function filterBox(slice, payload) {
onChange={slice.addFilter}
showDateFilter={fd.date_filter}
datasource={slice.datasource}
+ showLabel={fd.filter_label}
+ showMetricNumber={fd.display_metric}
origSelectedValues={slice.getFilters() || {}}
instantFiltering={fd.instant_filtering}
/>,
diff --git a/superset/assets/visualizations/table.css
b/superset/assets/visualizations/table.css
index 84c7e27351..9de4ec9c83 100644
--- a/superset/assets/visualizations/table.css
+++ b/superset/assets/visualizations/table.css
@@ -27,3 +27,27 @@ table.table thead th.sorting:after, table.table thead
th.sorting_asc:after, tabl
.like-pre {
white-space: pre-wrap;
}
+
+.dataTables_length {
+ display: inline-block;
+ padding: 4px 10px;
+ border: 1px;
+ margin: 0px;
+}
+
+.buttons-html5 {
+ padding: 4px 10px;
+}
+
+.dt-buttons {
+ margin: 0px;
+}
+
+.dataTables_filter {
+ margin: 4px;
+}
+
+.dataTables_scroll {
+ height: 100%;
+}
+
diff --git a/superset/assets/visualizations/table.js
b/superset/assets/visualizations/table.js
index 82b0305864..d444c576ff 100644
--- a/superset/assets/visualizations/table.js
+++ b/superset/assets/visualizations/table.js
@@ -1,14 +1,12 @@
import d3 from 'd3';
import 'datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css';
-import 'datatables.net';
-import dt from 'datatables.net-bs';
-
-import { fixDataTableBodyHeight, d3TimeFormatPreset } from
'../javascripts/modules/utils';
+import { fixDataTableBodyHeight } from '../javascripts/modules/utils';
import './table.css';
const $ = require('jquery');
-
-dt(window, $);
+require('datatables.net-bs')(window, $);
+require('datatables.net-buttons-bs')(window, $);
+require('datatables.net-buttons/js/buttons.html5.js')(window, $);
function tableVis(slice, payload) {
const container = $(slice.selector);
@@ -33,17 +31,26 @@ function tableVis(slice, payload) {
maxes[metrics[i]] = d3.max(col(metrics[i]));
}
- const tsFormatter = d3TimeFormatPreset(fd.table_timestamp_format);
-
const div = d3.select(slice.selector);
div.html('');
const table = div.append('table')
.classed(
'dataframe dataframe table table-striped table-bordered ' +
- 'table-condensed table-hover dataTable no-footer', true)
+ 'table-condensed table-hover dataTable', true)
.attr('width', '100%');
const cols = data.columns.map(c => slice.datasource.verbose_map[c] || c);
+ const height = slice.height();
+ let paging = false;
+ let pageLength;
+ if (fd.page_length && fd.page_length > 0) {
+ paging = true;
+ pageLength = parseInt(fd.page_length, 10);
+ }
+ const buttons = [];
+ if (fd.csv_button) {
+ buttons.push('csvHtml5');
+ }
table.append('thead').append('tr')
.selectAll('th')
@@ -54,89 +61,106 @@ function tableVis(slice, payload) {
return d;
});
- table.append('tbody')
- .selectAll('tr')
- .data(data.records)
- .enter()
- .append('tr')
- .selectAll('td')
- .data(row => data.columns.map((c) => {
- const val = row[c];
- let html;
- const isMetric = metrics.indexOf(c) >= 0;
- if (c === '__timestamp') {
- html = tsFormatter(val);
- }
- if (typeof (val) === 'string') {
- html = `<span class="like-pre">${val}</span>`;
- }
- if (isMetric) {
- html = slice.d3format(c, val);
- }
- return {
- col: c,
- val,
- html,
- isMetric,
- };
- }))
- .enter()
- .append('td')
- .style('background-image', function (d) {
- if (d.isMetric) {
- const perc = Math.round((d.val / maxes[d.col]) * 100);
- // The 0.01 to 0.001 is a workaround for what appears to be a
- // CSS rendering bug on flat, transparent colors
- return (
- `linear-gradient(to left, rgba(0,0,0,0.2), rgba(0,0,0,0.2) ${perc}%,
` +
- `rgba(0,0,0,0.01) ${perc}%, rgba(0,0,0,0.001) 100%)`
- );
- }
- return null;
- })
- .classed('text-right', d => d.isMetric)
- .attr('title', (d) => {
- if (!isNaN(d.val)) {
- return fC(d.val);
- }
- return null;
- })
- .attr('data-sort', function (d) {
- return (d.isMetric) ? d.val : null;
- })
- .on('click', function (d) {
- if (!d.isMetric && fd.table_filter) {
- const td = d3.select(this);
- if (td.classed('filtered')) {
- slice.removeFilter(d.col, [d.val]);
- d3.select(this).classed('filtered', false);
- } else {
- d3.select(this).classed('filtered', true);
- slice.addFilter(d.col, [d.val]);
+ let datatable;
+
+ if ((!data.columns.find(c => metrics.indexOf(c) >= 0))) {
+ const columns = data.columns.map(c => ({ data: c }));
+ datatable = container.find('.dataTable').DataTable({
+ data: data.records,
+ columns,
+ paging,
+ scrollY: true,
+ deferRender: true,
+ pageLength,
+ searching: fd.include_search,
+ dom: '<"row table-header"<"col-sm-6"lB><"col-sm-6"f>>' +
+ '<"row table-body"<"col-sm-12 table-data"tr>>' +
+ '<"row table-footer"<"col-sm-5"i><"col-sm-7"p>>',
+ buttons,
+ });
+ } else {
+ table.append('tbody')
+ .selectAll('tr')
+ .data(data.records)
+ .enter()
+ .append('tr')
+ .selectAll('td')
+ .data(row => data.columns.map((c) => {
+ const val = row[c];
+ let html;
+ const isMetric = metrics.indexOf(c) >= 0;
+ if (c === 'timestamp') {
+ html = timestampFormatter(val);
}
- }
- })
- .style('cursor', function (d) {
- return (!d.isMetric) ? 'pointer' : '';
- })
- .html(d => d.html ? d.html : d.val);
- const height = slice.height();
- let paging = false;
- let pageLength;
- if (fd.page_length && fd.page_length > 0) {
- paging = true;
- pageLength = parseInt(fd.page_length, 10);
+ if (typeof (val) === 'string') {
+ html = `<span class="like-pre">${val}</span>`;
+ }
+ if (isMetric) {
+ html = slice.d3format(c, val);
+ }
+ return {
+ col: c,
+ val,
+ html,
+ isMetric,
+ };
+ }))
+ .enter()
+ .append('td')
+ .style('background-image', function (d) {
+ if (d.isMetric) {
+ const perc = Math.round((d.val / maxes[d.col]) * 100);
+ // The 0.01 to 0.001 is a workaround for what appears to be a
+ // CSS rendering bug on flat, transparent colors
+ return (
+ `linear-gradient(to right, rgba(0,0,0,0.2), rgba(0,0,0,0.2)
${perc}%, ` +
+ `rgba(0,0,0,0.01) ${perc}%, rgba(0,0,0,0.001) 100%)`
+ );
+ }
+ return null;
+ })
+ .attr('title', (d) => {
+ if (!isNaN(d.val)) {
+ return fC(d.val);
+ }
+ return null;
+ })
+ .attr('data-sort', function (d) {
+ return (d.isMetric) ? d.val : null;
+ })
+ .on('click', function (d) {
+ if (!d.isMetric && fd.table_filter) {
+ const td = d3.select(this);
+ if (td.classed('filtered')) {
+ slice.removeFilter(d.col, [d.val]);
+ d3.select(this).classed('filtered', false);
+ } else {
+ d3.select(this).classed('filtered', true);
+ slice.addFilter(d.col, [d.val]);
+ }
+ }
+ })
+ .style('cursor', function (d) {
+ return (!d.isMetric) ? 'pointer' : '';
+ })
+ .html(d => d.html ? d.html : d.val);
+
+ datatable = container.find('.dataTable').DataTable({
+ paging,
+ scrollY: true,
+ deferRender: true,
+ pageLength,
+ aaSorting: [],
+ searching: fd.include_search,
+ bInfo: false,
+ buttons,
+ dom: '<"row table-header"<"col-sm-6"lB><"col-sm-6"f>>' +
+ '<"row table-body"<"col-sm-12 table-data"tr>>' +
+ '<"row table-footer"<"col-sm-5"i><"col-sm-7"p>>',
+ });
}
- const datatable = container.find('.dataTable').DataTable({
- paging,
- pageLength,
- aaSorting: [],
- searching: fd.include_search,
- bInfo: false,
- scrollY: height + 'px',
- scrollCollapse: true,
- scrollX: true,
- });
+ datatable.buttons().container().appendTo('.dataTables_wrapper
.col-sm-6:eq(0)');
+
fixDataTableBodyHeight(
container.find('.dataTables_wrapper'), height);
// Sorting table by main column
diff --git a/superset/templates/appbuilder/navbar.html
b/superset/templates/appbuilder/navbar.html
index 0ea2daec5f..bee0ccc741 100644
--- a/superset/templates/appbuilder/navbar.html
+++ b/superset/templates/appbuilder/navbar.html
@@ -12,6 +12,7 @@
</button>
<a class="navbar-brand" href="/superset/profile/{{ current_user.username
}}/">
<img
+ class="navbar-brand-image"
width="126" src="{{ appbuilder.app_icon }}"
alt="{{ appbuilder.app_name }}"
/>
diff --git a/superset/views/core.py b/superset/views/core.py
index 65d3feb500..53bdf6d66e 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -61,7 +61,6 @@
USER_MISSING_ERR = __("The user seems to have been deleted")
DATASOURCE_ACCESS_ERR = __("You don't have access to this datasource")
-
def get_database_access_error_msg(database_name):
return __("This view requires the database %(name)s or "
"`all_datasource_access` permission", name=database_name)
@@ -2229,7 +2228,7 @@ def show_traceback(self):
def welcome(self):
"""Personalized welcome page"""
if not g.user or not g.user.get_id():
- return redirect(appbuilder.get_url_for_login)
+ return appbuilder.sm.auth_view.login()
return self.render_template(
'superset/welcome.html', entry='welcome', utils=utils)
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
With regards,
Apache Git Services