Commit 6117f7b
fix(spec,service-analytics): carry a percentage measure's SCALE, and report an empty filtered group as zero (objectui#3136) (#4442)
* fix(spec,service-analytics): carry a percentage measure's SCALE on the result column (objectui#3136)
A `%` format string says how to print a number, not what scale it is on, and
the two readings collide at exactly 1 — both "100%" (a 0-1 ratio) and "1%" (one
percentage point). Renderers guessed from the value's magnitude and resolved it
the wrong way, so an SLA rate of full compliance displayed as "1.0%".
The scale was answerable from metadata all along; it just never left the
server. `derived: { op: 'ratio' }` is a 0-1 fraction by definition, and a
measure over a `percent` field has that field's scale. Both are now resolved in
the measure-column enrichment pass, next to the ADR-0053 currency chain that
already walks back to the source field for exactly this kind of display fact.
- `percentScaleOf(field)` (spec/data) — the one rule: a `percent` field stores
a fraction unless it declares `max > 1`, matching what the edit widget
writes. Non-percent fields get no opinion.
- `AnalyticsResult.fields[].percentScale` — 'fraction' | 'whole', absent when
the column is not a percentage. `currency` (emitted since ADR-0053 through a
cast) is declared on the same interface.
- `measureCurrency` → `sourceFieldMeta`, now returning `max`. The old name had
outgrown itself: date bucketing already read `type` through it, and the
percent chain is its third consumer.
- Showcase: a `paid_rate` ratio measure + KPI/table widgets on Revenue Pulse.
Grouping by status pins the Paid bucket at exactly 1 — the repro value.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(spec,service-analytics): an empty filtered group is a measured zero, not missing data (objectui#3136)
A measure-scoped filter can exclude every row of a group the grid still lists,
and the database reports that by omitting the group from the supplementary
result — after the merge, indistinguishable from "never measured". For a COUNT
or a SUM it IS measured: the answer is 0.
So "0 of 12 paid" rendered as a blank cell and every ratio built on it went
null — a compliance dashboard silently dropping the row it exists to show. On
the showcase's Paid-Rate table the Sent bucket read "—/—" where the truth is
"0 / 0.0%".
- `emptyGroupValueFor(aggregate)` (spec/data/aggregation-policy) states which
aggregates have an identity over the empty set. avg/min/max keep their null:
there is nothing to average, and a zero there would invent a measurement.
- `queryDataset` fills it in after ALL supplementary merges, not inside the
loop — a later measure's merge can append rows for dimension keys no earlier
query saw, and those rows need the same fill.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(spec): regenerate the api-surface baseline for the percent-scale exports (#4523)
Regenerates `packages/spec/api-surface.json` so the committed baseline records
the four `./data` exports the percent-scale chain adds — `PercentScale`,
`PercentScaleFieldMeta`, `percentScaleOf` and `emptyGroupValueFor` — plus an
empty-frontmatter changeset declaring that this releases nothing.
The api-surface gate is a snapshot check: it fails whenever the built public
surface and the committed list disagree, in either direction, which is what
makes an unintended public-API change impossible to land silently. #4442 added
the exports without refreshing the baseline, so `check:api-surface` reported 4
unrecorded additions and failed the TypeScript Type Check job. Nothing was
removed or narrowed — 0 breaking.
The changeset is empty on purpose: `api-surface.json` is a build-time snapshot,
not shipped code, and the exports it records already ship under
`dataset-percent-scale-chain.md` on this branch. A bump here would double-count
that release.
* fix(showcase): translate the two percent-scale widget titles at birth
`check-i18n-coverage` failed the TypeScript Type Check job: showcase's
untranslated declared strings grew 452 → 454. The two new strings are the
Revenue Pulse widgets this branch adds — `kpi_paid_rate` ("Paid Rate") and
`table_rate_by_status` ("Paid Rate by Status") — declared with English
titles and no zh-CN, a locale the example claims to support.
This defect was always here; it was simply unreachable. The job used to die
one step earlier on `check:api-surface`, so it never got as far as the i18n
gate. Fixing the baseline uncovered it.
Translated rather than baselined. The ratchet tolerates the debt that
predates it and refuses growth, so the honest remedy for a string this
branch introduces is to translate it — and the convention is already
written down two lines above, where `showcase_chart_gallery`'s newest widget
is translated at birth while its older siblings stay frozen. Revenue Pulse's
other eight widget titles predate the ratchet the same way and are left
alone; cleaning those up is not this branch's business.
Terminology follows the existing zh-CN invoice bundle (发票 / 状态 / 付款).
Count returns to exactly 452, so `scripts/i18n-coverage-baseline.json` needs
no edit — no ratchet-down, no baseline churn.
Verified: `check-i18n-coverage` OK (12 configs, none new); `check-i18n-bundles`
all in sync; showcase `tsc --noEmit` clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S9TVFwXGsXqR3SD5e2qAU8
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: os-zhuang <jack@objectstack.ai>1 parent 435415e commit 6117f7b
17 files changed
Lines changed: 394 additions & 22 deletions
File tree
- .changeset
- examples/app-showcase/src
- system/translations
- ui
- dashboards
- datasets
- packages
- services/service-analytics/src
- __tests__
- spec
- src
- contracts
- data
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
160 | 160 | | |
161 | 161 | | |
162 | 162 | | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
163 | 169 | | |
164 | 170 | | |
165 | 171 | | |
| |||
376 | 382 | | |
377 | 383 | | |
378 | 384 | | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
379 | 395 | | |
380 | 396 | | |
381 | 397 | | |
Lines changed: 9 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
67 | 76 | | |
68 | 77 | | |
Lines changed: 12 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
27 | 39 | | |
28 | 40 | | |
29 | 41 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
58 | 58 | | |
59 | 59 | | |
60 | 60 | | |
61 | | - | |
| 61 | + | |
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
| |||
Lines changed: 93 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
123 | 123 | | |
124 | 124 | | |
125 | 125 | | |
126 | | - | |
| 126 | + | |
127 | 127 | | |
128 | 128 | | |
129 | 129 | | |
130 | 130 | | |
131 | | - | |
| 131 | + | |
132 | 132 | | |
133 | 133 | | |
134 | 134 | | |
| |||
161 | 161 | | |
162 | 162 | | |
163 | 163 | | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
164 | 253 | | |
165 | 254 | | |
166 | 255 | | |
| |||
259 | 348 | | |
260 | 349 | | |
261 | 350 | | |
262 | | - | |
| 351 | + | |
263 | 352 | | |
264 | 353 | | |
265 | 354 | | |
| |||
282 | 371 | | |
283 | 372 | | |
284 | 373 | | |
285 | | - | |
| 374 | + | |
286 | 375 | | |
287 | 376 | | |
288 | 377 | | |
| |||
0 commit comments