feat(metrics): compiles and runs remember which gallery example they came from
useProjectStore.currentExampleId — stamped by loadExample, dropped the moment a real project (or a blank workspace) takes over. The compile request and the run event carry it as example_id, analytics only, so 'which examples get compiled and run most' becomes answerable.
This commit is contained in:
parent
27f712444b
commit
c0d3543573
|
|
@ -168,6 +168,9 @@ class CompileRequest(BaseModel):
|
|||
# RP2350 boards all compile as rpipico2), and only the kind can tell
|
||||
# them apart. Rides into the metric hook's `extra`.
|
||||
board_kind: str | None = None
|
||||
# Optional: gallery example the workspace was loaded from. Analytics
|
||||
# only ("which examples get compiled most"); rides in `extra` too.
|
||||
example_id: str | None = None
|
||||
# Per-board ESP32 build options (Partition Scheme, CPU Freq, Flash Mode,
|
||||
# PSRAM, etc.). Loose dict so the frontend can add fields without a
|
||||
# backend deploy — espidf_compiler.compile validates known keys and
|
||||
|
|
@ -520,6 +523,7 @@ async def _compile_job(
|
|||
"async": True,
|
||||
"initiated_by": request.initiated_by,
|
||||
"board_kind": request.board_kind,
|
||||
"example_id": request.example_id,
|
||||
"partition_scheme": (request.board_options or {}).get("partitionScheme"),
|
||||
"spiffs_file_count": len(request.spiffs_files or []),
|
||||
},
|
||||
|
|
@ -541,7 +545,7 @@ async def _compile_job(
|
|||
success=False,
|
||||
duration_ms=int((time.monotonic() - started) * 1000),
|
||||
error_kind="exception",
|
||||
extra={"file_count": len(files), "exception": str(exc)[:200], "async": True, "initiated_by": request.initiated_by, "board_kind": request.board_kind},
|
||||
extra={"file_count": len(files), "exception": str(exc)[:200], "async": True, "initiated_by": request.initiated_by, "board_kind": request.board_kind, "example_id": request.example_id},
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -574,7 +578,7 @@ async def compile_sketch(
|
|||
success=False,
|
||||
duration_ms=int((time.monotonic() - started) * 1000),
|
||||
error_kind="exception",
|
||||
extra={"file_count": len(files), "exception": str(e)[:200], "initiated_by": request.initiated_by, "board_kind": request.board_kind},
|
||||
extra={"file_count": len(files), "exception": str(e)[:200], "initiated_by": request.initiated_by, "board_kind": request.board_kind, "example_id": request.example_id},
|
||||
request=http_request,
|
||||
)
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
|
@ -592,6 +596,7 @@ async def compile_sketch(
|
|||
"has_wifi": response.has_wifi,
|
||||
"initiated_by": request.initiated_by,
|
||||
"board_kind": request.board_kind,
|
||||
"example_id": request.example_id,
|
||||
"partition_scheme": (request.board_options or {}).get("partitionScheme"),
|
||||
"spiffs_file_count": len(request.spiffs_files or []),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -235,6 +235,7 @@ export const EditorToolbar = ({
|
|||
project_id: currentProject?.id ?? null,
|
||||
board_fqbn: fqbn ?? null,
|
||||
board_kind: boardKind ?? null,
|
||||
example_id: useProjectStore.getState().currentExampleId,
|
||||
engine: engine ?? null,
|
||||
});
|
||||
},
|
||||
|
|
@ -573,6 +574,7 @@ export const EditorToolbar = ({
|
|||
boardOptions: activeBoard?.boardOptions,
|
||||
spiffsFiles: activeBoard?.spiffsFiles,
|
||||
boardKind: kind ?? undefined,
|
||||
exampleId: useProjectStore.getState().currentExampleId,
|
||||
// P2.4 — THIS board's declared manifest (compile scope). Per-board so
|
||||
// two boards can use different libraries without clashing.
|
||||
libraries: activeBoard?.libraries?.length ? activeBoard.libraries : null,
|
||||
|
|
@ -1079,7 +1081,7 @@ export const EditorToolbar = ({
|
|||
})),
|
||||
]);
|
||||
},
|
||||
{ boardOptions: board.boardOptions, spiffsFiles: board.spiffsFiles, boardKind: board.boardKind, libraries: board.libraries?.length ? board.libraries : null, language: board.languageMode === 'espidf' ? 'espidf' : undefined },
|
||||
{ boardOptions: board.boardOptions, spiffsFiles: board.spiffsFiles, boardKind: board.boardKind, exampleId: useProjectStore.getState().currentExampleId, libraries: board.libraries?.length ? board.libraries : null, language: board.languageMode === 'espidf' ? 'espidf' : undefined },
|
||||
);
|
||||
|
||||
const resultLogs = parseCompileResult(result, label, boardTarget, lastStreamedLen > 0);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ export interface CompileExtras {
|
|||
// FQBN (Pimoroni RP2350 boards all compile as rpipico2); the kind is the
|
||||
// only identifier that tells them apart in the metrics.
|
||||
boardKind?: string;
|
||||
// Gallery example the workspace was loaded from — analytics only
|
||||
// ("which examples get compiled most"). Null/omitted outside examples.
|
||||
exampleId?: string | null;
|
||||
}
|
||||
|
||||
export interface CompileResult {
|
||||
|
|
@ -127,6 +130,7 @@ export async function compileCode(
|
|||
board_options,
|
||||
spiffs_files,
|
||||
board_kind: extras?.boardKind ?? null,
|
||||
example_id: extras?.exampleId ?? null,
|
||||
libraries,
|
||||
language: extras?.language ?? null,
|
||||
initiated_by: extras?.initiatedBy ?? null,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ export interface RunEventPayload {
|
|||
board_fqbn?: string | null;
|
||||
/** Editor BoardKind — disambiguates boards that share an FQBN (analytics). */
|
||||
board_kind?: string | null;
|
||||
/** Gallery example the workspace was loaded from (analytics). */
|
||||
example_id?: string | null;
|
||||
/** Which engine took this run — 'instant' (browser) or 'linux' (guest).
|
||||
* Absent for boards where the question doesn't apply. */
|
||||
engine?: 'instant' | 'linux' | null;
|
||||
|
|
|
|||
|
|
@ -13,8 +13,16 @@ interface CurrentProject {
|
|||
|
||||
interface ProjectState {
|
||||
currentProject: CurrentProject | null;
|
||||
/**
|
||||
* Gallery example currently loaded in the editor (null when the workspace
|
||||
* came from a project, a file or scratch). Set by loadExample, cleared
|
||||
* whenever a project takes over — analytics only: compiles/runs stamp it
|
||||
* so "which examples get compiled most" is answerable.
|
||||
*/
|
||||
currentExampleId: string | null;
|
||||
setCurrentProject: (project: CurrentProject) => void;
|
||||
clearCurrentProject: () => void;
|
||||
setCurrentExampleId: (id: string | null) => void;
|
||||
// Updated to accept either the legacy boolean OR the new enum so the
|
||||
// ShareModal callsite and any older callers keep working uniformly.
|
||||
setVisibility: (next: boolean | ProjectVisibility) => void;
|
||||
|
|
@ -22,8 +30,14 @@ interface ProjectState {
|
|||
|
||||
export const useProjectStore = create<ProjectState>((set) => ({
|
||||
currentProject: null,
|
||||
setCurrentProject: (project) => set({ currentProject: project }),
|
||||
clearCurrentProject: () => set({ currentProject: null }),
|
||||
currentExampleId: null,
|
||||
// Loading a real project supersedes the example context.
|
||||
setCurrentProject: (project) => set({ currentProject: project, currentExampleId: null }),
|
||||
// Also drops the example context: every caller (new project, blank
|
||||
// workspace, logout cleanup) means "this workspace is no longer that".
|
||||
// loadExample re-stamps its id right after calling this.
|
||||
clearCurrentProject: () => set({ currentProject: null, currentExampleId: null }),
|
||||
setCurrentExampleId: (id) => set({ currentExampleId: id }),
|
||||
setVisibility: (next) =>
|
||||
set((s) => {
|
||||
if (!s.currentProject) return s;
|
||||
|
|
|
|||
|
|
@ -114,6 +114,8 @@ export async function loadExample(
|
|||
// sees null as projectId during every subsequent simulator/editor change,
|
||||
// so no PUT goes out.
|
||||
useProjectStore.getState().clearCurrentProject();
|
||||
// Analytics context: compiles/runs from this workspace stamp the example id.
|
||||
useProjectStore.getState().setCurrentExampleId(example.id);
|
||||
|
||||
// P2.4 — this example's declared manifest (compile scope) is assigned to each
|
||||
// board it creates at the END of this function (the boards don't exist yet).
|
||||
|
|
|
|||
Loading…
Reference in New Issue