diff --git a/vscode/src/events.ts b/vscode/src/events.ts index 3a08c52..50ab5e0 100644 --- a/vscode/src/events.ts +++ b/vscode/src/events.ts @@ -4,6 +4,7 @@ import * as api from "./modalityApi"; import * as commonNotebookCells from "../templates/common.json"; import * as eventTimingNotebookCells from "../templates/eventTiming.json"; import * as eventAttributeValuesNotebookCells from "../templates/eventAttributeValues.json"; +import * as eventMultiAttributeValuesNotebookCells from "../templates/eventMultiAttributeValues.json"; export class EventsTreeDataProvider implements vscode.TreeDataProvider { selectedTimelineId?: api.TimelineId = undefined; @@ -24,7 +25,7 @@ export class EventsTreeDataProvider implements vscode.TreeDataProvider this.setSelectedTimeline(timelineId, timelineName) ), - vscode.commands.registerCommand("auxon.events.createEventTimingNotebook", async (itemData) => - this.createEventTimingNotebook(itemData) + vscode.commands.registerCommand("auxon.events.createEventTimingNotebook", async (item) => + this.createEventTimingNotebook(item) ), vscode.commands.registerCommand("auxon.events.createEventAttrNotebook", async (itemData) => this.createEventAttrNotebook(itemData) @@ -93,16 +94,62 @@ export class EventsTreeDataProvider implements vscode.TreeDataProvider data.eventName); + // Add the item the command was executed on, it may not be in the selection + selectedEventNames.push(item.eventName); + selectedEventNames = [...new Set(selectedEventNames)]; // dedupe + for (const eventName of selectedEventNames) { + const varMap = this.templateVariableMap(); + varMap["eventName"] = eventName; + await this.createJupyterNotebook(eventTimingNotebookCells.cells, varMap); + } } async createEventAttrNotebook(item: EventAttributeTreeItemData) { - const varMap = this.templateVariableMap(); - varMap["eventName"] = item.eventName; - varMap["eventAttribute"] = item.attribute; - await this.createJupyterNotebook(eventAttributeValuesNotebookCells.cells, varMap); + if (this.view.selection.length == 1) { + const varMap = this.templateVariableMap(); + varMap["eventName"] = item.eventName; + varMap["eventAttribute"] = item.attribute; + await this.createJupyterNotebook(eventAttributeValuesNotebookCells.cells, varMap); + } else { + let selectedEventAttrs = [...this.view.selection, ...[item]]; + selectedEventAttrs = [...new Set(selectedEventAttrs)]; // dedupe + + const attributesByEvent = new Map(); + for (const ev of selectedEventAttrs) { + if (!(ev instanceof EventAttributeTreeItemData)) { + throw new Error("Internal error: event tree node not of expected type"); + } + if (attributesByEvent.has(ev.eventName)) { + const attributes = attributesByEvent.get(ev.eventName); + attributes.push(ev.attribute); + } else { + const attributes = []; + attributes.push(ev.attribute); + attributesByEvent.set(ev.eventName, attributes); + } + } + + attributesByEvent.forEach(async (attributes: string[], eventName: string) => { + const varMap = this.templateVariableMap(); + varMap["eventName"] = eventName; + const cells = lodash.cloneDeep(eventMultiAttributeValuesNotebookCells.cells.slice(0, 2)); + const srcCell = eventMultiAttributeValuesNotebookCells.cells[2]; + const figShowCell = eventMultiAttributeValuesNotebookCells.cells[3]; + const eventAttributesList = []; + for (let i = 0; i < attributes.length; i++) { + eventAttributesList.push("'event." + attributes[i] + "'"); + varMap["eventAttribute" + i] = attributes[i]; + const newSrc = srcCell.source[0].replace(/eventAttribute/g, "eventAttribute" + i); + cells[1].source.push(newSrc); + } + cells[1].source.push(figShowCell.source[0]); + + varMap["eventAttributes"] = eventAttributesList.join(", "); + + await this.createJupyterNotebook(cells, varMap); + }); + } } async createJupyterNotebook(notebookSrcCells: object[], templateVarMap: object) { diff --git a/vscode/templates/common.json b/vscode/templates/common.json index 763f9ca..8bcdb0c 100644 --- a/vscode/templates/common.json +++ b/vscode/templates/common.json @@ -148,6 +148,7 @@ "# Import plotly\n", "import plotly.io as pio\n", "import plotly.express as px\n", + "import plotly.graph_objects as go\n", "pio.renderers.default = 'notebook'\n" ] } diff --git a/vscode/templates/eventMultiAttributeValues.json b/vscode/templates/eventMultiAttributeValues.json new file mode 100644 index 0000000..34034c2 --- /dev/null +++ b/vscode/templates/eventMultiAttributeValues.json @@ -0,0 +1,71 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Request data frame\n", + "modality = Modality()\n", + "\n", + "df = modality.events_data_frame(\n", + " workspace_version_id='${workspaceVersionId}',\n", + " segments=[${segments}],\n", + " timeline_filter='_.timeline.id = ${timelineId}',\n", + " event_filter='_.name = \"${eventName}\"',\n", + " include_attrs=['event.timestamp', ${eventAttributes}])\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Scatter plot\n", + "fig = go.Figure()\n", + "fig.update_layout(title='Event Attribute Scatter Plot
${eventName} @ ${timelineName}')\n", + "fig.update_xaxes(title_text='event.timestamp')\n", + "fig.update_yaxes(title_text='Attribute Value')\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fig.add_trace(go.Scatter(name='_.${eventAttribute}', x=df['event.timestamp'], y=df['event.${eventAttribute}']))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": ["fig.show()"] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}