* Pass specified totals/intervals fields parameter to the Order Stats Query.

* Only request stats used in render for report summaries and charts.

Implement Orders as an example.

* Specify stats fields for all report requests.

* Add 'fields' parameter handling to all stats endpoint controllers.

* Reduce stats fields requested by dashboard charts.
This commit is contained in:
Jeff Stieler 2020-03-31 09:08:40 -06:00 committed by GitHub
parent 5242560972
commit f8bc173cfb
30 changed files with 189 additions and 39 deletions

View File

@ -330,6 +330,7 @@ ReportChart.defaultProps = {
export default compose(
withSelect( ( select, props ) => {
const {
charts,
endpoint,
filters,
isRequesting,
@ -366,6 +367,8 @@ export default compose(
};
}
const fields = charts && charts.map( chart => chart.key );
const primaryData = getReportChartData( {
endpoint,
dataType: 'primary',
@ -375,6 +378,7 @@ export default compose(
filters,
advancedFilters,
defaultDateRange,
fields,
} );
if ( chartMode === 'item-comparison' ) {
@ -393,6 +397,7 @@ export default compose(
filters,
advancedFilters,
defaultDateRange,
fields,
} );
return {
...newProps,

View File

@ -195,6 +195,7 @@ ReportSummary.defaultProps = {
export default compose(
withSelect( ( select, props ) => {
const {
charts,
endpoint,
isRequesting,
limitProperties,
@ -218,6 +219,8 @@ export default compose(
};
}
const fields = charts && charts.map( chart => chart.key );
const { woocommerce_default_date_range: defaultDateRange } = select(
SETTINGS_STORE_NAME
).getSetting( 'wc_admin', 'wcAdminSettings' );
@ -230,6 +233,7 @@ export default compose(
filters,
advancedFilters,
defaultDateRange,
fields,
} );
return {

View File

@ -552,6 +552,10 @@ ReportTable.propTypes = {
* The string to use as a query parameter when searching row items.
*/
searchBy: PropTypes.string,
/**
* List of fields used for summary numbers. (Reduces queries)
*/
summaryFields: PropTypes.arrayOf( PropTypes.string ),
/**
* Table data of that report. If it's not provided, it will be automatically
* loaded via the provided `endpoint`.
@ -596,6 +600,7 @@ export default compose(
columnPrefsKey,
filters,
advancedFilters,
summaryFields,
} = props;
let userPrefColumns = [];
@ -638,6 +643,7 @@ export default compose(
advancedFilters,
tableQuery,
defaultDateRange,
fields: summaryFields,
} )
: {};
const queriedTableData =

View File

@ -80,6 +80,7 @@ export default class CategoriesReport extends Component {
report="categories"
/>
<ReportChart
charts={ charts }
filters={ filters }
advancedFilters={ advancedFilters }
mode={ mode }

View File

@ -183,6 +183,11 @@ class CategoriesReportTable extends Component {
getHeadersContent={ this.getHeadersContent }
getRowsContent={ this.getRowsContent }
getSummary={ this.getSummary }
summaryFields={ [
'items_sold',
'net_revenue',
'orders_count',
] }
isRequesting={ isRequesting }
itemIdField="category_id"
query={ query }

View File

@ -63,6 +63,7 @@ export default class CouponsReport extends Component {
advancedFilters={ advancedFilters }
/>
<ReportChart
charts={ charts }
filters={ filters }
advancedFilters={ advancedFilters }
mode={ mode }

View File

@ -194,6 +194,11 @@ export default class CouponsReportTable extends Component {
getHeadersContent={ this.getHeadersContent }
getRowsContent={ this.getRowsContent }
getSummary={ this.getSummary }
summaryFields={ [
'coupons_count',
'orders_count',
'amount',
] }
isRequesting={ isRequesting }
itemIdField="coupon_id"
query={ query }

View File

@ -270,6 +270,12 @@ export default class CustomersReportTable extends Component {
getHeadersContent={ this.getHeadersContent }
getRowsContent={ this.getRowsContent }
getSummary={ this.getSummary }
summaryFields={ [
'customers_count',
'avg_orders_count',
'avg_total_spend',
'avg_avg_order_value',
] }
isRequesting={ isRequesting }
itemIdField="id"
query={ query }

View File

@ -36,6 +36,7 @@ export default class DownloadsReport extends Component {
advancedFilters={ advancedFilters }
/>
<ReportChart
charts={ charts }
endpoint="downloads"
path={ path }
query={ query }

View File

@ -177,6 +177,7 @@ class CouponsReportTable extends Component {
getHeadersContent={ this.getHeadersContent }
getRowsContent={ this.getRowsContent }
getSummary={ this.getSummary }
summaryFields={ [ 'download_count' ] }
query={ query }
tableQuery={ {
_embed: true,

View File

@ -36,6 +36,7 @@ export default class OrdersReport extends Component {
advancedFilters={ advancedFilters }
/>
<ReportChart
charts={ charts }
endpoint="orders"
path={ path }
query={ query }

View File

@ -327,6 +327,15 @@ export default class OrdersReportTable extends Component {
getHeadersContent={ this.getHeadersContent }
getRowsContent={ this.getRowsContent }
getSummary={ this.getSummary }
summaryFields={ [
'orders_count',
'num_new_customers',
'num_returning_customers',
'products',
'num_items_sold',
'coupons_count',
'net_revenue',
] }
query={ query }
tableQuery={ {
extended_info: true,

View File

@ -94,6 +94,7 @@ class ProductsReport extends Component {
advancedFilters={ advancedFilters }
/>
<ReportChart
charts={ charts }
mode={ mode }
filters={ filters }
advancedFilters={ advancedFilters }

View File

@ -243,6 +243,12 @@ export default class VariationsReportTable extends Component {
labels={ labels }
query={ query }
getSummary={ this.getSummary }
summaryFields={ [
'variations_count',
'items_sold',
'net_revenue',
'orders_count',
] }
searchBy="variations"
tableQuery={ {
orderby: query.orderby || 'items_sold',

View File

@ -333,6 +333,12 @@ class ProductsReportTable extends Component {
getHeadersContent={ this.getHeadersContent }
getRowsContent={ this.getRowsContent }
getSummary={ this.getSummary }
summaryFields={ [
'products_count',
'items_sold',
'net_revenue',
'orders_count',
] }
itemIdField="product_id"
isRequesting={ isRequesting }
labels={ labels }

View File

@ -36,6 +36,7 @@ export default class RevenueReport extends Component {
advancedFilters={ advancedFilters }
/>
<ReportChart
charts={ charts }
endpoint="revenue"
path={ path }
query={ query }

View File

@ -250,6 +250,16 @@ class RevenueReportTable extends Component {
getHeadersContent={ this.getHeadersContent }
getRowsContent={ this.getRowsContent }
getSummary={ this.getSummary }
summaryFields={ [
'orders_count',
'gross_sales',
'total_sales',
'refunds',
'coupons',
'taxes',
'shipping',
'net_revenue',
] }
query={ query }
tableData={ tableData }
title={ __( 'Revenue', 'woocommerce-admin' ) }

View File

@ -180,6 +180,13 @@ export default class StockReportTable extends Component {
getHeadersContent={ this.getHeadersContent }
getRowsContent={ this.getRowsContent }
getSummary={ this.getSummary }
summaryFields={ [
'products',
'outofstock',
'lowstock',
'instock',
'onbackorder',
] }
query={ query }
tableQuery={ {
orderby: query.orderby || 'stock_status',

View File

@ -58,6 +58,7 @@ export default class TaxesReport extends Component {
advancedFilters={ advancedFilters }
/>
<ReportChart
charts={ charts }
filters={ filters }
advancedFilters={ advancedFilters }
mode={ mode }

View File

@ -182,6 +182,13 @@ export default class TaxesReportTable extends Component {
getHeadersContent={ this.getHeadersContent }
getRowsContent={ this.getRowsContent }
getSummary={ this.getSummary }
summaryFields={ [
'tax_codes',
'total_tax',
'order_tax',
'shipping_tax',
'orders_count',
] }
isRequesting={ isRequesting }
itemIdField="tax_rate_id"
query={ query }

View File

@ -24,13 +24,9 @@ import './block.scss';
class ChartBlock extends Component {
handleChartClick = () => {
const { charts } = this.props;
const { selectedChart } = this.props;
if ( ! charts || ! charts.length ) {
return null;
}
getHistory().push( this.getChartPath( charts[ 0 ] ) );
getHistory().push( this.getChartPath( selectedChart ) );
};
getChartPath( chart ) {
@ -42,9 +38,15 @@ class ChartBlock extends Component {
}
render() {
const { charts, endpoint, path, query } = this.props;
const {
charts,
endpoint,
path,
query,
selectedChart,
} = this.props;
if ( ! charts || ! charts.length ) {
if ( ! selectedChart ) {
return null;
}
@ -56,27 +58,28 @@ class ChartBlock extends Component {
>
<Card
className="woocommerce-dashboard__chart-block woocommerce-analytics__card"
title={ charts[ 0 ].label }
title={ selectedChart.label }
>
<a
className="screen-reader-text"
href={ getAdminLink(
this.getChartPath( charts[ 0 ] )
this.getChartPath( selectedChart )
) }
>
{ /* translators: %s is the chart type */
sprintf(
__( '%s Report', 'woocommerce-admin' ),
charts[ 0 ].label
selectedChart.label
) }
</a>
<ReportChart
charts={ charts }
endpoint={ endpoint }
query={ query }
interactiveLegend={ false }
legendPosition="bottom"
path={ path }
selectedChart={ charts[ 0 ] }
selectedChart={ selectedChart }
showHeaderControls={ false }
/>
</Card>
@ -90,6 +93,7 @@ ChartBlock.propTypes = {
endpoint: PropTypes.string.isRequired,
path: PropTypes.string.isRequired,
query: PropTypes.object.isRequired,
selectedChart: PropTypes.object.isRequired,
};
export default ChartBlock;

View File

@ -164,8 +164,42 @@ class DashboardCharts extends Component {
} );
};
renderChartBlocks( query ) {
const { hiddenBlocks, path } = this.props;
// Reduce the API response to only the necessary stat fields
// by supplying all charts common to each endpoint.
const chartsByEndpoint = uniqCharts.reduce( ( byEndpoint, chart ) => {
if ( typeof byEndpoint[ chart.endpoint ] === 'undefined' ) {
byEndpoint[ chart.endpoint ] = [];
}
byEndpoint[ chart.endpoint ].push( chart );
return byEndpoint;
}, {} );
return (
<div className="woocommerce-dashboard__columns">
{ uniqCharts.map( ( chart ) => {
return hiddenBlocks.includes(
chart.endpoint + '_' + chart.key
) ? null : (
<ChartBlock
charts={ chartsByEndpoint[ chart.endpoint ] }
endpoint={ chart.endpoint }
key={ chart.endpoint + '_' + chart.key }
path={ path }
query={ query }
selectedChart={ chart }
/>
);
} ) }
</div>
);
}
render() {
const { hiddenBlocks, path, title } = this.props;
const { title } = this.props;
const { chartType, interval } = this.state;
const query = { ...this.props.query, chartType, interval };
return (
@ -218,21 +252,7 @@ class DashboardCharts extends Component {
/>
</NavigableMenu>
</SectionHeader>
<div className="woocommerce-dashboard__columns">
{ uniqCharts.map( ( chart ) => {
return hiddenBlocks.includes(
chart.endpoint + '_' + chart.key
) ? null : (
<ChartBlock
charts={ [ chart ] }
endpoint={ chart.endpoint }
key={ chart.endpoint + '_' + chart.key }
path={ path }
query={ query }
/>
);
} ) }
</div>
{ this.renderChartBlocks( query ) }
</div>
</Fragment>
);

View File

@ -197,7 +197,7 @@ export function isReportDataEmpty( report, endpoint ) {
* @return {Object} data request query parameters.
*/
function getRequestQuery( options ) {
const { endpoint, dataType, query } = options;
const { endpoint, dataType, query, fields } = options;
const datesFromQuery = getCurrentDates( query, options.defaultDateRange );
const interval = getIntervalForQuery( query );
const filterQuery = getFilterQuery( options );
@ -205,7 +205,7 @@ function getRequestQuery( options ) {
const noIntervals = includes( noIntervalEndpoints, endpoint );
return noIntervals
? { ...filterQuery }
? { ...filterQuery, fields }
: {
order: 'asc',
interval,
@ -216,6 +216,7 @@ function getRequestQuery( options ) {
),
before: appendTimestamp( end, 'end' ),
segmentby: query.segmentby,
fields,
...filterQuery,
};
}

View File

@ -53,6 +53,7 @@ class Controller extends \WC_REST_Reports_Controller {
$args['order'] = $request['order'];
$args['coupons'] = (array) $request['coupons'];
$args['segmentby'] = $request['segmentby'];
$args['fields'] = $request['fields'];
return $args;
}
@ -352,6 +353,12 @@ class Controller extends \WC_REST_Reports_Controller {
),
'validate_callback' => 'rest_validate_request_arg',
);
$params['fields'] = array(
'description' => __( 'Limit stats fields to the specified items.', 'woocommerce-admin' ),
'type' => 'array',
'sanitize_callback' => 'wp_parse_slug_list',
'validate_callback' => 'rest_validate_request_arg',
);
return $params;
}

View File

@ -65,6 +65,7 @@ class Controller extends \WC_REST_Reports_Controller {
$args['last_order_before'] = $request['last_order_before'];
$args['last_order_after'] = $request['last_order_after'];
$args['customers'] = $request['customers'];
$args['fields'] = $request['fields'];
$between_params_numeric = array( 'orders_count', 'total_spend', 'avg_order_value' );
$normalized_params_numeric = TimeInterval::normalize_between_params( $request, $between_params_numeric, false );
@ -362,6 +363,12 @@ class Controller extends \WC_REST_Reports_Controller {
'type' => 'integer',
),
);
$params['fields'] = array(
'description' => __( 'Limit stats fields to the specified items.', 'woocommerce-admin' ),
'type' => 'array',
'sanitize_callback' => 'wp_parse_slug_list',
'validate_callback' => 'rest_validate_request_arg',
);
return $params;
}

View File

@ -57,6 +57,7 @@ class Controller extends \WC_REST_Reports_Controller {
$args['order_excludes'] = (array) $request['order_excludes'];
$args['ip_address_includes'] = (array) $request['ip_address_includes'];
$args['ip_address_excludes'] = (array) $request['ip_address_excludes'];
$args['fields'] = $request['fields'];
return $args;
}
@ -369,6 +370,12 @@ class Controller extends \WC_REST_Reports_Controller {
'type' => 'string',
),
);
$params['fields'] = array(
'description' => __( 'Limit stats fields to the specified items.', 'woocommerce-admin' ),
'type' => 'array',
'sanitize_callback' => 'wp_parse_slug_list',
'validate_callback' => 'rest_validate_request_arg',
);
return $params;
}

View File

@ -42,15 +42,15 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller {
* @return array
*/
protected function prepare_reports_query( $request ) {
$args = array();
$args['before'] = $request['before'];
$args['after'] = $request['after'];
$args['interval'] = $request['interval'];
$args['page'] = $request['page'];
$args['per_page'] = $request['per_page'];
$args['orderby'] = $request['orderby'];
$args['order'] = $request['order'];
$args = array();
$args['before'] = $request['before'];
$args['after'] = $request['after'];
$args['interval'] = $request['interval'];
$args['page'] = $request['page'];
$args['per_page'] = $request['per_page'];
$args['orderby'] = $request['orderby'];
$args['order'] = $request['order'];
$args['fields'] = $request['fields'];
$args['match'] = $request['match'];
$args['status_is'] = (array) $request['status_is'];
$args['status_is_not'] = (array) $request['status_is_not'];
@ -513,6 +513,12 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller {
),
'validate_callback' => 'rest_validate_request_arg',
);
$params['fields'] = array(
'description' => __( 'Limit stats fields to the specified items.', 'woocommerce-admin' ),
'type' => 'array',
'sanitize_callback' => 'wp_parse_slug_list',
'validate_callback' => 'rest_validate_request_arg',
);
return $params;
}

View File

@ -415,6 +415,12 @@ class Controller extends \WC_REST_Reports_Controller {
),
'validate_callback' => 'rest_validate_request_arg',
);
$params['fields'] = array(
'description' => __( 'Limit stats fields to the specified items.', 'woocommerce-admin' ),
'type' => 'array',
'sanitize_callback' => 'wp_parse_slug_list',
'validate_callback' => 'rest_validate_request_arg',
);
return $params;
}

View File

@ -58,6 +58,7 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
$args['orderby'] = $request['orderby'];
$args['order'] = $request['order'];
$args['segmentby'] = $request['segmentby'];
$args['fields'] = $request['fields'];
return $args;
}

View File

@ -80,6 +80,7 @@ class Controller extends \WC_REST_Reports_Controller {
$args['order'] = $request['order'];
$args['taxes'] = (array) $request['taxes'];
$args['segmentby'] = $request['segmentby'];
$args['fields'] = $request['fields'];
return $args;
}
@ -387,6 +388,12 @@ class Controller extends \WC_REST_Reports_Controller {
),
'validate_callback' => 'rest_validate_request_arg',
);
$params['fields'] = array(
'description' => __( 'Limit stats fields to the specified items.', 'woocommerce-admin' ),
'type' => 'array',
'sanitize_callback' => 'wp_parse_slug_list',
'validate_callback' => 'rest_validate_request_arg',
);
return $params;
}