diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d23639..42d11b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [3.2.0](https://github.com/deshaw/jupyterlab-execute-time/compare/v3.1.2...v3.2.0) (unreleased) + +### Added + +- Add an option to show the cell outputs per second [#116](https://github.com/deshaw/jupyterlab-execute-time/pull/116) + ## [3.1.2](https://github.com/deshaw/jupyterlab-execute-time/compare/v3.1.0...v3.1.2) (2024-02-14) ### Fixed diff --git a/schema/settings.json b/schema/settings.json index 675150f..cf1a23b 100644 --- a/schema/settings.json +++ b/schema/settings.json @@ -59,6 +59,12 @@ "description": "Show the formatted date string in the given format. For example \"yyy-MM-dd HH:mm:ss\" results in \"2023-02-28 14:25:57 \", \"dd/MM/YYY HH:mm:ss (O)\" results in \"28/02/2023 14:25:57 (GMT +3)\". Find out more on unicode date field symbols on this link [https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table]", "default": "yyy-MM-dd HH:mm:ss", "pattern": "[yGuqQMLwdeEcabBhHkKmsSzOxX\\W].*" + }, + "showOutputsPerSecond": { + "type": "boolean", + "title": "Show Outputs Per Second", + "description": "After a cell has finished running, show the outputs per second calculated from the last cell execution time and number of outputs.", + "default": false } } } diff --git a/src/ExecuteTimeWidget.ts b/src/ExecuteTimeWidget.ts index 1d40d63..439bc28 100644 --- a/src/ExecuteTimeWidget.ts +++ b/src/ExecuteTimeWidget.ts @@ -32,6 +32,7 @@ export interface IExecuteTimeSettings { showDate: boolean; historyCount: number; dateFormat: string; + showOutputsPerSecond: boolean; } export default class ExecuteTimeWidget extends Widget { @@ -290,11 +291,13 @@ export default class ExecuteTimeWidget extends Widget { if (isLikelyAborted) { msg = ''; } else if (endTime) { - if ( - this._settings.minTime <= - differenceInMilliseconds(endTime, startTime) / 1000.0 - ) { + const executionTimeMillis = differenceInMilliseconds( + endTime, + startTime + ); + if (this._settings.minTime <= executionTimeMillis / 1000.0) { const executionTime = getTimeDiff(endTime, startTime); + const executionsPerSecond = 1000.0 / executionTimeMillis; const lastExecutionTime = executionTimeNode.getAttribute( PREV_DATA_EXECUTION_TIME_ATTR ); @@ -324,6 +327,15 @@ export default class ExecuteTimeWidget extends Widget { msg += ` at ${getTimeString(endTime, this._settings.dateFormat)}`; } msg += ` in ${executionTime}`; + + const numberOfOutputs = cell.model.outputs.length; + if (this._settings.showOutputsPerSecond && numberOfOutputs > 0) { + const outputsPerSecond = executionsPerSecond / numberOfOutputs; + msg += `, ${numberOfOutputs} output${ + numberOfOutputs === 1 ? '' : 's' + }`; + msg += ` at ${outputsPerSecond.toFixed(2)}/s`; + } } } else if (startTime) { if (this._settings.showLiveExecutionTime) { @@ -429,6 +441,9 @@ export default class ExecuteTimeWidget extends Widget { ); } + this._settings.showOutputsPerSecond = settings.get('showOutputsPerSecond') + .composite as boolean; + const cells = this._panel.context.model.cells; if (this._settings.enabled) { cells.changed.connect(this.updateConnectedCell); @@ -513,5 +528,6 @@ export default class ExecuteTimeWidget extends Widget { showDate: true, historyCount: 5, dateFormat: 'yyy-MM-dd HH:mm:ss', + showOutputsPerSecond: false, }; } diff --git a/ui-tests/notebooks/Timing_outputs_outcomes.ipynb b/ui-tests/notebooks/Timing_outputs_outcomes.ipynb new file mode 100644 index 0000000..ac2ecd1 --- /dev/null +++ b/ui-tests/notebooks/Timing_outputs_outcomes.ipynb @@ -0,0 +1,55 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "98ce2d4e-e422-40ea-aa89-5adabbb8d0f0", + "metadata": {}, + "source": [ + "Notes:\n", + "- Each cells is self-contained to allow parallel testing.\n", + "- Cell indices are used in tests." + ] + }, + { + "cell_type": "markdown", + "id": "f28cb5ea-94a4-47d3-9860-4cf20a29d690", + "metadata": {}, + "source": [ + "#### Last executed at with outputs:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3cfa2867-8f80-4793-9a4d-d773b06136e1", + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(5):\n", + " display(1)\n", + " sleep(0.01)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ui-tests/tests/timing_outputs_outcomes.spec.ts b/ui-tests/tests/timing_outputs_outcomes.spec.ts new file mode 100644 index 0000000..7fea776 --- /dev/null +++ b/ui-tests/tests/timing_outputs_outcomes.spec.ts @@ -0,0 +1,34 @@ +import { expect, galata, test } from '@jupyterlab/galata'; +import { openNotebook, cleanup, maskedScreenshot } from './utils'; + +const SETTINGS_ID = 'jupyterlab-execute-time:settings'; + +test.describe('Timing outcomes with ', () => { + test.beforeEach(openNotebook('Timing_outputs_outcomes.ipynb')); + test.afterEach(cleanup); + // Disable flashing highlight for screenshot consistency + test.use({ + mockSettings: { + ...galata.DEFAULT_SETTINGS, + [SETTINGS_ID]: { + ...galata.DEFAULT_SETTINGS[SETTINGS_ID], + highlight: false, + showOutputsPerSecond: true, + }, + }, + }); + + test('"Last executed at" state', async ({ page }) => { + const cell = await page.notebook.getCell(2); + + // Execute cell and wait for it to complete + await page.notebook.runCell(2); + + const widget = await cell.waitForSelector('.execute-time'); + expect(await widget.textContent()).toContain('Last executed at'); + expect(await widget.textContent()).toContain('outputs at'); + expect(await maskedScreenshot(widget)).toMatchSnapshot( + 'last-executed-outputs.png' + ); + }); +}); diff --git a/ui-tests/tests/timing_outputs_outcomes.spec.ts-snapshots/last-executed-outputs-linux.png b/ui-tests/tests/timing_outputs_outcomes.spec.ts-snapshots/last-executed-outputs-linux.png new file mode 100644 index 0000000..18f42b0 Binary files /dev/null and b/ui-tests/tests/timing_outputs_outcomes.spec.ts-snapshots/last-executed-outputs-linux.png differ