mirror of
https://github.com/apache/superset.git
synced 2024-09-12 16:49:40 -04:00
Adding rowcount label to explore view header (#4059)
This commit is contained in:
parent
ec752b1378
commit
c21513fb8c
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
||||
|
||||
import { chartPropType } from '../../chart/chartReducer';
|
||||
import ExploreActionButtons from './ExploreActionButtons';
|
||||
import RowCountLabel from './RowCountLabel';
|
||||
import EditableTitle from '../../components/EditableTitle';
|
||||
import AlteredSliceTag from '../../components/AlteredSliceTag';
|
||||
import FaveStar from '../../components/FaveStar';
|
||||
@ -66,11 +67,12 @@ class ExploreChartHeader extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const formData = this.props.form_data;
|
||||
const queryResponse = this.props.chart.queryResponse;
|
||||
const data = {
|
||||
csv_endpoint: getExploreUrl(this.props.form_data, 'csv'),
|
||||
json_endpoint: getExploreUrl(this.props.form_data, 'json'),
|
||||
standalone_endpoint: getExploreUrl(this.props.form_data, 'standalone'),
|
||||
csv_endpoint: getExploreUrl(formData, 'csv'),
|
||||
json_endpoint: getExploreUrl(formData, 'json'),
|
||||
standalone_endpoint: getExploreUrl(formData, 'standalone'),
|
||||
};
|
||||
|
||||
return (
|
||||
@ -109,13 +111,20 @@ class ExploreChartHeader extends React.PureComponent {
|
||||
{this.props.chart.sliceFormData &&
|
||||
<AlteredSliceTag
|
||||
origFormData={this.props.chart.sliceFormData}
|
||||
currentFormData={this.props.form_data}
|
||||
currentFormData={formData}
|
||||
/>
|
||||
}
|
||||
<div className="pull-right">
|
||||
{this.props.chart.chartStatus === 'success' && queryResponse &&
|
||||
<RowCountLabel
|
||||
rowcount={queryResponse.rowcount}
|
||||
limit={formData.row_limit}
|
||||
/>
|
||||
}
|
||||
{this.props.chart.chartStatus === 'success' &&
|
||||
queryResponse &&
|
||||
queryResponse.is_cached &&
|
||||
|
||||
<CachedLabel
|
||||
onClick={this.runQuery.bind(this)}
|
||||
cachedTimestamp={queryResponse.cached_dttm}
|
||||
@ -133,7 +142,7 @@ class ExploreChartHeader extends React.PureComponent {
|
||||
canDownload={this.props.can_download}
|
||||
chartStatus={this.props.chart.chartStatus}
|
||||
queryResponse={queryResponse}
|
||||
queryEndpoint={getExploreUrl(this.props.form_data, 'query')}
|
||||
queryEndpoint={getExploreUrl(formData, 'query')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Label } from 'react-bootstrap';
|
||||
|
||||
import { t } from '../../locales';
|
||||
import { defaultNumberFormatter } from '../../modules/utils';
|
||||
import TooltipWrapper from '../../components/TooltipWrapper';
|
||||
|
||||
|
||||
const propTypes = {
|
||||
rowcount: PropTypes.number,
|
||||
limit: PropTypes.number,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
};
|
||||
|
||||
export default function RowCountLabel({ rowcount, limit }) {
|
||||
const limitReached = rowcount === limit;
|
||||
const bsStyle = (limitReached || rowcount === 0) ? 'warning' : 'default';
|
||||
const formattedRowCount = defaultNumberFormatter(rowcount);
|
||||
const tooltip = (
|
||||
<span>
|
||||
{limitReached &&
|
||||
<div>{t('Limit reached')}</div>}
|
||||
{rowcount}
|
||||
</span>
|
||||
);
|
||||
return (
|
||||
<TooltipWrapper label="tt-rowcount" tooltip={tooltip}>
|
||||
<Label
|
||||
bsStyle={bsStyle}
|
||||
style={{ fontSize: '10px', marginRight: '5px', cursor: 'pointer' }}
|
||||
>
|
||||
{formattedRowCount} rows
|
||||
</Label>
|
||||
</TooltipWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
RowCountLabel.propTypes = propTypes;
|
||||
RowCountLabel.defaultProps = defaultProps;
|
@ -4,6 +4,17 @@ import $ from 'jquery';
|
||||
|
||||
import { formatDate, UTC } from './dates';
|
||||
|
||||
const siFormatter = d3.format('.3s');
|
||||
|
||||
export function defaultNumberFormatter(n) {
|
||||
let si = siFormatter(n);
|
||||
// Removing trailing `.00` if any
|
||||
if (si.slice(-1) < 'A') {
|
||||
si = parseFloat(si).toString();
|
||||
}
|
||||
return si;
|
||||
}
|
||||
|
||||
export function d3FormatPreset(format) {
|
||||
// like d3.format, but with support for presets like 'smart_date'
|
||||
if (format === 'smart_date') {
|
||||
@ -12,7 +23,7 @@ export function d3FormatPreset(format) {
|
||||
if (format) {
|
||||
return d3.format(format);
|
||||
}
|
||||
return d3.format('.3s');
|
||||
return defaultNumberFormatter;
|
||||
}
|
||||
export const d3TimeFormatPreset = function (format) {
|
||||
const effFormat = format || 'smart_date';
|
||||
|
@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import { expect } from 'chai';
|
||||
import { describe, it } from 'mocha';
|
||||
import { shallow } from 'enzyme';
|
||||
import { Label } from 'react-bootstrap';
|
||||
|
||||
import TooltipWrapper from './../../../../javascripts/components/TooltipWrapper';
|
||||
|
||||
import RowCountLabel from '../../../../javascripts/explore/components/RowCountLabel';
|
||||
|
||||
describe('RowCountLabel', () => {
|
||||
const defaultProps = {
|
||||
rowcount: 51,
|
||||
limit: 100,
|
||||
};
|
||||
|
||||
it('is valid', () => {
|
||||
expect(React.isValidElement(<RowCountLabel {...defaultProps} />)).to.equal(true);
|
||||
});
|
||||
it('renders a Label and a TooltipWrapper', () => {
|
||||
const wrapper = shallow(<RowCountLabel {...defaultProps} />);
|
||||
expect(wrapper.find(Label)).to.have.lengthOf(1);
|
||||
expect(wrapper.find(TooltipWrapper)).to.have.lengthOf(1);
|
||||
});
|
||||
it('renders a warning when limit is reached', () => {
|
||||
const props = {
|
||||
rowcount: 100,
|
||||
limit: 100,
|
||||
};
|
||||
const wrapper = shallow(<RowCountLabel {...props} />);
|
||||
expect(wrapper.find(Label).first().props().bsStyle).to.equal('warning');
|
||||
});
|
||||
});
|
@ -2,7 +2,7 @@ import { it, describe } from 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import {
|
||||
tryNumify, slugify, formatSelectOptionsForRange, d3format,
|
||||
d3FormatPreset, d3TimeFormatPreset,
|
||||
d3FormatPreset, d3TimeFormatPreset, defaultNumberFormatter,
|
||||
} from '../../../javascripts/modules/utils';
|
||||
|
||||
describe('utils', () => {
|
||||
@ -52,4 +52,21 @@ describe('utils', () => {
|
||||
expect(d3FormatPreset('smart_date')(0)).to.equal('1970');
|
||||
});
|
||||
});
|
||||
describe('d3TimeFormatPreset', () => {
|
||||
expect(defaultNumberFormatter(10)).to.equal('10');
|
||||
expect(defaultNumberFormatter(1)).to.equal('1');
|
||||
expect(defaultNumberFormatter(1.0)).to.equal('1');
|
||||
expect(defaultNumberFormatter(10.0)).to.equal('10');
|
||||
expect(defaultNumberFormatter(10001)).to.equal('10.0k');
|
||||
expect(defaultNumberFormatter(111000000)).to.equal('111M');
|
||||
expect(defaultNumberFormatter(0.23)).to.equal('230m');
|
||||
|
||||
expect(defaultNumberFormatter(-10)).to.equal('-10');
|
||||
expect(defaultNumberFormatter(-1)).to.equal('-1');
|
||||
expect(defaultNumberFormatter(-1.0)).to.equal('-1');
|
||||
expect(defaultNumberFormatter(-10.0)).to.equal('-10');
|
||||
expect(defaultNumberFormatter(-10001)).to.equal('-10.0k');
|
||||
expect(defaultNumberFormatter(-111000000)).to.equal('-111M');
|
||||
expect(defaultNumberFormatter(-0.23)).to.equal('-230m');
|
||||
});
|
||||
});
|
||||
|
@ -273,11 +273,13 @@ class BaseViz(object):
|
||||
cache_timeout = self.cache_timeout
|
||||
stacktrace = None
|
||||
annotations = []
|
||||
rowcount = None
|
||||
try:
|
||||
df = self.get_df()
|
||||
if not self.error_message:
|
||||
data = self.get_data(df)
|
||||
annotations = self.get_annotations()
|
||||
rowcount = len(df.index)
|
||||
except Exception as e:
|
||||
logging.exception(e)
|
||||
if not self.error_message:
|
||||
@ -295,6 +297,7 @@ class BaseViz(object):
|
||||
'status': self.status,
|
||||
'stacktrace': stacktrace,
|
||||
'annotations': annotations,
|
||||
'rowcount': rowcount,
|
||||
}
|
||||
payload['cached_dttm'] = datetime.utcnow().isoformat().split('.')[0]
|
||||
logging.info('Caching for the next {} seconds'.format(
|
||||
|
Loading…
Reference in New Issue
Block a user