feat(lab): separate evidence catalogs and record methods
This commit is contained in:
@@ -15,6 +15,8 @@ export interface E29EvidenceResult {
|
||||
frameCount: number;
|
||||
timelineStartSeconds: number;
|
||||
timelineEndSeconds: number;
|
||||
profileId: string;
|
||||
producerSha256: string;
|
||||
};
|
||||
metrics: {
|
||||
frames: {
|
||||
@@ -208,6 +210,7 @@ function parseReviewFrame(value: unknown): E29ReviewFrame {
|
||||
function parseEvidenceResult(value: unknown): E29EvidenceResult {
|
||||
const source = record(value, "E29 result");
|
||||
const identity = record(source.identity, "E29 identity");
|
||||
const profile = record(identity.profile, "E29 identity.profile");
|
||||
const metrics = record(source.metrics, "E29 metrics");
|
||||
const frames = record(metrics.frames, "E29 metrics.frames");
|
||||
const semantic = record(
|
||||
@@ -253,6 +256,11 @@ function parseEvidenceResult(value: unknown): E29EvidenceResult {
|
||||
identity.timeline_end_seconds,
|
||||
"E29 identity.timeline_end_seconds",
|
||||
),
|
||||
profileId: stringValue(profile.profile_id, "E29 identity.profile.profile_id"),
|
||||
producerSha256: stringValue(
|
||||
identity.producer_sha256,
|
||||
"E29 identity.producer_sha256",
|
||||
),
|
||||
},
|
||||
metrics: {
|
||||
frames: {
|
||||
|
||||
@@ -22,6 +22,12 @@ export interface LidarLocalSurfaceModel {
|
||||
sessionId: string;
|
||||
sourcePackId: string;
|
||||
status: "diagnostic-only";
|
||||
method: {
|
||||
executionClass: "deterministic";
|
||||
pipelineId: string;
|
||||
algorithm: string;
|
||||
producerSha256: string;
|
||||
};
|
||||
source: {
|
||||
frameCount: number;
|
||||
availableLidarFrames: number;
|
||||
@@ -562,6 +568,8 @@ function temporalQualification(
|
||||
function model(value: unknown): LidarLocalSurfaceModel {
|
||||
const source = record(value, "LiDAR local-surface model");
|
||||
const sourceEvidence = record(source.source, "source");
|
||||
const surfaceModel = record(source.surface_model, "surface_model");
|
||||
const surfaceProfile = record(surfaceModel.profile, "surface_model.profile");
|
||||
const metrics = record(source.metrics, "metrics");
|
||||
const frames = record(metrics.frames, "metrics.frames");
|
||||
if (
|
||||
@@ -611,6 +619,19 @@ function model(value: unknown): LidarLocalSurfaceModel {
|
||||
sessionId: text(source.session_id, "session_id", SAFE_ID),
|
||||
sourcePackId: text(source.source_pack_id, "source_pack_id", SAFE_PACK_ID),
|
||||
status: "diagnostic-only",
|
||||
method: {
|
||||
executionClass: "deterministic",
|
||||
pipelineId: text(
|
||||
surfaceProfile.profile_id,
|
||||
"surface_model.profile.profile_id",
|
||||
),
|
||||
algorithm: text(surfaceModel.kind, "surface_model.kind"),
|
||||
producerSha256: text(
|
||||
source.producer_sha256,
|
||||
"producer_sha256",
|
||||
/^[a-f0-9]{64}$/,
|
||||
),
|
||||
},
|
||||
source: {
|
||||
frameCount: integer(sourceEvidence.frame_count, "source.frame_count"),
|
||||
availableLidarFrames: integer(
|
||||
|
||||
@@ -9,6 +9,8 @@ export type ObservationSessionStatus =
|
||||
| "interrupted"
|
||||
| "failed";
|
||||
|
||||
export type ObservationSessionScope = "all" | "source" | "laboratory";
|
||||
|
||||
export interface ObservationLabInstance {
|
||||
labId: string;
|
||||
sourceSessionId: string;
|
||||
@@ -1077,15 +1079,21 @@ function requirePreparationEtag(
|
||||
export async function fetchObservationSessionCatalog({
|
||||
signal,
|
||||
limit,
|
||||
scope = "all",
|
||||
fetcher = globalThis.fetch,
|
||||
}: {
|
||||
signal?: AbortSignal;
|
||||
limit?: number;
|
||||
scope?: ObservationSessionScope;
|
||||
fetcher?: ObservationSessionFetch;
|
||||
} = {}): Promise<ObservationSessionCatalog> {
|
||||
const query = Number.isInteger(limit) && Number(limit) >= 1 && Number(limit) <= 100
|
||||
? `?limit=${Number(limit)}`
|
||||
: "";
|
||||
const queryParameters = new URLSearchParams();
|
||||
if (Number.isInteger(limit) && Number(limit) >= 1 && Number(limit) <= 100) {
|
||||
queryParameters.set("limit", String(Number(limit)));
|
||||
}
|
||||
if (scope !== "all") queryParameters.set("scope", scope);
|
||||
const serializedQuery = queryParameters.toString();
|
||||
const query = serializedQuery ? `?${serializedQuery}` : "";
|
||||
let response: Response;
|
||||
try {
|
||||
response = await fetcher(`/api/v1/observation-sessions${query}`, {
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
type ObservationSessionFetch,
|
||||
type ObservationSessionPreparation,
|
||||
type ObservationSessionReplayLaunch,
|
||||
type ObservationSessionScope,
|
||||
type ObservationSessionSummary,
|
||||
} from "./sessionArchive";
|
||||
|
||||
@@ -369,12 +370,14 @@ export function clearObservationReplayPreparation(
|
||||
|
||||
export function useObservationSessions({
|
||||
limit = 100,
|
||||
scope = "all",
|
||||
replayEnabled = true,
|
||||
onReplayBegin,
|
||||
onReplayAccepted,
|
||||
onReplaySettled,
|
||||
}: {
|
||||
limit?: number;
|
||||
scope?: ObservationSessionScope;
|
||||
replayEnabled?: boolean;
|
||||
/** Called only after the archive is ready, immediately before replacing the old viewer. */
|
||||
onReplayBegin?: (
|
||||
@@ -425,7 +428,10 @@ export function useObservationSessions({
|
||||
const sequence = ++catalogSequence.current;
|
||||
if (foreground) setState("loading");
|
||||
try {
|
||||
const catalog = await fetchObservationSessionCatalog({ limit: safeLimit });
|
||||
const catalog = await fetchObservationSessionCatalog({
|
||||
limit: safeLimit,
|
||||
scope,
|
||||
});
|
||||
if (!mounted.current || sequence !== catalogSequence.current) return false;
|
||||
setItems(catalog.items.slice(0, safeLimit));
|
||||
setState("ready");
|
||||
@@ -437,7 +443,7 @@ export function useObservationSessions({
|
||||
setError(errorMessage(loadError));
|
||||
return false;
|
||||
}
|
||||
}, [safeLimit]);
|
||||
}, [safeLimit, scope]);
|
||||
|
||||
const refresh = useCallback(() => loadCatalog(true), [loadCatalog]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user