mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
docs(insights): pin the median window contract across the summary paths
DataSummary.median carries two windows under one field name: the SQL summaries-slice path windows PERCENTILE_CONT to the trailing 90 days, while the JS summarize() takes the median over the full series it is handed (a caller-defined window). No user-visible bug today — the stat strip only reads the 90-day SQL path — but a future consumer reading summary.median off a summarize()-derived payload would silently get a different number. Document the window on the type and at both call sites so the divergence is explicit; the percentile algorithm matches, only the window differs.
This commit is contained in:
1 parent
146867b09f
commit
0b10932c53
2 files changed
+27
-6
No files matched your search
@@ -117,6 +117,12 @@ interface NarrowAggregateRow {
|
||||
* sorting every raw row per type, which the rollup tier cannot
|
||||
* compose and which the perf posture (v1.8.3 anti-freeze) forbids on
|
||||
* the read path. Null when the window holds no rows.
|
||||
*
|
||||
* NOTE: this window (trailing 90 days) is the contract for the stat
|
||||
* strip's `DataSummary.median`. The JS `summarize()` in `trends.ts`
|
||||
* computes its median over the *full* series it is handed (a
|
||||
* caller-defined window), so the two share a field name but not a
|
||||
* window — see the `median` doc on `DataSummary`.
|
||||
*/
|
||||
median: number | null;
|
||||
avg30_last_month: number | null;
|
||||
|
||||
@@ -125,6 +125,17 @@ export interface DataSummary {
|
||||
* the insights category-page stat strip alongside min / max / mean so
|
||||
* a skewed series (a few very high or very low readings dragging the
|
||||
* mean) reads honestly. Null when the series is empty.
|
||||
*
|
||||
* Window: caller-defined. `summarize()` computes the median over the
|
||||
* full `DataPoint[]` it is handed, so the window is whatever the caller
|
||||
* passed (full history for `/api/measurements/series`, per-day means
|
||||
* for the snapshot builder, …). It is NOT guaranteed to be the trailing
|
||||
* 90-day window. The insights stat strip does not read this value — its
|
||||
* median comes from the SQL `summaries-slice` path, which fixes the
|
||||
* window to the trailing 90 days (`PERCENTILE_CONT(0.5) … FILTER (WHERE
|
||||
* measured_at >= NOW() - INTERVAL '90 days')`). A consumer comparing the
|
||||
* two must account for the differing windows. The percentile *algorithm*
|
||||
* matches (linear-interpolated midpoint), only the window differs.
|
||||
*/
|
||||
median: number | null;
|
||||
avg7: number | null;
|
||||
@@ -192,12 +203,16 @@ export function summarize(data: DataPoint[]): DataSummary {
|
||||
}
|
||||
const mean = sum / values.length;
|
||||
|
||||
// v1.8.5 — median (50th percentile) over the full series. A separate
|
||||
// numeric sort from the date-ordered `sorted` array above so the
|
||||
// percentile is taken over values, not timestamps. Linear-interpolated
|
||||
// midpoint on an even-length series (the standard PERCENTILE_CONT
|
||||
// definition) so this matches the SQL `PERCENTILE_CONT(0.5)` the
|
||||
// rollup-tier slim slice emits.
|
||||
// v1.8.5 — median (50th percentile) over the FULL series handed to
|
||||
// this function (the window is caller-defined; see the `median` doc on
|
||||
// DataSummary). A separate numeric sort from the date-ordered `sorted`
|
||||
// array above so the percentile is taken over values, not timestamps.
|
||||
// Linear-interpolated midpoint on an even-length series (the standard
|
||||
// PERCENTILE_CONT definition) so the *algorithm* matches the SQL
|
||||
// `PERCENTILE_CONT(0.5)` the rollup-tier slim slice emits — but note
|
||||
// the SQL path windows to the trailing 90 days while this path does
|
||||
// not, so the two are byte-comparable only when the caller already
|
||||
// passed a 90-day series.
|
||||
const valueSorted = [...values].sort((a, b) => a - b);
|
||||
const mid = Math.floor(valueSorted.length / 2);
|
||||
const median =
|
||||
|
||||
Reference in new issue
Block a user