Skip to content

Commit cb37995

Browse files
Stats: fix CSV export filenames for selected date ranges
Use the custom range query dates (and period) for download filenames so exports match the selected selection instead of a single period unit. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent b74495a commit cb37995

3 files changed

Lines changed: 111 additions & 10 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import moment from 'moment';
2+
3+
/**
4+
* Builds the CSV export filename so it matches the data selection.
5+
*
6+
* When a custom date range query is present (`start_date` + `date`), those dates
7+
* and the query period are used. Otherwise the legacy period object bounds are used.
8+
*
9+
* @param {Object} options
10+
* @param {string} options.siteSlug Site slug used as the filename prefix.
11+
* @param {string} options.path Stats module path segment (e.g. "posts").
12+
* @param {Object} options.period Period object with `period`, `startOf`, and `endOf`.
13+
* @param {Object} [options.query] Stats query; custom ranges include `start_date` and `date`.
14+
* @returns {string} Filename ending in `.csv`.
15+
*/
16+
export function getStatsCsvFileName( { siteSlug, path, period, query } ) {
17+
const hasCustomDateRange = Boolean( query?.start_date && query?.date );
18+
const periodLabel = hasCustomDateRange && query.period ? query.period : period.period;
19+
20+
const startDate = hasCustomDateRange
21+
? moment( query.start_date, 'YYYY-MM-DD' ).format( 'L' )
22+
: period.startOf.format( 'L' );
23+
const endDate = hasCustomDateRange
24+
? moment( query.date, 'YYYY-MM-DD' ).format( 'L' )
25+
: period.endOf.format( 'L' );
26+
27+
return [ siteSlug, path, periodLabel, startDate, endDate ].join( '-' ) + '.csv';
28+
}

client/my-sites/stats/stats-download-csv/index.jsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
isRequestingSiteStatsForQuery,
1717
} from 'calypso/state/stats/lists/selectors';
1818
import { getSelectedSiteId } from 'calypso/state/ui/selectors';
19+
import { getStatsCsvFileName } from './get-stats-csv-filename';
1920

2021
import './style.scss';
2122

@@ -53,16 +54,9 @@ class StatsDownloadCsv extends Component {
5354

5455
downloadCsv = ( event ) => {
5556
event.preventDefault();
56-
const { siteSlug, path, period, data, headers } = this.props;
57-
58-
const fileName =
59-
[
60-
siteSlug,
61-
path,
62-
period.period,
63-
period.startOf.format( 'L' ),
64-
period.endOf.format( 'L' ),
65-
].join( '-' ) + '.csv';
57+
const { siteSlug, path, period, query, data, headers } = this.props;
58+
59+
const fileName = getStatsCsvFileName( { siteSlug, path, period, query } );
6660

6761
this.props.recordGoogleEvent( 'Stats', 'CSV Download ' + titlecase( path ) );
6862

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import moment from 'moment';
2+
import { getStatsCsvFileName } from '../get-stats-csv-filename';
3+
4+
describe( 'getStatsCsvFileName', () => {
5+
const siteSlug = 'mercantile.wordpress.org';
6+
const path = 'posts';
7+
8+
beforeAll( () => {
9+
moment.locale( 'en' );
10+
} );
11+
12+
it( 'uses the selected custom date range for multi-month exports (STATS-420)', () => {
13+
// Period object still reflects a single month unit around the start date,
14+
// which previously produced filenames like posts-month-01/01/2026-01/31/2026.
15+
const period = {
16+
period: 'month',
17+
startOf: moment( '2026-01-01' ),
18+
endOf: moment( '2026-01-31' ),
19+
};
20+
const query = {
21+
period: 'day',
22+
start_date: '2026-01-01',
23+
date: '2026-08-06',
24+
summarize: 1,
25+
};
26+
27+
expect( getStatsCsvFileName( { siteSlug, path, period, query } ) ).toBe(
28+
'mercantile.wordpress.org-posts-day-01/01/2026-08/06/2026.csv'
29+
);
30+
} );
31+
32+
it( 'uses the selected custom date range for a single-month export (STATS-420)', () => {
33+
// A day-period unit around the start date previously produced
34+
// posts-day-01/01/2026-01/01/2026 for a Jan 1–31 selection.
35+
const period = {
36+
period: 'day',
37+
startOf: moment( '2026-01-01' ),
38+
endOf: moment( '2026-01-01' ),
39+
};
40+
const query = {
41+
period: 'day',
42+
start_date: '2026-01-01',
43+
date: '2026-01-31',
44+
summarize: 1,
45+
};
46+
47+
expect( getStatsCsvFileName( { siteSlug, path, period, query } ) ).toBe(
48+
'mercantile.wordpress.org-posts-day-01/01/2026-01/31/2026.csv'
49+
);
50+
} );
51+
52+
it( 'falls back to period bounds when no custom date range is present', () => {
53+
const period = {
54+
period: 'month',
55+
startOf: moment( '2026-01-01' ),
56+
endOf: moment( '2026-01-31' ),
57+
};
58+
const query = {
59+
period: 'month',
60+
date: '2026-01-31',
61+
};
62+
63+
expect( getStatsCsvFileName( { siteSlug, path, period, query } ) ).toBe(
64+
'mercantile.wordpress.org-posts-month-01/01/2026-01/31/2026.csv'
65+
);
66+
} );
67+
68+
it( 'falls back to period bounds when query is omitted', () => {
69+
const period = {
70+
period: 'week',
71+
startOf: moment( '2026-01-05' ),
72+
endOf: moment( '2026-01-11' ),
73+
};
74+
75+
expect( getStatsCsvFileName( { siteSlug, path, period } ) ).toBe(
76+
'mercantile.wordpress.org-posts-week-01/05/2026-01/11/2026.csv'
77+
);
78+
} );
79+
} );

0 commit comments

Comments
 (0)