@uptimizr/db 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/duckdb/events.d.ts.map +1 -1
- package/dist/duckdb/events.js +5 -0
- package/dist/duckdb/events.js.map +1 -1
- package/dist/duckdb/migrations.d.ts.map +1 -1
- package/dist/duckdb/migrations.js +24 -0
- package/dist/duckdb/migrations.js.map +1 -1
- package/dist/events.d.ts +3 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +7 -0
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/parity/cases.d.ts.map +1 -1
- package/dist/parity/cases.js +58 -8
- package/dist/parity/cases.js.map +1 -1
- package/dist/parity/fixtures.d.ts.map +1 -1
- package/dist/parity/fixtures.js +7 -0
- package/dist/parity/fixtures.js.map +1 -1
- package/dist/query/aggregations.d.ts +50 -14
- package/dist/query/aggregations.d.ts.map +1 -1
- package/dist/query/aggregations.js +182 -38
- package/dist/query/aggregations.js.map +1 -1
- package/dist/query/dialect.d.ts +14 -1
- package/dist/query/dialect.d.ts.map +1 -1
- package/dist/query/dialect.js +23 -0
- package/dist/query/dialect.js.map +1 -1
- package/dist/query/types.d.ts +32 -0
- package/dist/query/types.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -10,7 +10,16 @@
|
|
|
10
10
|
* optional scene/source/session dimensions only.
|
|
11
11
|
*/
|
|
12
12
|
import { type Dialect } from "./dialect.js";
|
|
13
|
-
import type { QuerySpec, CameraModeOptions, FunnelOptions, RangeOptions, SceneOptions, SessionOptions, SourceOptions, TimeseriesOptions } from "./types.js";
|
|
13
|
+
import type { QuerySpec, CameraModeOptions, FunnelOptions, RangeOptions, RegionOptions, SceneOptions, SessionOptions, SourceOptions, TimeseriesOptions, WorldAabb } from "./types.js";
|
|
14
|
+
/**
|
|
15
|
+
* Bounds-driven default voxel size (ADR 0040 §1). Picks a `cellSize` so the
|
|
16
|
+
* longest axis of `bounds` spans roughly `targetCells` cells, keeping spatial
|
|
17
|
+
* resolution proportional to scene extent instead of a fixed world-unit default
|
|
18
|
+
* that dissolves large scenes into a few coarse blocks. Returns `null` for a
|
|
19
|
+
* missing or degenerate (zero/negative longest-axis) box so the caller can fall
|
|
20
|
+
* back to its fixed default.
|
|
21
|
+
*/
|
|
22
|
+
export declare function defaultCellSizeForBounds(bounds: WorldAabb | null | undefined, targetCells?: number): number | null;
|
|
14
23
|
/** List sessions for a project with event counts and time bounds. */
|
|
15
24
|
export declare function buildListSessions(projectId: string, opts: RangeOptions & CameraModeOptions & {
|
|
16
25
|
limit?: number;
|
|
@@ -27,10 +36,20 @@ export declare function buildPointerHeatmap(projectId: string, opts: RangeOption
|
|
|
27
36
|
* events into a uniform grid of `cellSize`-sized cubes. Results are capped to the
|
|
28
37
|
* busiest `limit` voxels.
|
|
29
38
|
*/
|
|
30
|
-
export declare function buildWorldHeatmap(projectId: string, opts: RangeOptions & SceneOptions & SourceOptions & CameraModeOptions & {
|
|
39
|
+
export declare function buildWorldHeatmap(projectId: string, opts: RangeOptions & SceneOptions & SourceOptions & RegionOptions & CameraModeOptions & {
|
|
31
40
|
cellSize?: number;
|
|
32
41
|
limit?: number;
|
|
33
42
|
}, d: Dialect): QuerySpec;
|
|
43
|
+
/**
|
|
44
|
+
* Scene-wide totals for the world (pointer) heatmap (ADR 0040 §3): the true count
|
|
45
|
+
* of occupied voxels and total hits, computed with no `LIMIT` so the viewer can
|
|
46
|
+
* report "showing top N of M cells" and reason about cold spots/coverage. Shares
|
|
47
|
+
* every filter (including {@link RegionOptions.region}) with {@link buildWorldHeatmap}.
|
|
48
|
+
* Uses a grouped sub-select so the dialect needs no `COUNT(DISTINCT tuple)`.
|
|
49
|
+
*/
|
|
50
|
+
export declare function buildWorldHeatmapStats(projectId: string, opts: RangeOptions & SceneOptions & SourceOptions & RegionOptions & CameraModeOptions & {
|
|
51
|
+
cellSize?: number;
|
|
52
|
+
}, d: Dialect): QuerySpec;
|
|
34
53
|
/**
|
|
35
54
|
* World-space (3D) gaze heatmap (ADR 0030): voxel-bin the camera-pose gaze
|
|
36
55
|
* surface hits (`camera_sample.hitPoint`) into the same uniform grid as the
|
|
@@ -41,10 +60,18 @@ export declare function buildWorldHeatmap(projectId: string, opts: RangeOptions
|
|
|
41
60
|
* the busiest `limit` voxels; an optional `session` scopes it to one visit
|
|
42
61
|
* (ADR 0010 §1a). Gaze has no pointer input-source, so there is no `source` filter.
|
|
43
62
|
*/
|
|
44
|
-
export declare function buildGazeHeatmap(projectId: string, opts: RangeOptions & SceneOptions & SessionOptions & CameraModeOptions & {
|
|
63
|
+
export declare function buildGazeHeatmap(projectId: string, opts: RangeOptions & SceneOptions & SessionOptions & RegionOptions & CameraModeOptions & {
|
|
45
64
|
cellSize?: number;
|
|
46
65
|
limit?: number;
|
|
47
66
|
}, d: Dialect): QuerySpec;
|
|
67
|
+
/**
|
|
68
|
+
* Scene-wide totals for the gaze heatmap (ADR 0040 §3): the true occupied-voxel
|
|
69
|
+
* and hit counts behind the truncated top-N gaze voxels, with no `LIMIT`. Shares
|
|
70
|
+
* every filter (including {@link RegionOptions.region}) with {@link buildGazeHeatmap}.
|
|
71
|
+
*/
|
|
72
|
+
export declare function buildGazeHeatmapStats(projectId: string, opts: RangeOptions & SceneOptions & SessionOptions & RegionOptions & CameraModeOptions & {
|
|
73
|
+
cellSize?: number;
|
|
74
|
+
}, d: Dialect): QuerySpec;
|
|
48
75
|
/**
|
|
49
76
|
* View-direction heatmap: bin camera forward vectors by spherical angles.
|
|
50
77
|
* `azimuth = atan2(z, x)`, `elevation = asin(y / |v|)`, each bucketed into `bins`.
|
|
@@ -58,7 +85,7 @@ export declare function buildCameraDirectionHeatmap(projectId: string, opts: Ran
|
|
|
58
85
|
* mean height per cell. For first-person (`cameraType: "free"`) sessions this is
|
|
59
86
|
* the "where do visitors walk / dwell" map; capped to the busiest `limit` cells.
|
|
60
87
|
*/
|
|
61
|
-
export declare function buildCameraPositionHeatmap(projectId: string, opts: RangeOptions & SceneOptions & SessionOptions & CameraModeOptions & {
|
|
88
|
+
export declare function buildCameraPositionHeatmap(projectId: string, opts: RangeOptions & SceneOptions & SessionOptions & RegionOptions & CameraModeOptions & {
|
|
62
89
|
cellSize?: number;
|
|
63
90
|
limit?: number;
|
|
64
91
|
}, d: Dialect): QuerySpec;
|
|
@@ -96,11 +123,18 @@ export declare function buildAggregateTrajectories(projectId: string, opts: Rang
|
|
|
96
123
|
* controllers, hands, gaze) carry their own world-space pointing ray, so when a
|
|
97
124
|
* click has a `ray.origin` we use it verbatim — the controller/hand/gaze is the
|
|
98
125
|
* true pointing origin, not the headset/camera. Flat pointers (mouse, touch,
|
|
99
|
-
* stylus) have no native ray
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
126
|
+
* stylus) have no native ray; when the ASOF-joined `camera_sample` carries the
|
|
127
|
+
* projection intrinsics (`fov`/`aspect`/`near`, #22, ADR 0043) we unproject the
|
|
128
|
+
* click's normalized `screen[x, y]` onto the camera **near plane** and use that
|
|
129
|
+
* point as the origin, so flat-pointer rays fan out across the near plane the way
|
|
130
|
+
* the clicks were actually made. The near-plane basis assumes the canonical
|
|
131
|
+
* world-up `(0, 1, 0)` and no camera roll (true for mouse/touch); a degenerate
|
|
132
|
+
* look-straight-up/down view (or any missing/zero intrinsic, e.g. legacy data)
|
|
133
|
+
* falls back to the nearest preceding `camera_sample` **position** — the
|
|
134
|
+
* historical view-gated behavior. The camera join is therefore a LEFT join so
|
|
135
|
+
* pose clicks survive even in sessions that never emit a `camera_sample`; rows
|
|
136
|
+
* with neither a ray, a reconstructable near-plane point, nor a camera origin are
|
|
137
|
+
* dropped.
|
|
104
138
|
*/
|
|
105
139
|
export declare function buildClickGazeRay(projectId: string, opts: RangeOptions & SceneOptions & SourceOptions & SessionOptions & {
|
|
106
140
|
cellSize?: number;
|
|
@@ -328,11 +362,13 @@ export declare function buildJankRate(projectId: string, opts: RangeOptions & Sc
|
|
|
328
362
|
/**
|
|
329
363
|
* FPS segmented by device class, computed **per-session then aggregated** (ADR
|
|
330
364
|
* 0028 §2). Each session's median FPS is attributed to the graphics backend,
|
|
331
|
-
* mobile flag, and GPU `renderer` recorded in its `session_start.device` block
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
365
|
+
* mobile flag, and GPU `renderer` recorded in its `session_start.device` block,
|
|
366
|
+
* plus the coarse `browser`/`os` families derived server-side from the
|
|
367
|
+
* User-Agent at ingestion (ADR 0041) — all data already on the wire, so there is
|
|
368
|
+
* no SDK or schema change. Device fields are read from the `session_start`
|
|
369
|
+
* payload JSON (they are not promoted columns) and `coalesce`d to `''` when a
|
|
370
|
+
* session never reported them. `p50_fps` is the median across sessions in the
|
|
371
|
+
* group of each session's median FPS.
|
|
336
372
|
*/
|
|
337
373
|
export declare function buildPerfByDevice(projectId: string, opts: RangeOptions & SceneOptions & SessionOptions, d: Dialect): QuerySpec;
|
|
338
374
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aggregations.d.ts","sourceRoot":"","sources":["../../src/query/aggregations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"aggregations.d.ts","sourceRoot":"","sources":["../../src/query/aggregations.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,28 @@
|
|
|
9
9
|
* Invariant: no multi-tenant concepts here. Filtering is by `project_id` and the
|
|
10
10
|
* optional scene/source/session dimensions only.
|
|
11
11
|
*/
|
|
12
|
-
import { ParamBag, cameraModeClause, dayRangeClause, rangeClause, sceneClause, sessionClause, sourceClause, } from "./dialect.js";
|
|
12
|
+
import { ParamBag, cameraModeClause, dayRangeClause, rangeClause, regionClause, sceneClause, sessionClause, sourceClause, } from "./dialect.js";
|
|
13
|
+
/** World/gaze voxel coordinates derive from the raycast `hit_point` vector. */
|
|
14
|
+
const HIT_POINT_COLS = { x: "hit_point[1]", y: "hit_point[2]", z: "hit_point[3]" };
|
|
15
|
+
/** Floor-plan cells derive from the camera `position` vector (Y is height). */
|
|
16
|
+
const POSITION_COLS = { x: "position[1]", y: "position[2]", z: "position[3]" };
|
|
17
|
+
/**
|
|
18
|
+
* Bounds-driven default voxel size (ADR 0040 §1). Picks a `cellSize` so the
|
|
19
|
+
* longest axis of `bounds` spans roughly `targetCells` cells, keeping spatial
|
|
20
|
+
* resolution proportional to scene extent instead of a fixed world-unit default
|
|
21
|
+
* that dissolves large scenes into a few coarse blocks. Returns `null` for a
|
|
22
|
+
* missing or degenerate (zero/negative longest-axis) box so the caller can fall
|
|
23
|
+
* back to its fixed default.
|
|
24
|
+
*/
|
|
25
|
+
export function defaultCellSizeForBounds(bounds, targetCells = 64) {
|
|
26
|
+
if (bounds == null)
|
|
27
|
+
return null;
|
|
28
|
+
const [minX, minY, minZ, maxX, maxY, maxZ] = bounds;
|
|
29
|
+
const longest = Math.max(maxX - minX, maxY - minY, maxZ - minZ);
|
|
30
|
+
if (!Number.isFinite(longest) || longest <= 0 || targetCells <= 0)
|
|
31
|
+
return null;
|
|
32
|
+
return longest / targetCells;
|
|
33
|
+
}
|
|
13
34
|
/** List sessions for a project with event counts and time bounds. */
|
|
14
35
|
export function buildListSessions(projectId, opts, d) {
|
|
15
36
|
const bag = new ParamBag(d);
|
|
@@ -76,6 +97,7 @@ export function buildWorldHeatmap(projectId, opts, d) {
|
|
|
76
97
|
const scene = sceneClause(bag, opts);
|
|
77
98
|
const source = sourceClause(bag, opts);
|
|
78
99
|
const cameraMode = cameraModeClause(bag, d, projectId, opts);
|
|
100
|
+
const region = regionClause(bag, opts, HIT_POINT_COLS);
|
|
79
101
|
const limit = bag.add("limit", "u32", opts.limit ?? 1000);
|
|
80
102
|
return {
|
|
81
103
|
query: `
|
|
@@ -87,7 +109,7 @@ export function buildWorldHeatmap(projectId, opts, d) {
|
|
|
87
109
|
FROM events
|
|
88
110
|
WHERE project_id = ${pid}
|
|
89
111
|
AND event_type IN ('pointer_move', 'pointer_click')
|
|
90
|
-
AND length(hit_point) = 3${range}${scene}${source}${cameraMode}
|
|
112
|
+
AND length(hit_point) = 3${range}${scene}${source}${cameraMode}${region}
|
|
91
113
|
GROUP BY vx, vy, vz
|
|
92
114
|
ORDER BY count DESC
|
|
93
115
|
LIMIT ${limit}
|
|
@@ -95,6 +117,41 @@ export function buildWorldHeatmap(projectId, opts, d) {
|
|
|
95
117
|
query_params: bag.values,
|
|
96
118
|
};
|
|
97
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Scene-wide totals for the world (pointer) heatmap (ADR 0040 §3): the true count
|
|
122
|
+
* of occupied voxels and total hits, computed with no `LIMIT` so the viewer can
|
|
123
|
+
* report "showing top N of M cells" and reason about cold spots/coverage. Shares
|
|
124
|
+
* every filter (including {@link RegionOptions.region}) with {@link buildWorldHeatmap}.
|
|
125
|
+
* Uses a grouped sub-select so the dialect needs no `COUNT(DISTINCT tuple)`.
|
|
126
|
+
*/
|
|
127
|
+
export function buildWorldHeatmapStats(projectId, opts, d) {
|
|
128
|
+
const bag = new ParamBag(d);
|
|
129
|
+
const pid = bag.add("projectId", "string", projectId);
|
|
130
|
+
const cellSize = bag.add("cellSize", "f64", opts.cellSize ?? 0.5);
|
|
131
|
+
const range = rangeClause(bag, opts);
|
|
132
|
+
const scene = sceneClause(bag, opts);
|
|
133
|
+
const source = sourceClause(bag, opts);
|
|
134
|
+
const cameraMode = cameraModeClause(bag, d, projectId, opts);
|
|
135
|
+
const region = regionClause(bag, opts, HIT_POINT_COLS);
|
|
136
|
+
return {
|
|
137
|
+
query: `
|
|
138
|
+
SELECT count() AS cells, coalesce(sum(c), 0) AS hits
|
|
139
|
+
FROM (
|
|
140
|
+
SELECT
|
|
141
|
+
floor(hit_point[1] / ${cellSize}) AS vx,
|
|
142
|
+
floor(hit_point[2] / ${cellSize}) AS vy,
|
|
143
|
+
floor(hit_point[3] / ${cellSize}) AS vz,
|
|
144
|
+
count() AS c
|
|
145
|
+
FROM events
|
|
146
|
+
WHERE project_id = ${pid}
|
|
147
|
+
AND event_type IN ('pointer_move', 'pointer_click')
|
|
148
|
+
AND length(hit_point) = 3${range}${scene}${source}${cameraMode}${region}
|
|
149
|
+
GROUP BY vx, vy, vz
|
|
150
|
+
) t
|
|
151
|
+
`,
|
|
152
|
+
query_params: bag.values,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
98
155
|
/**
|
|
99
156
|
* World-space (3D) gaze heatmap (ADR 0030): voxel-bin the camera-pose gaze
|
|
100
157
|
* surface hits (`camera_sample.hitPoint`) into the same uniform grid as the
|
|
@@ -113,6 +170,7 @@ export function buildGazeHeatmap(projectId, opts, d) {
|
|
|
113
170
|
const scene = sceneClause(bag, opts);
|
|
114
171
|
const session = sessionClause(bag, opts);
|
|
115
172
|
const cameraMode = cameraModeClause(bag, d, projectId, opts);
|
|
173
|
+
const region = regionClause(bag, opts, HIT_POINT_COLS);
|
|
116
174
|
const limit = bag.add("limit", "u32", opts.limit ?? 1000);
|
|
117
175
|
return {
|
|
118
176
|
query: `
|
|
@@ -124,7 +182,7 @@ export function buildGazeHeatmap(projectId, opts, d) {
|
|
|
124
182
|
FROM events
|
|
125
183
|
WHERE project_id = ${pid}
|
|
126
184
|
AND event_type = 'camera_sample'
|
|
127
|
-
AND length(hit_point) = 3${range}${scene}${session}${cameraMode}
|
|
185
|
+
AND length(hit_point) = 3${range}${scene}${session}${cameraMode}${region}
|
|
128
186
|
GROUP BY vx, vy, vz
|
|
129
187
|
ORDER BY count DESC
|
|
130
188
|
LIMIT ${limit}
|
|
@@ -132,6 +190,39 @@ export function buildGazeHeatmap(projectId, opts, d) {
|
|
|
132
190
|
query_params: bag.values,
|
|
133
191
|
};
|
|
134
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Scene-wide totals for the gaze heatmap (ADR 0040 §3): the true occupied-voxel
|
|
195
|
+
* and hit counts behind the truncated top-N gaze voxels, with no `LIMIT`. Shares
|
|
196
|
+
* every filter (including {@link RegionOptions.region}) with {@link buildGazeHeatmap}.
|
|
197
|
+
*/
|
|
198
|
+
export function buildGazeHeatmapStats(projectId, opts, d) {
|
|
199
|
+
const bag = new ParamBag(d);
|
|
200
|
+
const pid = bag.add("projectId", "string", projectId);
|
|
201
|
+
const cellSize = bag.add("cellSize", "f64", opts.cellSize ?? 0.5);
|
|
202
|
+
const range = rangeClause(bag, opts);
|
|
203
|
+
const scene = sceneClause(bag, opts);
|
|
204
|
+
const session = sessionClause(bag, opts);
|
|
205
|
+
const cameraMode = cameraModeClause(bag, d, projectId, opts);
|
|
206
|
+
const region = regionClause(bag, opts, HIT_POINT_COLS);
|
|
207
|
+
return {
|
|
208
|
+
query: `
|
|
209
|
+
SELECT count() AS cells, coalesce(sum(c), 0) AS hits
|
|
210
|
+
FROM (
|
|
211
|
+
SELECT
|
|
212
|
+
floor(hit_point[1] / ${cellSize}) AS vx,
|
|
213
|
+
floor(hit_point[2] / ${cellSize}) AS vy,
|
|
214
|
+
floor(hit_point[3] / ${cellSize}) AS vz,
|
|
215
|
+
count() AS c
|
|
216
|
+
FROM events
|
|
217
|
+
WHERE project_id = ${pid}
|
|
218
|
+
AND event_type = 'camera_sample'
|
|
219
|
+
AND length(hit_point) = 3${range}${scene}${session}${cameraMode}${region}
|
|
220
|
+
GROUP BY vx, vy, vz
|
|
221
|
+
) t
|
|
222
|
+
`,
|
|
223
|
+
query_params: bag.values,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
135
226
|
/**
|
|
136
227
|
* View-direction heatmap: bin camera forward vectors by spherical angles.
|
|
137
228
|
* `azimuth = atan2(z, x)`, `elevation = asin(y / |v|)`, each bucketed into `bins`.
|
|
@@ -174,6 +265,7 @@ export function buildCameraPositionHeatmap(projectId, opts, d) {
|
|
|
174
265
|
const scene = sceneClause(bag, opts);
|
|
175
266
|
const session = sessionClause(bag, opts);
|
|
176
267
|
const cameraMode = cameraModeClause(bag, d, projectId, opts);
|
|
268
|
+
const region = regionClause(bag, opts, POSITION_COLS);
|
|
177
269
|
const limit = bag.add("limit", "u32", opts.limit ?? 2000);
|
|
178
270
|
return {
|
|
179
271
|
query: `
|
|
@@ -185,7 +277,7 @@ export function buildCameraPositionHeatmap(projectId, opts, d) {
|
|
|
185
277
|
FROM events
|
|
186
278
|
WHERE project_id = ${pid}
|
|
187
279
|
AND event_type = 'camera_sample'
|
|
188
|
-
AND length(position) = 3${range}${scene}${session}${cameraMode}
|
|
280
|
+
AND length(position) = 3${range}${scene}${session}${cameraMode}${region}
|
|
189
281
|
GROUP BY gx, gz
|
|
190
282
|
ORDER BY count DESC
|
|
191
283
|
LIMIT ${limit}
|
|
@@ -269,11 +361,18 @@ export function buildAggregateTrajectories(projectId, opts, d) {
|
|
|
269
361
|
* controllers, hands, gaze) carry their own world-space pointing ray, so when a
|
|
270
362
|
* click has a `ray.origin` we use it verbatim — the controller/hand/gaze is the
|
|
271
363
|
* true pointing origin, not the headset/camera. Flat pointers (mouse, touch,
|
|
272
|
-
* stylus) have no native ray
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
364
|
+
* stylus) have no native ray; when the ASOF-joined `camera_sample` carries the
|
|
365
|
+
* projection intrinsics (`fov`/`aspect`/`near`, #22, ADR 0043) we unproject the
|
|
366
|
+
* click's normalized `screen[x, y]` onto the camera **near plane** and use that
|
|
367
|
+
* point as the origin, so flat-pointer rays fan out across the near plane the way
|
|
368
|
+
* the clicks were actually made. The near-plane basis assumes the canonical
|
|
369
|
+
* world-up `(0, 1, 0)` and no camera roll (true for mouse/touch); a degenerate
|
|
370
|
+
* look-straight-up/down view (or any missing/zero intrinsic, e.g. legacy data)
|
|
371
|
+
* falls back to the nearest preceding `camera_sample` **position** — the
|
|
372
|
+
* historical view-gated behavior. The camera join is therefore a LEFT join so
|
|
373
|
+
* pose clicks survive even in sessions that never emit a `camera_sample`; rows
|
|
374
|
+
* with neither a ray, a reconstructable near-plane point, nor a camera origin are
|
|
375
|
+
* dropped.
|
|
277
376
|
*/
|
|
278
377
|
export function buildClickGazeRay(projectId, opts, d) {
|
|
279
378
|
const bag = new ParamBag(d);
|
|
@@ -302,29 +401,66 @@ export function buildClickGazeRay(projectId, opts, d) {
|
|
|
302
401
|
count() AS count
|
|
303
402
|
FROM (
|
|
304
403
|
SELECT
|
|
305
|
-
|
|
306
|
-
CASE
|
|
307
|
-
|
|
308
|
-
|
|
404
|
+
ja.hx AS hx, ja.hy AS hy, ja.hz AS hz, ja.mesh AS mesh,
|
|
405
|
+
CASE
|
|
406
|
+
WHEN ja.has_ray THEN ja.rox
|
|
407
|
+
WHEN ja.recon THEN ja.px + ja.dx * ja.near / ja.dlen
|
|
408
|
+
+ (ja.dz / ja.hlen) * ja.off_r
|
|
409
|
+
+ (-(ja.dx * ja.dy) / (ja.dlen * ja.hlen)) * ja.off_u
|
|
410
|
+
WHEN ja.cam_present = 1 THEN ja.px
|
|
411
|
+
END AS ox,
|
|
412
|
+
CASE
|
|
413
|
+
WHEN ja.has_ray THEN ja.roy
|
|
414
|
+
WHEN ja.recon THEN ja.py + ja.dy * ja.near / ja.dlen
|
|
415
|
+
+ (ja.hlen / ja.dlen) * ja.off_u
|
|
416
|
+
WHEN ja.cam_present = 1 THEN ja.py
|
|
417
|
+
END AS oy,
|
|
418
|
+
CASE
|
|
419
|
+
WHEN ja.has_ray THEN ja.roz
|
|
420
|
+
WHEN ja.recon THEN ja.pz + ja.dz * ja.near / ja.dlen
|
|
421
|
+
+ (-(ja.dx) / ja.hlen) * ja.off_r
|
|
422
|
+
+ (-(ja.dy * ja.dz) / (ja.dlen * ja.hlen)) * ja.off_u
|
|
423
|
+
WHEN ja.cam_present = 1 THEN ja.pz
|
|
424
|
+
END AS oz
|
|
309
425
|
FROM (
|
|
310
|
-
SELECT
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
426
|
+
SELECT
|
|
427
|
+
c.hx AS hx, c.hy AS hy, c.hz AS hz, c.mesh AS mesh,
|
|
428
|
+
c.has_ray AS has_ray, c.rox AS rox, c.roy AS roy, c.roz AS roz,
|
|
429
|
+
m.cam_present AS cam_present,
|
|
430
|
+
m.px AS px, m.py AS py, m.pz AS pz,
|
|
431
|
+
m.dx AS dx, m.dy AS dy, m.dz AS dz, m.cam_near AS near,
|
|
432
|
+
sqrt(m.dx * m.dx + m.dy * m.dy + m.dz * m.dz) AS dlen,
|
|
433
|
+
sqrt(m.dx * m.dx + m.dz * m.dz) AS hlen,
|
|
434
|
+
(2 * c.sx - 1) * m.cam_near * tan(m.cam_fov / 2) * m.cam_aspect AS off_r,
|
|
435
|
+
(1 - 2 * c.sy) * m.cam_near * tan(m.cam_fov / 2) AS off_u,
|
|
436
|
+
(NOT c.has_ray) AND c.has_screen
|
|
437
|
+
AND m.cam_fov > 0 AND m.cam_aspect > 0 AND m.cam_near > 0
|
|
438
|
+
AND sqrt(m.dx * m.dx + m.dz * m.dz)
|
|
439
|
+
> 1e-6 * sqrt(m.dx * m.dx + m.dy * m.dy + m.dz * m.dz) AS recon
|
|
440
|
+
FROM (
|
|
441
|
+
SELECT session_id, ts,
|
|
442
|
+
hit_point[1] AS hx, hit_point[2] AS hy, hit_point[3] AS hz, mesh,
|
|
443
|
+
length(ray_origin) = 3 AS has_ray,
|
|
444
|
+
ray_origin[1] AS rox, ray_origin[2] AS roy, ray_origin[3] AS roz,
|
|
445
|
+
length(screen) = 2 AS has_screen,
|
|
446
|
+
screen[1] AS sx, screen[2] AS sy
|
|
447
|
+
FROM events
|
|
448
|
+
WHERE project_id = ${pid}
|
|
449
|
+
AND event_type = 'pointer_click'
|
|
450
|
+
AND length(hit_point) = 3${range}${scene}${source}${session}
|
|
451
|
+
) AS c
|
|
452
|
+
${d.asofLeftJoin} (
|
|
453
|
+
SELECT session_id, ts, 1 AS cam_present,
|
|
454
|
+
position[1] AS px, position[2] AS py, position[3] AS pz,
|
|
455
|
+
direction[1] AS dx, direction[2] AS dy, direction[3] AS dz,
|
|
456
|
+
fov AS cam_fov, aspect AS cam_aspect, near AS cam_near
|
|
457
|
+
FROM events
|
|
458
|
+
WHERE project_id = ${pid}
|
|
459
|
+
AND event_type = 'camera_sample'
|
|
460
|
+
AND length(position) = 3${range}${scene}${session}
|
|
461
|
+
) AS m
|
|
462
|
+
ON c.session_id = m.session_id AND c.ts >= m.ts
|
|
463
|
+
) AS ja
|
|
328
464
|
) AS j
|
|
329
465
|
WHERE j.ox IS NOT NULL
|
|
330
466
|
GROUP BY cam_vx, cam_vy, cam_vz, hit_vx, hit_vy, hit_vz, mesh
|
|
@@ -1081,11 +1217,13 @@ export function buildJankRate(projectId, opts, d) {
|
|
|
1081
1217
|
/**
|
|
1082
1218
|
* FPS segmented by device class, computed **per-session then aggregated** (ADR
|
|
1083
1219
|
* 0028 §2). Each session's median FPS is attributed to the graphics backend,
|
|
1084
|
-
* mobile flag, and GPU `renderer` recorded in its `session_start.device` block
|
|
1085
|
-
*
|
|
1086
|
-
*
|
|
1087
|
-
*
|
|
1088
|
-
*
|
|
1220
|
+
* mobile flag, and GPU `renderer` recorded in its `session_start.device` block,
|
|
1221
|
+
* plus the coarse `browser`/`os` families derived server-side from the
|
|
1222
|
+
* User-Agent at ingestion (ADR 0041) — all data already on the wire, so there is
|
|
1223
|
+
* no SDK or schema change. Device fields are read from the `session_start`
|
|
1224
|
+
* payload JSON (they are not promoted columns) and `coalesce`d to `''` when a
|
|
1225
|
+
* session never reported them. `p50_fps` is the median across sessions in the
|
|
1226
|
+
* group of each session's median FPS.
|
|
1089
1227
|
*/
|
|
1090
1228
|
export function buildPerfByDevice(projectId, opts, d) {
|
|
1091
1229
|
const bag = new ParamBag(d);
|
|
@@ -1096,6 +1234,8 @@ export function buildPerfByDevice(projectId, opts, d) {
|
|
|
1096
1234
|
const engine = d.jsonText("payload", "device", "engine");
|
|
1097
1235
|
const isMobile = d.jsonText("payload", "device", "isMobile");
|
|
1098
1236
|
const renderer = d.jsonText("payload", "device", "renderer");
|
|
1237
|
+
const browser = d.jsonText("payload", "device", "browser");
|
|
1238
|
+
const os = d.jsonText("payload", "device", "os");
|
|
1099
1239
|
return {
|
|
1100
1240
|
query: `
|
|
1101
1241
|
WITH session_device AS (
|
|
@@ -1103,7 +1243,9 @@ export function buildPerfByDevice(projectId, opts, d) {
|
|
|
1103
1243
|
session_id,
|
|
1104
1244
|
${engine} AS engine,
|
|
1105
1245
|
${isMobile} AS is_mobile,
|
|
1106
|
-
${renderer} AS renderer
|
|
1246
|
+
${renderer} AS renderer,
|
|
1247
|
+
${browser} AS browser,
|
|
1248
|
+
${os} AS os
|
|
1107
1249
|
FROM events
|
|
1108
1250
|
WHERE project_id = ${pid} AND event_type = 'session_start'
|
|
1109
1251
|
),
|
|
@@ -1120,12 +1262,14 @@ export function buildPerfByDevice(projectId, opts, d) {
|
|
|
1120
1262
|
coalesce(dev.engine, '') AS engine,
|
|
1121
1263
|
coalesce(dev.is_mobile, '') AS is_mobile,
|
|
1122
1264
|
coalesce(dev.renderer, '') AS renderer,
|
|
1265
|
+
coalesce(dev.browser, '') AS browser,
|
|
1266
|
+
coalesce(dev.os, '') AS os,
|
|
1123
1267
|
count() AS sessions,
|
|
1124
1268
|
sum(perf.s_samples) AS samples,
|
|
1125
1269
|
${d.quantile("perf.s_p50", 0.5)} AS p50_fps
|
|
1126
1270
|
FROM session_perf perf
|
|
1127
1271
|
LEFT JOIN session_device dev ON dev.session_id = perf.session_id
|
|
1128
|
-
GROUP BY engine, is_mobile, renderer
|
|
1272
|
+
GROUP BY engine, is_mobile, renderer, browser, os
|
|
1129
1273
|
ORDER BY sessions DESC
|
|
1130
1274
|
`,
|
|
1131
1275
|
query_params: bag.values,
|