Commit a2047ac
CMM-2152: Fix Views card stat accuracy and halve its network calls (#23101)
* Align bottom-row and chart previous windows for month-unit periods
For Last6Months/Last12Months the chart derived its previous window by
whole-month subtraction while the bottom row used an exact day-span
mirror, so the header and the bottom row showed contradictory Views %
change on the same card. Share the chart's config-based previous-window
rule so both fetches compare against identical windows.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Stop bottom-row stats call from truncating the first bucket
The dedicated bottom-stats call passed a mid-bucket startDate, so for
month/year units the API truncated the first bucket's views (additive)
while leaving visitors (unique) at the full-bucket value. The bottom
row's Views therefore under-counted and disagreed with the chart header.
Mirror the chart's fetch (unit + quantity + endDate, no startDate) so the
API returns full buckets and the totals match.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Align Today chart hourly window to the calendar day
The hourly window ended at the next day's 00:00, so the API returned
01:00→00:00 buckets — dropping today's 00:00 hour and under-counting the
chart total versus the day-level bottom row. End the window at
"<day> 23:00:00" instead so its 24 buckets cover 00:00–23:00 of the
actual day (and yesterday for the previous window).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Fill the Views card bottom row from the chart call for multi-day periods
The chart's stats/visits call already returns visitors/likes/comments/posts
per bucket, so request those fields and reuse the same response to populate
the bottom row instead of a separate dedicated call — halving the card's
network calls (2 instead of 4) for every period except single-day ones.
Single-day periods (Today, single-day Custom) render an hourly chart whose
response only carries views, so they keep fetching the bottom row from a
dedicated day-level call, matching what the web app does.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Route Custom periods to the dedicated bottom call for correct visitor de-dup
The Views card filled the bottom row from the chart response for every
multi-day period. That over-counts unique Visitors for a Custom range
beyond two years: the chart uses MONTH buckets, so a visitor active
across several months is counted once per month, whereas the bottom row
should de-duplicate per year. Views/Likes/Comments/Posts are additive
and unaffected.
Fill the bottom row from the chart only for the fixed periods
(Last7Days/Last30Days/Last6Months/Last12Months), whose chart bucket
already matches the unit the bottom row needs. Today and all Custom
ranges now fetch the bottom row from the dedicated call, which coarsens
to YEAR beyond two years so visitors are de-duplicated per year.
This also drops the now-unreachable fixed-period branches from
calculateBottomStatsRange (they no longer call it) and replaces
isSingleDayPeriod with an exhaustive fillsBottomFromChart predicate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Coarsen the Custom chart to YEAR beyond two years to match the bottom row
For a Custom range over two years the chart used MONTH buckets while the
bottom-row totals coarsen to YEAR, so the header's Views (chart) and the
bottom row's Views (dedicated call) covered different, misaligned windows
and could disagree on the same card. Pass allowYear = true for the custom
chart window too, so the chart and the bottom row share one unit (YEAR):
their Views totals — and their per-year visitor de-duplication — now always
agree. Shorter ranges (day/month) are unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Count calendar buckets and fix the hourly window for the Today's card
Three fixes from review of this branch.
unitAndQuantityFor derived its quantity from ChronoUnit.MONTHS/YEARS.between,
which count whole units elapsed between the endpoints rather than the calendar
buckets the window spans. The API returns `quantity` buckets ending at the
bucket holding the end date, so whenever the end's day-of-month fell before the
start's, the window's first bucket was silently dropped: Jan 15..Mar 5 elapses
one whole month but spans three, so the chart and the bottom row both fetched
Feb+Mar and lost January. Count spanned buckets instead. Both the chart and the
bottom-row range share this function, so they kept agreeing with each other
while under-counting; they now agree on the right total.
fetchHourlyViews still applied the "endDate is exclusive for hourly queries"
workaround that a9ac20a disproved, leaving the Today's Stats card with the same
off-by-one hour the Views card just fixed: it asked for the next day at 00:00,
dropping the day's own 00:00 bucket in favour of the following day's. Route it
through formatApiEndDate so both callers model the API identically.
Finally, fetchBottomStats's KDoc still described the routing as it stood at
80551d5, before 5bd3e0e sent every Custom range to the dedicated call.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Fill the Views card bottom row from the chart for multi-day Custom ranges
fillsBottomFromChart excluded every Custom range on the grounds that a range
beyond two years needs yearly visitor de-duplication the chart's monthly buckets
can't give. a4dc42f invalidated that: the custom chart window now coarsens to
YEAR past two years, so for any multi-day Custom range the chart and the bottom
row derive their unit, quantity and windows from the very same rules and issue
byte-identical requests. Those periods were paying for two calls that duplicated
the chart's own — the exact redundancy this branch set out to remove, on the one
period that never got it.
Route multi-day Custom through the chart response like the fixed periods, and
keep only the single-day periods (Today, a Custom range whose start and end are
the same day) on the dedicated call: their chart is hourly, and an hourly
response populates `views` alone.
This leaves calculateBottomStatsRange's coarser units unreachable from the card,
since only single-day windows now reach it. The span-based rule stays anyway, so
a caller that does need a standalone total for a longer window gets a correct one
rather than a silently wrong bucket; the KDoc records that.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Send start_date for YEAR windows and give each window its own quantity
A Custom range over two years renders every number on the Views card
wrong -- chart, header total, % change and bottom row alike. For the
range 2024-01-01..2026-06-29 the app already computed unit=year,
quantity=3 and date=2026-06-29, identical to the web app, but omitted
`start_date`: without it the API does not anchor its year buckets to the
requested window. YEAR windows now send their own real start, matching
web's unit=year&date=<end>&start_date=<start>&quantity=<n>.
DAY and MONTH windows still send none. They are correct from
unit + quantity + endDate, and a mid-bucket start_date there truncates
the first bucket's views (additive) while leaving visitors (a per-bucket
unique) at the full-bucket value. That asymmetry no longer splits the
header from the bottom row -- both read one response now -- but it is
still a reason not to send a start_date where nothing needs one.
Sending start_date forces a second fix: a window's quantity and its
start_date must describe the same window, and fetchStatsForPeriod sent
the *current* window's quantity on the *previous* request. The previous
window mirrors the current one's day span, which always resolves to the
same unit but not always to the same bucket count -- 2023-12-31..
2026-01-01 spans four years while its mirror spans three, so the API
returned 2020-2023 and folded an extra year into the previous total.
PeriodDateRange now carries a quantity per window, derived from that
window's own span. Only Custom ranges coarsened to MONTH or YEAR change
behaviour; every other period's two windows span equal buckets by
construction, and tests pin start_date to null on the fixed periods so
the YEAR rule cannot leak into paths that already work.
Also drops the now-dead `allowYear` parameter and refreshes the KDocs
the change falsified.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Label YEAR chart buckets with the year instead of the month
A Custom range over two years is charted in YEAR buckets, but its axis
labels rendered as "Jan, Jan, Jan" and its legend as "Jan - Jun".
The repository already picks StatsUnit.YEAR correctly and then dropped
the value at buildPeriodStatsSuccess, so the ViewModel re-derived
granularity from a day-count threshold that only distinguishes day from
month: anything past 31 days got "MMM". The period string could not
disambiguate either, since the API returns a full ISO date for DAY,
MONTH and YEAR buckets alike.
Carry the unit through PeriodStatsResult.Success so labels and the
legend format from the granularity actually requested rather than
guessing it a second time. This avoids duplicating the repository's
two-year threshold into the ViewModel, which is how the two sides drifted
apart to begin with. The period string still decides how to parse a
bucket, as the API returns both "yyyy-MM" and "yyyy-MM-dd" shapes.
DAY, MONTH and HOUR output is unchanged; only YEAR differs. Adds the
first coverage for chart labels, including locale-independent locks that
the year pattern cannot leak into the other units.
Drill-down still makes the same day-count guess, so tapping a year bar
opens that January. Left as a follow-up: it changes an interaction.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Simplify Views stats card cleanup and satisfy detekt
Reuse ShimmerBox for the loading placeholders, dedup the previous-window
mirror math, collapse the copy-paste bottom-stats aggregate mapping, and
drop the redundant type-check in onChartTypeChanged. Convert
formatDataPointLabel to an expression body to stay under the ReturnCount
limit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>1 parent 49e53b5 commit a2047ac
5 files changed
Lines changed: 856 additions & 245 deletions
File tree
- WordPress/src
- main/java/org/wordpress/android/ui/newstats
- repository
- viewsstats
- test/java/org/wordpress/android/ui/newstats
- repository
- viewsstats
0 commit comments