Add progress bars to Historical Data Import screen (https://github.com/woocommerce/woocommerce-admin/pull/2312)
* Hook up import/status endpoint to Historical Data Import screen * Fix PHP tests * Add speak message when the import is complete * Several fixes * Cleanup * Update progress bars every 3 seconds and bugfixes * Rename ongoingImport to activeImport * Use timestamp to identify queries * Use timestamps for historical data state * Add 'initializing' status * Cleanup * Pass less props around * Refactor getStatus * Set stop timestamp on request error * Typo
This commit is contained in:
parent
3123c4e047
commit
5d01cee56c
|
@ -7,19 +7,18 @@ import { Button } from '@wordpress/components';
|
|||
import { Fragment } from '@wordpress/element';
|
||||
|
||||
function HistoricalDataActions( {
|
||||
customersProgress,
|
||||
customersTotal,
|
||||
hasImportedData,
|
||||
inProgress,
|
||||
importDate,
|
||||
onDeletePreviousData,
|
||||
onReimportData,
|
||||
onStartImport,
|
||||
onStopImport,
|
||||
ordersProgress,
|
||||
ordersTotal,
|
||||
status,
|
||||
} ) {
|
||||
const getActions = () => {
|
||||
const importDisabled = status !== 'ready';
|
||||
|
||||
// An import is currently in progress
|
||||
if ( inProgress ) {
|
||||
if ( [ 'initializing', 'customers', 'orders', 'finalizing' ].includes( status ) ) {
|
||||
return (
|
||||
<Fragment>
|
||||
<Button
|
||||
|
@ -44,42 +43,34 @@ function HistoricalDataActions( {
|
|||
);
|
||||
}
|
||||
|
||||
// Has no imported data
|
||||
if ( ! hasImportedData ) {
|
||||
// @todo When the import status endpoint is hooked up,
|
||||
// the 'Delete Previously Imported Data' button should be
|
||||
// removed from this section.
|
||||
if ( [ 'ready', 'nothing' ].includes( status ) ) {
|
||||
if ( importDate ) {
|
||||
return (
|
||||
<Fragment>
|
||||
<Button isPrimary onClick={ onStartImport } disabled={ importDisabled }>
|
||||
{ __( 'Start', 'woocommerce-admin' ) }
|
||||
</Button>
|
||||
<Button isDefault onClick={ onDeletePreviousData }>
|
||||
{ __( 'Delete Previously Imported Data', 'woocommerce-admin' ) }
|
||||
</Button>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Button isPrimary onClick={ onStartImport }>
|
||||
<Button isPrimary onClick={ onStartImport } disabled={ importDisabled }>
|
||||
{ __( 'Start', 'woocommerce-admin' ) }
|
||||
</Button>
|
||||
<Button isDefault onClick={ onDeletePreviousData }>
|
||||
{ __( 'Delete Previously Imported Data', 'woocommerce-admin' ) }
|
||||
</Button>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
// Has imported all possible data
|
||||
if ( customersProgress === customersTotal && ordersProgress === ordersTotal ) {
|
||||
return (
|
||||
<Fragment>
|
||||
<Button isDefault onClick={ () => null }>
|
||||
{ __( 'Re-import Data', 'woocommerce-admin' ) }
|
||||
</Button>
|
||||
<Button isDefault onClick={ onDeletePreviousData }>
|
||||
{ __( 'Delete Previously Imported Data', 'woocommerce-admin' ) }
|
||||
</Button>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
// It's not in progress and has some imported data
|
||||
return (
|
||||
<Fragment>
|
||||
<Button isPrimary onClick={ onStartImport }>
|
||||
{ __( 'Start', 'woocommerce-admin' ) }
|
||||
<Button isDefault onClick={ onReimportData }>
|
||||
{ __( 'Re-import Data', 'woocommerce-admin' ) }
|
||||
</Button>
|
||||
<Button isDefault onClick={ onDeletePreviousData }>
|
||||
{ __( 'Delete Previously Imported Data', 'woocommerce-admin' ) }
|
||||
|
|
|
@ -6,6 +6,7 @@ import { __ } from '@wordpress/i18n';
|
|||
import apiFetch from '@wordpress/api-fetch';
|
||||
import { Component } from '@wordpress/element';
|
||||
import moment from 'moment';
|
||||
import { withSpokenMessages } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
|
@ -25,7 +26,11 @@ class HistoricalData extends Component {
|
|||
this.dateFormat = __( 'MM/DD/YYYY', 'woocommerce-admin' );
|
||||
|
||||
this.state = {
|
||||
inProgress: false,
|
||||
// Whether there is an active import (which might have been stopped)
|
||||
// that matches the period and skipChecked settings
|
||||
activeImport: null,
|
||||
lastImportStartTimestamp: 0,
|
||||
lastImportStopTimestamp: 0,
|
||||
period: {
|
||||
date: moment().format( this.dateFormat ),
|
||||
label: 'all',
|
||||
|
@ -34,7 +39,10 @@ class HistoricalData extends Component {
|
|||
};
|
||||
|
||||
this.makeQuery = this.makeQuery.bind( this );
|
||||
this.onImportFinished = this.onImportFinished.bind( this );
|
||||
this.onImportStarted = this.onImportStarted.bind( this );
|
||||
this.onDeletePreviousData = this.onDeletePreviousData.bind( this );
|
||||
this.onReimportData = this.onReimportData.bind( this );
|
||||
this.onStartImport = this.onStartImport.bind( this );
|
||||
this.onStopImport = this.onStopImport.bind( this );
|
||||
this.onDateChange = this.onDateChange.bind( this );
|
||||
|
@ -50,15 +58,38 @@ class HistoricalData extends Component {
|
|||
addNotice( { status: 'success', message: response.message } );
|
||||
} else {
|
||||
addNotice( { status: 'error', message: errorMessage } );
|
||||
this.setState( {
|
||||
activeImport: false,
|
||||
lastImportStopTimestamp: Date.now(),
|
||||
} );
|
||||
}
|
||||
} )
|
||||
.catch( error => {
|
||||
if ( error && error.message ) {
|
||||
addNotice( { status: 'error', message: error.message } );
|
||||
this.setState( {
|
||||
activeImport: false,
|
||||
lastImportStopTimestamp: Date.now(),
|
||||
} );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
onImportFinished() {
|
||||
const { debouncedSpeak } = this.props;
|
||||
debouncedSpeak( 'Import complete' );
|
||||
this.setState( {
|
||||
lastImportStopTimestamp: Date.now(),
|
||||
} );
|
||||
}
|
||||
|
||||
onImportStarted() {
|
||||
this.setState( {
|
||||
activeImport: true,
|
||||
lastImportStartTimestamp: Date.now(),
|
||||
} );
|
||||
}
|
||||
|
||||
onDeletePreviousData() {
|
||||
const path = '/wc/v4/reports/import/delete';
|
||||
const errorMessage = __(
|
||||
|
@ -66,24 +97,33 @@ class HistoricalData extends Component {
|
|||
'woocommerce-admin'
|
||||
);
|
||||
this.makeQuery( path, errorMessage );
|
||||
this.setState( {
|
||||
activeImport: false,
|
||||
} );
|
||||
}
|
||||
|
||||
onReimportData() {
|
||||
this.setState( {
|
||||
activeImport: false,
|
||||
} );
|
||||
}
|
||||
|
||||
onStartImport() {
|
||||
const { period, skipChecked } = this.state;
|
||||
this.setState( {
|
||||
inProgress: true,
|
||||
} );
|
||||
const path = '/wc/v4/reports/import' + stringifyQuery( formatParams( period, skipChecked ) );
|
||||
const path =
|
||||
'/wc/v4/reports/import' +
|
||||
stringifyQuery( formatParams( this.dateFormat, period, skipChecked ) );
|
||||
const errorMessage = __(
|
||||
'There was a problem rebuilding your report data.',
|
||||
'woocommerce-admin'
|
||||
);
|
||||
this.makeQuery( path, errorMessage );
|
||||
this.onImportStarted();
|
||||
}
|
||||
|
||||
onStopImport() {
|
||||
this.setState( {
|
||||
inProgress: false,
|
||||
lastImportStopTimestamp: Date.now(),
|
||||
} );
|
||||
const path = '/wc/v4/reports/import/cancel';
|
||||
const errorMessage = __(
|
||||
|
@ -95,6 +135,7 @@ class HistoricalData extends Component {
|
|||
|
||||
onPeriodChange( val ) {
|
||||
this.setState( {
|
||||
activeImport: false,
|
||||
period: {
|
||||
...this.state.period,
|
||||
label: val,
|
||||
|
@ -104,6 +145,7 @@ class HistoricalData extends Component {
|
|||
|
||||
onDateChange( val ) {
|
||||
this.setState( {
|
||||
activeImport: false,
|
||||
period: {
|
||||
date: val,
|
||||
label: 'custom',
|
||||
|
@ -113,21 +155,33 @@ class HistoricalData extends Component {
|
|||
|
||||
onSkipChange( val ) {
|
||||
this.setState( {
|
||||
activeImport: false,
|
||||
skipChecked: val,
|
||||
} );
|
||||
}
|
||||
|
||||
render() {
|
||||
const { inProgress, period, skipChecked } = this.state;
|
||||
const {
|
||||
activeImport,
|
||||
lastImportStartTimestamp,
|
||||
lastImportStopTimestamp,
|
||||
period,
|
||||
skipChecked,
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<HistoricalDataLayout
|
||||
activeImport={ activeImport }
|
||||
dateFormat={ this.dateFormat }
|
||||
inProgress={ inProgress }
|
||||
onImportFinished={ this.onImportFinished }
|
||||
onImportStarted={ this.onImportStarted }
|
||||
lastImportStartTimestamp={ lastImportStartTimestamp }
|
||||
lastImportStopTimestamp={ lastImportStopTimestamp }
|
||||
onPeriodChange={ this.onPeriodChange }
|
||||
onDateChange={ this.onDateChange }
|
||||
onSkipChange={ this.onSkipChange }
|
||||
onDeletePreviousData={ this.onDeletePreviousData }
|
||||
onReimportData={ this.onReimportData }
|
||||
onStartImport={ this.onStartImport }
|
||||
onStopImport={ this.onStopImport }
|
||||
period={ period }
|
||||
|
@ -137,4 +191,4 @@ class HistoricalData extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default HistoricalData;
|
||||
export default withSpokenMessages( HistoricalData );
|
||||
|
|
|
@ -4,11 +4,14 @@
|
|||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Component, Fragment } from '@wordpress/element';
|
||||
import { isNil } from 'lodash';
|
||||
import { SECOND } from '@fresh-data/framework';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { formatParams } from './utils';
|
||||
import { DEFAULT_REQUIREMENT } from 'wc-api/constants';
|
||||
import { formatParams, getStatus } from './utils';
|
||||
import HistoricalDataActions from './actions';
|
||||
import HistoricalDataPeriodSelector from './period-selector';
|
||||
import HistoricalDataProgress from './progress';
|
||||
|
@ -18,46 +21,18 @@ import withSelect from 'wc-api/with-select';
|
|||
import './style.scss';
|
||||
|
||||
class HistoricalDataLayout extends Component {
|
||||
getStatus() {
|
||||
const {
|
||||
customersProgress,
|
||||
customersTotal,
|
||||
inProgress,
|
||||
ordersProgress,
|
||||
ordersTotal,
|
||||
} = this.props;
|
||||
|
||||
if ( inProgress ) {
|
||||
if ( customersProgress < customersTotal ) {
|
||||
return 'customers';
|
||||
}
|
||||
if ( ordersProgress < ordersTotal ) {
|
||||
return 'orders';
|
||||
}
|
||||
return 'finalizing';
|
||||
}
|
||||
if (
|
||||
( customersTotal > 0 || ordersTotal > 0 ) &&
|
||||
customersProgress === customersTotal &&
|
||||
ordersProgress === ordersTotal
|
||||
) {
|
||||
return 'finished';
|
||||
}
|
||||
return 'ready';
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
customersProgress,
|
||||
customersTotal,
|
||||
dateFormat,
|
||||
hasImportedData,
|
||||
importDate,
|
||||
inProgress,
|
||||
onPeriodChange,
|
||||
onDateChange,
|
||||
onSkipChange,
|
||||
onDeletePreviousData,
|
||||
onReimportData,
|
||||
onStartImport,
|
||||
onStopImport,
|
||||
ordersProgress,
|
||||
|
@ -65,14 +40,13 @@ class HistoricalDataLayout extends Component {
|
|||
period,
|
||||
skipChecked,
|
||||
} = this.props;
|
||||
const hasImportedAllData =
|
||||
! inProgress &&
|
||||
hasImportedData &&
|
||||
customersProgress === customersTotal &&
|
||||
ordersProgress === ordersTotal;
|
||||
// @todo When the import status endpoint is hooked up,
|
||||
// this bool should be removed and assume it's true.
|
||||
const showImportStatus = false;
|
||||
const status = getStatus( {
|
||||
customersProgress,
|
||||
customersTotal,
|
||||
inProgress,
|
||||
ordersProgress,
|
||||
ordersTotal,
|
||||
} );
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
|
@ -88,7 +62,7 @@ class HistoricalDataLayout extends Component {
|
|||
'woocommerce-admin'
|
||||
) }
|
||||
</span>
|
||||
{ ! hasImportedAllData && (
|
||||
{ status !== 'finished' && (
|
||||
<Fragment>
|
||||
<HistoricalDataPeriodSelector
|
||||
dateFormat={ dateFormat }
|
||||
|
@ -102,37 +76,28 @@ class HistoricalDataLayout extends Component {
|
|||
checked={ skipChecked }
|
||||
onChange={ onSkipChange }
|
||||
/>
|
||||
{ showImportStatus && (
|
||||
<Fragment>
|
||||
<HistoricalDataProgress
|
||||
label={ __( 'Registered Customers', 'woocommerce-admin' ) }
|
||||
progress={ customersProgress }
|
||||
total={ customersTotal }
|
||||
/>
|
||||
<HistoricalDataProgress
|
||||
label={ __( 'Orders', 'woocommerce-admin' ) }
|
||||
progress={ ordersProgress }
|
||||
total={ ordersTotal }
|
||||
/>
|
||||
</Fragment>
|
||||
) }
|
||||
<HistoricalDataProgress
|
||||
label={ __( 'Registered Customers', 'woocommerce-admin' ) }
|
||||
progress={ customersProgress }
|
||||
total={ customersTotal }
|
||||
/>
|
||||
<HistoricalDataProgress
|
||||
label={ __( 'Orders', 'woocommerce-admin' ) }
|
||||
progress={ ordersProgress }
|
||||
total={ ordersTotal }
|
||||
/>
|
||||
</Fragment>
|
||||
) }
|
||||
{ showImportStatus && (
|
||||
<HistoricalDataStatus importDate={ importDate } status={ this.getStatus() } />
|
||||
) }
|
||||
<HistoricalDataStatus importDate={ importDate } status={ status } />
|
||||
</div>
|
||||
</div>
|
||||
<HistoricalDataActions
|
||||
customersProgress={ customersProgress }
|
||||
customersTotal={ customersTotal }
|
||||
hasImportedData={ hasImportedData }
|
||||
inProgress={ inProgress }
|
||||
importDate={ importDate }
|
||||
onDeletePreviousData={ onDeletePreviousData }
|
||||
onReimportData={ onReimportData }
|
||||
onStartImport={ onStartImport }
|
||||
onStopImport={ onStopImport }
|
||||
ordersProgress={ ordersProgress }
|
||||
ordersTotal={ ordersTotal }
|
||||
status={ status }
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
|
@ -140,19 +105,75 @@ class HistoricalDataLayout extends Component {
|
|||
}
|
||||
|
||||
export default withSelect( ( select, props ) => {
|
||||
const { getImportTotals } = select( 'wc-api' );
|
||||
const { period, skipChecked } = props;
|
||||
const { getImportStatus, isGetImportStatusRequesting, getImportTotals } = select( 'wc-api' );
|
||||
const {
|
||||
activeImport,
|
||||
dateFormat,
|
||||
lastImportStartTimestamp,
|
||||
lastImportStopTimestamp,
|
||||
onImportStarted,
|
||||
onImportFinished,
|
||||
period,
|
||||
skipChecked,
|
||||
} = props;
|
||||
|
||||
const { customers: customersTotal, orders: ordersTotal } = getImportTotals(
|
||||
formatParams( period, skipChecked )
|
||||
const inProgress =
|
||||
( typeof lastImportStartTimestamp !== 'undefined' &&
|
||||
typeof lastImportStopTimestamp === 'undefined' ) ||
|
||||
lastImportStartTimestamp > lastImportStopTimestamp;
|
||||
|
||||
const params = formatParams( dateFormat, period, skipChecked );
|
||||
// Use timestamp to invalidate previous totals when the import finished/stopped
|
||||
const { customers, orders } = getImportTotals( params, lastImportStopTimestamp );
|
||||
const requirement = inProgress
|
||||
? {
|
||||
freshness: 3 * SECOND,
|
||||
timeout: 3 * SECOND,
|
||||
}
|
||||
: DEFAULT_REQUIREMENT;
|
||||
|
||||
// Use timestamp to invalidate previous status when a new import starts
|
||||
const {
|
||||
customers_count: customersProgress,
|
||||
customers_total: customersTotal,
|
||||
imported_from: importDate,
|
||||
is_importing: isImporting,
|
||||
orders_count: ordersProgress,
|
||||
orders_total: ordersTotal,
|
||||
} = getImportStatus( lastImportStartTimestamp, requirement );
|
||||
const isStatusLoading = isGetImportStatusRequesting( lastImportStartTimestamp );
|
||||
|
||||
const hasImportStarted = Boolean(
|
||||
! lastImportStartTimestamp && ! isStatusLoading && ! inProgress && isImporting === true
|
||||
);
|
||||
if ( hasImportStarted ) {
|
||||
onImportStarted();
|
||||
}
|
||||
const hasImportFinished = Boolean(
|
||||
! isStatusLoading &&
|
||||
inProgress &&
|
||||
isImporting === false &&
|
||||
( ( customersProgress === customersTotal && customersTotal > 0 ) ||
|
||||
( ordersProgress === ordersTotal && ordersTotal > 0 ) )
|
||||
);
|
||||
if ( hasImportFinished ) {
|
||||
onImportFinished();
|
||||
}
|
||||
|
||||
if ( ! activeImport ) {
|
||||
return {
|
||||
customersTotal: customers,
|
||||
importDate,
|
||||
ordersTotal: orders,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
customersProgress: 0,
|
||||
customersTotal,
|
||||
hasImportedData: false,
|
||||
importDate: '2019-04-01',
|
||||
ordersProgress: 0,
|
||||
ordersTotal,
|
||||
customersProgress,
|
||||
customersTotal: isNil( customersTotal ) ? customers : customersTotal,
|
||||
importDate,
|
||||
inProgress,
|
||||
ordersProgress,
|
||||
ordersTotal: isNil( ordersTotal ) ? orders : ordersTotal,
|
||||
};
|
||||
} )( HistoricalDataLayout );
|
||||
|
|
|
@ -10,13 +10,12 @@ function HistoricalDataProgress( { label, progress, total } ) {
|
|||
label,
|
||||
} );
|
||||
|
||||
const labelCounters =
|
||||
! isNil( progress ) && ! isNil( total )
|
||||
? sprintf( __( '%(progress)s of %(total)s', 'woocommerce-admin' ), {
|
||||
progress,
|
||||
total,
|
||||
} )
|
||||
: null;
|
||||
const labelCounters = ! isNil( total )
|
||||
? sprintf( __( '%(progress)s of %(total)s', 'woocommerce-admin' ), {
|
||||
progress: progress || 0,
|
||||
total,
|
||||
} )
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className="woocommerce-settings-historical-data__progress">
|
||||
|
@ -29,7 +28,7 @@ function HistoricalDataProgress( { label, progress, total } ) {
|
|||
<progress
|
||||
className="woocommerce-settings-historical-data__progress-bar"
|
||||
max={ total }
|
||||
value={ progress }
|
||||
value={ progress || 0 }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -16,13 +16,15 @@ const HISTORICAL_DATA_STATUS_FILTER = 'woocommerce_admin_import_status';
|
|||
|
||||
function HistoricalDataStatus( { importDate, status } ) {
|
||||
const statusLabels = applyFilters( HISTORICAL_DATA_STATUS_FILTER, {
|
||||
nothing: __( 'Nothing To Import', 'woocommerce-admin' ),
|
||||
ready: __( 'Ready To Import', 'woocommerce-admin' ),
|
||||
initializing: [ __( 'Initializing', 'woocommerce-admin' ), <Spinner key="spinner" /> ],
|
||||
customers: [ __( 'Importing Customers', 'woocommerce-admin' ), <Spinner key="spinner" /> ],
|
||||
orders: [ __( 'Importing Orders', 'woocommerce-admin' ), <Spinner key="spinner" /> ],
|
||||
finalizing: [ __( 'Finalizing', 'woocommerce-admin' ), <Spinner key="spinner" /> ],
|
||||
finished: sprintf(
|
||||
__( 'Historical data from %s onward imported', 'woocommerce-admin' ),
|
||||
moment( importDate ).format( 'll' )
|
||||
importDate !== '-1' ? moment( importDate ).format( 'll' ) : ''
|
||||
),
|
||||
} );
|
||||
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
display: grid;
|
||||
grid-column-gap: $gap-large;
|
||||
grid-template-columns: calc(50% - #{$gap-large/2}) calc(50% - #{$gap-large/2});
|
||||
margin-top: $gap-small;
|
||||
|
||||
.woocommerce-settings-historical-data__column {
|
||||
align-self: end;
|
||||
margin-top: $gap-small;
|
||||
// Auto-position fix for IE11.
|
||||
@include set-grid-item-position( 2, 2 );
|
||||
}
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/** @format */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import moment from 'moment';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { formatParams, getStatus } from '../utils';
|
||||
|
||||
describe( 'formatParams', () => {
|
||||
it( 'returns empty object when skipChecked is false and period is all', () => {
|
||||
expect( formatParams( 'YYYY-MM-DD', { label: 'all' }, false ) ).toEqual( {} );
|
||||
} );
|
||||
|
||||
it( 'returns skip_existing param', () => {
|
||||
expect( formatParams( 'YYYY-MM-DD', { label: 'all' }, true ) ).toEqual( {
|
||||
skip_existing: true,
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'returns correct days param based on period label', () => {
|
||||
expect( formatParams( 'YYYY-MM-DD', { label: '30' }, false ) ).toEqual( { days: 30 } );
|
||||
} );
|
||||
|
||||
it( 'returns correct days param based on period date', () => {
|
||||
const date = '2018-01-01';
|
||||
const days = Math.floor( moment().diff( moment( date, 'YYYY-MM-DD' ), 'days', true ) );
|
||||
expect( formatParams( 'YYYY-MM-DD', { label: 'custom', date }, false ) ).toEqual( { days } );
|
||||
} );
|
||||
|
||||
it( 'returns both params', () => {
|
||||
expect( formatParams( 'YYYY-MM-DD', { label: '30' }, true ) ).toEqual( {
|
||||
skip_existing: true,
|
||||
days: 30,
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'getStatus', () => {
|
||||
it( 'returns `initializing` when no progress numbers are defined', () => {
|
||||
expect(
|
||||
getStatus( {
|
||||
customersTotal: 1,
|
||||
inProgress: true,
|
||||
ordersTotal: 1,
|
||||
} )
|
||||
).toEqual( 'initializing' );
|
||||
} );
|
||||
|
||||
it( 'returns `customers` when importing customers', () => {
|
||||
expect(
|
||||
getStatus( {
|
||||
customersProgress: 0,
|
||||
customersTotal: 1,
|
||||
inProgress: true,
|
||||
ordersProgress: 0,
|
||||
ordersTotal: 1,
|
||||
} )
|
||||
).toEqual( 'customers' );
|
||||
} );
|
||||
|
||||
it( 'returns `orders` when importing orders', () => {
|
||||
expect(
|
||||
getStatus( {
|
||||
customersProgress: 1,
|
||||
customersTotal: 1,
|
||||
inProgress: true,
|
||||
ordersProgress: 0,
|
||||
ordersTotal: 1,
|
||||
} )
|
||||
).toEqual( 'orders' );
|
||||
} );
|
||||
|
||||
it( 'returns `finalizing` when customers and orders are already imported', () => {
|
||||
expect(
|
||||
getStatus( {
|
||||
customersProgress: 1,
|
||||
customersTotal: 1,
|
||||
inProgress: true,
|
||||
ordersProgress: 1,
|
||||
ordersTotal: 1,
|
||||
} )
|
||||
).toEqual( 'finalizing' );
|
||||
} );
|
||||
|
||||
it( 'returns `finished` when customers and orders are already imported and inProgress is false', () => {
|
||||
expect(
|
||||
getStatus( {
|
||||
customersProgress: 1,
|
||||
customersTotal: 1,
|
||||
inProgress: false,
|
||||
ordersProgress: 1,
|
||||
ordersTotal: 1,
|
||||
} )
|
||||
).toEqual( 'finished' );
|
||||
} );
|
||||
|
||||
it( 'returns `ready` when there are customers or orders to import', () => {
|
||||
expect(
|
||||
getStatus( {
|
||||
customersProgress: 0,
|
||||
customersTotal: 1,
|
||||
inProgress: false,
|
||||
ordersProgress: 0,
|
||||
ordersTotal: 1,
|
||||
} )
|
||||
).toEqual( 'ready' );
|
||||
} );
|
||||
|
||||
it( 'returns `nothing` when there are no customers or orders to import', () => {
|
||||
expect(
|
||||
getStatus( {
|
||||
customersProgress: 0,
|
||||
customersTotal: 0,
|
||||
inProgress: false,
|
||||
ordersProgress: 0,
|
||||
ordersTotal: 0,
|
||||
} )
|
||||
).toEqual( 'nothing' );
|
||||
} );
|
||||
} );
|
|
@ -2,17 +2,18 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { isNil } from 'lodash';
|
||||
import moment from 'moment';
|
||||
|
||||
export const formatParams = ( period, skipChecked ) => {
|
||||
export const formatParams = ( dateFormat, period, skipChecked ) => {
|
||||
const params = {};
|
||||
if ( skipChecked ) {
|
||||
params.skip_existing = true;
|
||||
}
|
||||
if ( period.label !== 'all' ) {
|
||||
if ( period.label === 'custom' ) {
|
||||
const daysDifference = moment().diff( moment( period.date, this.dateFormat ), 'days', true );
|
||||
params.days = Math.ceil( daysDifference );
|
||||
const daysDifference = moment().diff( moment( period.date, dateFormat ), 'days', true );
|
||||
params.days = Math.floor( daysDifference );
|
||||
} else {
|
||||
params.days = parseInt( period.label, 10 );
|
||||
}
|
||||
|
@ -20,3 +21,36 @@ export const formatParams = ( period, skipChecked ) => {
|
|||
|
||||
return params;
|
||||
};
|
||||
|
||||
export const getStatus = ( {
|
||||
customersProgress,
|
||||
customersTotal,
|
||||
inProgress,
|
||||
ordersProgress,
|
||||
ordersTotal,
|
||||
} ) => {
|
||||
if ( inProgress ) {
|
||||
if (
|
||||
isNil( customersProgress ) ||
|
||||
isNil( ordersProgress ) ||
|
||||
isNil( customersTotal ) ||
|
||||
isNil( ordersTotal )
|
||||
) {
|
||||
return 'initializing';
|
||||
}
|
||||
if ( customersProgress < customersTotal ) {
|
||||
return 'customers';
|
||||
}
|
||||
if ( ordersProgress < ordersTotal ) {
|
||||
return 'orders';
|
||||
}
|
||||
return 'finalizing';
|
||||
}
|
||||
if ( customersTotal > 0 || ordersTotal > 0 ) {
|
||||
if ( customersProgress === customersTotal && ordersProgress === ordersTotal ) {
|
||||
return 'finished';
|
||||
}
|
||||
return 'ready';
|
||||
}
|
||||
return 'nothing';
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
import { omit } from 'lodash';
|
||||
|
||||
/**
|
||||
* WooCommerce dependencies
|
||||
|
@ -12,25 +13,35 @@ import { stringifyQuery } from '@woocommerce/navigation';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { isResourcePrefix, getResourceIdentifier } from '../utils';
|
||||
import { getResourcePrefix, getResourceIdentifier } from '../utils';
|
||||
import { NAMESPACE } from '../constants';
|
||||
|
||||
const typeEndpointMap = {
|
||||
'import-status': 'reports/import/status',
|
||||
'import-totals': 'reports/import/totals',
|
||||
};
|
||||
|
||||
function read( resourceNames, fetch = apiFetch ) {
|
||||
const filteredNames = resourceNames.filter( name => isResourcePrefix( name, 'import-totals' ) );
|
||||
const filteredNames = resourceNames.filter( name => {
|
||||
const prefix = getResourcePrefix( name );
|
||||
return Boolean( typeEndpointMap[ prefix ] );
|
||||
} );
|
||||
|
||||
return filteredNames.map( async resourceName => {
|
||||
const prefix = getResourcePrefix( resourceName );
|
||||
const endpoint = typeEndpointMap[ prefix ];
|
||||
const query = getResourceIdentifier( resourceName );
|
||||
const fetchArgs = {
|
||||
parse: false,
|
||||
path: NAMESPACE + '/reports/import/totals' + stringifyQuery( query ),
|
||||
path: NAMESPACE + `/${ endpoint }${ stringifyQuery( omit( query, [ 'timestamp' ] ) ) }`,
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch( fetchArgs );
|
||||
const totals = await response.json();
|
||||
const data = await response.json();
|
||||
|
||||
return {
|
||||
[ resourceName ]: totals,
|
||||
[ resourceName ]: { data },
|
||||
};
|
||||
} catch ( error ) {
|
||||
return { [ resourceName ]: { error } };
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
/** @format */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { isNil } from 'lodash';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -6,14 +10,37 @@
|
|||
import { getResourceName } from '../utils';
|
||||
import { DEFAULT_REQUIREMENT } from '../constants';
|
||||
|
||||
const getImportTotals = ( getResource, requireResource ) => (
|
||||
query = {},
|
||||
const getImportStatus = ( getResource, requireResource ) => (
|
||||
timestamp,
|
||||
requirement = DEFAULT_REQUIREMENT
|
||||
) => {
|
||||
const resourceName = getResourceName( 'import-totals', query );
|
||||
return requireResource( requirement, resourceName ) || { customers: null, orders: null };
|
||||
const resourceName = getResourceName( 'import-status', timestamp );
|
||||
return requireResource( requirement, resourceName ).data || {};
|
||||
};
|
||||
|
||||
const isGetImportStatusRequesting = getResource => timestamp => {
|
||||
const resourceName = getResourceName( 'import-status', timestamp );
|
||||
const { lastRequested, lastReceived } = getResource( resourceName );
|
||||
|
||||
if ( isNil( lastRequested ) || isNil( lastReceived ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return lastRequested > lastReceived;
|
||||
};
|
||||
|
||||
const getImportTotals = ( getResource, requireResource ) => (
|
||||
query = {},
|
||||
timestamp,
|
||||
requirement = DEFAULT_REQUIREMENT
|
||||
) => {
|
||||
const identifier = { ...query, timestamp };
|
||||
const resourceName = getResourceName( 'import-totals', identifier );
|
||||
return requireResource( requirement, resourceName ).data || {};
|
||||
};
|
||||
|
||||
export default {
|
||||
getImportStatus,
|
||||
isGetImportStatusRequesting,
|
||||
getImportTotals,
|
||||
};
|
||||
|
|
|
@ -187,7 +187,7 @@ class WC_Admin_REST_Reports_Import_Controller extends WC_Admin_REST_Reports_Cont
|
|||
'type' => 'integer',
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'minimum' => 1,
|
||||
'minimum' => 0,
|
||||
);
|
||||
$params['skip_existing'] = array(
|
||||
'description' => __( 'Skip importing existing order data.', 'woocommerce-admin' ),
|
||||
|
@ -284,10 +284,10 @@ class WC_Admin_REST_Reports_Import_Controller extends WC_Admin_REST_Reports_Cont
|
|||
public function get_import_status( $request ) {
|
||||
$result = array(
|
||||
'is_importing' => WC_Admin_Reports_Sync::is_importing(),
|
||||
'customers_total' => get_option( 'wc_admin_import_customers_total', 0 ),
|
||||
'customers_count' => get_option( 'wc_admin_import_customers_count', 0 ),
|
||||
'orders_total' => get_option( 'wc_admin_import_orders_total', 0 ),
|
||||
'orders_count' => get_option( 'wc_admin_import_orders_count', 0 ),
|
||||
'customers_total' => (int) get_option( 'wc_admin_import_customers_total', 0 ),
|
||||
'customers_count' => (int) get_option( 'wc_admin_import_customers_count', 0 ),
|
||||
'orders_total' => (int) get_option( 'wc_admin_import_orders_total', 0 ),
|
||||
'orders_count' => (int) get_option( 'wc_admin_import_orders_count', 0 ),
|
||||
'imported_from' => get_option( 'wc_admin_imported_from_date', false ),
|
||||
);
|
||||
|
||||
|
|
|
@ -161,12 +161,14 @@ class WC_Admin_Reports_Sync {
|
|||
*/
|
||||
public static function get_import_totals( $days, $skip_existing ) {
|
||||
$orders = self::get_orders( 1, 1, $days, $skip_existing );
|
||||
$customer_roles = apply_filters( 'woocommerce_admin_import_customer_roles', array( 'customer' ) );
|
||||
$customer_query = self::get_user_ids_for_batch(
|
||||
$days,
|
||||
$skip_existing,
|
||||
array(
|
||||
'fields' => 'ID',
|
||||
'number' => 1,
|
||||
'fields' => 'ID',
|
||||
'number' => 1,
|
||||
'role__in' => $customer_roles,
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -187,6 +189,7 @@ class WC_Admin_Reports_Sync {
|
|||
'status' => 'pending',
|
||||
'per_page' => 1,
|
||||
'claimed' => false,
|
||||
'search' => 'import',
|
||||
'group' => self::QUEUE_GROUP,
|
||||
)
|
||||
);
|
||||
|
@ -235,6 +238,13 @@ class WC_Admin_Reports_Sync {
|
|||
// Delete customers after order data is deleted.
|
||||
self::queue_dependent_action( self::CUSTOMERS_DELETE_BATCH_INIT, array(), self::ORDERS_DELETE_BATCH_INIT );
|
||||
|
||||
// Delete import options.
|
||||
delete_option( 'wc_admin_import_customers_count' );
|
||||
delete_option( 'wc_admin_import_orders_count' );
|
||||
delete_option( 'wc_admin_import_customers_total' );
|
||||
delete_option( 'wc_admin_import_orders_total' );
|
||||
delete_option( 'wc_admin_imported_from_date' );
|
||||
|
||||
return __( 'Report table data is being deleted.', 'woocommerce-admin' );
|
||||
}
|
||||
|
||||
|
@ -334,9 +344,9 @@ class WC_Admin_Reports_Sync {
|
|||
public static function get_orders( $limit = 10, $page = 1, $days = false, $skip_existing = false ) {
|
||||
global $wpdb;
|
||||
$where_clause = '';
|
||||
$offset = $page > 1 ? $page * $limit : 0;
|
||||
$offset = $page > 1 ? ( $page - 1 ) * $limit : 0;
|
||||
|
||||
if ( $days ) {
|
||||
if ( is_int( $days ) ) {
|
||||
$days_ago = date( 'Y-m-d 00:00:00', time() - ( DAY_IN_SECONDS * $days ) );
|
||||
$where_clause .= " AND post_date >= '{$days_ago}'";
|
||||
}
|
||||
|
@ -564,7 +574,7 @@ class WC_Admin_Reports_Sync {
|
|||
$query_args = array();
|
||||
}
|
||||
|
||||
if ( $days ) {
|
||||
if ( is_int( $days ) ) {
|
||||
$query_args['date_query'] = array(
|
||||
'after' => date( 'Y-m-d 00:00:00', time() - ( DAY_IN_SECONDS * $days ) ),
|
||||
);
|
||||
|
@ -587,7 +597,7 @@ class WC_Admin_Reports_Sync {
|
|||
* Init customer lookup table update (in batches).
|
||||
*
|
||||
* @param int|bool $days Number of days to process.
|
||||
* @param bool $skip_existing Skip exisiting records.
|
||||
* @param bool $skip_existing Skip existing records.
|
||||
*/
|
||||
public static function customer_lookup_import_batch_init( $days, $skip_existing ) {
|
||||
$batch_size = self::get_batch_size( self::CUSTOMERS_IMPORT_BATCH_ACTION );
|
||||
|
|
|
@ -330,8 +330,9 @@ class WC_Tests_API_Reports_Import extends WC_REST_Unit_Test_Case {
|
|||
$report = $response->get_data();
|
||||
$user_query = new WP_User_Query(
|
||||
array(
|
||||
'fields' => 'ID',
|
||||
'number' => 1,
|
||||
'fields' => 'ID',
|
||||
'number' => 1,
|
||||
'role__in' => array( 'customer' ),
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -363,7 +364,7 @@ class WC_Tests_API_Reports_Import extends WC_REST_Unit_Test_Case {
|
|||
$this->assertEquals( 200, $response->get_status() );
|
||||
$this->assertEquals( true, $report['is_importing'] );
|
||||
$this->assertEquals( 0, $report['customers_count'] );
|
||||
$this->assertEquals( 3, $report['customers_total'] );
|
||||
$this->assertEquals( 1, $report['customers_total'] );
|
||||
$this->assertEquals( 0, $report['orders_count'] );
|
||||
$this->assertEquals( 4, $report['orders_total'] );
|
||||
|
||||
|
@ -380,7 +381,7 @@ class WC_Tests_API_Reports_Import extends WC_REST_Unit_Test_Case {
|
|||
$this->assertEquals( 200, $response->get_status() );
|
||||
$this->assertEquals( false, $report['is_importing'] );
|
||||
$this->assertEquals( 1, $report['customers_count'] );
|
||||
$this->assertEquals( 3, $report['customers_total'] );
|
||||
$this->assertEquals( 1, $report['customers_total'] );
|
||||
$this->assertEquals( 4, $report['orders_count'] );
|
||||
$this->assertEquals( 4, $report['orders_total'] );
|
||||
|
||||
|
@ -390,8 +391,7 @@ class WC_Tests_API_Reports_Import extends WC_REST_Unit_Test_Case {
|
|||
$response = $this->server->dispatch( $request );
|
||||
$report = $response->get_data();
|
||||
|
||||
// @todo The following line should be uncommented when https://github.com/woocommerce/woocommerce-admin/issues/2195 is resolved.
|
||||
// $this->assertEquals( 0, $report['customers'] );
|
||||
$this->assertEquals( 0, $report['customers'] );
|
||||
$this->assertEquals( 0, $report['orders'] );
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue