@@ -2,25 +2,13 @@ import {
22 JupyterFrontEnd ,
33 JupyterFrontEndPlugin
44} from '@jupyterlab/application' ;
5- import { NotebookActions } from '@jupyterlab/notebook' ;
6- import { IObservableJSON } from '@jupyterlab/observables ' ;
5+ import { KernelError , Notebook , NotebookActions } from '@jupyterlab/notebook' ;
6+ import { Cell } from '@jupyterlab/cells ' ;
77import { ISettingRegistry } from '@jupyterlab/settingregistry' ;
88import { ICodeCellModel } from '@jupyterlab/cells' ;
99
1010import { checkBrowserNotificationSettings } from './settings' ;
1111
12- /**
13- * Extracts Code Cell Start and End Time
14- */
15- function extractExecutionMetadata ( metadata : IObservableJSON ) : [ Date , Date ] {
16- const executionMetadata = Object . assign ( { } , metadata . get ( 'execution' ) as any ) ;
17- const cellStartTime = new Date (
18- executionMetadata [ 'shell.execute_reply.started' ] ||
19- executionMetadata [ 'iopub.execute_input' ]
20- ) ;
21- const cellEndTime = new Date ( executionMetadata [ 'shell.execute_reply' ] ) ;
22- return [ cellStartTime , cellEndTime ] ;
23- }
2412
2513/**
2614 * Constructs notification message and displays it.
@@ -30,14 +18,20 @@ function displayNotification(
3018 cellNumber : number ,
3119 notebookName : string ,
3220 reportCellNumber : boolean ,
33- reportCellExecutionTime : boolean
21+ reportCellExecutionTime : boolean ,
22+ failedExecution : boolean ,
23+ error : KernelError | null
3424) : void {
3525 const notificationPayload = {
3626 icon : '/static/favicon.ico' ,
3727 body : ''
3828 } ;
29+ const title = failedExecution ? `${ notebookName } Failed!` : `${ notebookName } Completed!` ;
3930 let message = '' ;
40- if ( reportCellNumber && reportCellExecutionTime ) {
31+
32+ if ( failedExecution ) {
33+ message = error ? `${ error . errorName } ${ error . errorValue } ` : '' ;
34+ } else if ( reportCellNumber && reportCellExecutionTime ) {
4135 message = `Cell[${ cellNumber } ] Duration: ${ cellDuration } ` ;
4236 } else if ( reportCellNumber ) {
4337 message = `Cell Number: ${ cellNumber } ` ;
@@ -46,20 +40,65 @@ function displayNotification(
4640 }
4741
4842 notificationPayload . body = message ;
49- new Notification ( `${ notebookName } Cell Completed!` , notificationPayload ) ;
43+ new Notification ( title , notificationPayload ) ;
44+ }
45+
46+ /**
47+ * Trigger notification.
48+ */
49+ function triggerNotification (
50+ cell : Cell ,
51+ notebook : Notebook ,
52+ cellStartTime : Date ,
53+ cellEndTime : Date ,
54+ minimumCellExecutionTime : number ,
55+ reportCellNumber : boolean ,
56+ reportCellExecutionTime : boolean ,
57+ cellNumberType : string ,
58+ failedExecution : boolean ,
59+ error : KernelError | null
60+ ) {
61+ const codeCell = cell . model . type === 'code' ;
62+ const nonEmptyCell = cell . model . value . text . length > 0 ;
63+ if ( codeCell && nonEmptyCell ) {
64+ const codeCellModel = cell . model as ICodeCellModel ;
65+ const diff = new Date ( < any > cellEndTime - < any > cellStartTime ) ;
66+ const diffSeconds = Math . floor ( diff . getTime ( ) / 1000 ) ;
67+ if ( diffSeconds >= minimumCellExecutionTime ) {
68+ const cellDuration = diff . toISOString ( ) . substr ( 11 , 8 ) ;
69+ const cellNumber =
70+ cellNumberType === 'cell_index'
71+ ? notebook . activeCellIndex
72+ : codeCellModel . executionCount ;
73+ const notebookName = notebook . title . label . replace (
74+ / \. [ ^ / . ] + $ / ,
75+ ''
76+ ) ;
77+ displayNotification (
78+ cellDuration ,
79+ cellNumber ,
80+ notebookName ,
81+ reportCellNumber ,
82+ reportCellExecutionTime ,
83+ failedExecution ,
84+ error
85+ ) ;
86+ }
87+ }
5088}
5189
5290const extension : JupyterFrontEndPlugin < void > = {
5391 id : 'jupyterlab-notifications:plugin' ,
5492 autoStart : true ,
55- optional : [ ISettingRegistry ] ,
93+ requires : [ ISettingRegistry ] ,
5694 activate : async ( app : JupyterFrontEnd , settingRegistry : ISettingRegistry ) => {
5795 checkBrowserNotificationSettings ( ) ;
5896 let enabled = true ;
5997 let minimumCellExecutionTime = 60 ;
6098 let reportCellExecutionTime = true ;
6199 let reportCellNumber = true ;
62100 let cellNumberType = 'cell_index' ;
101+
63102 if ( settingRegistry ) {
64103 const setting = await settingRegistry . load ( extension . id ) ;
65104 const updateSettings = ( ) : void => {
@@ -70,51 +109,35 @@ const extension: JupyterFrontEndPlugin<void> = {
70109 . composite as boolean ;
71110 reportCellNumber = setting . get ( 'report_cell_number' )
72111 . composite as boolean ;
73- cellNumberType = setting . get ( 'cell_number_type' ) . composite as string ;
112+ cellNumberType = setting . get ( 'cell_number_type' )
113+ . composite as string ;
74114 } ;
75115 updateSettings ( ) ;
76116 setting . changed . connect ( updateSettings ) ;
77117 }
78118
119+ let cellStartTime = new Date ( ) ;
120+
121+ NotebookActions . executionScheduled . connect ( ( _ , args ) => {
122+ if ( enabled ) {
123+ cellStartTime = new Date ( ) ;
124+ }
125+ } ) ;
126+
79127 NotebookActions . executed . connect ( ( _ , args ) => {
80128 if ( enabled ) {
81- const { cell, notebook } = args ;
82- const codeCell = cell . model . type === 'code' ;
83- const nonEmptyCell = cell . model . value . text . length > 0 ;
84- const metadata = cell . model . metadata ;
85- if ( codeCell && nonEmptyCell ) {
86- const codeCellModel = cell . model as ICodeCellModel ;
87- if ( metadata . has ( 'execution' ) ) {
88- const [ cellStartTime , cellEndTime ] = extractExecutionMetadata (
89- metadata
90- ) ;
91- const diff = new Date ( < any > cellEndTime - < any > cellStartTime ) ;
92- const diffSeconds = Math . floor ( diff . getTime ( ) / 1000 ) ;
93- if ( diffSeconds >= minimumCellExecutionTime ) {
94- const cellDuration = diff . toISOString ( ) . substr ( 11 , 8 ) ;
95- const cellNumber =
96- cellNumberType === 'cell_index'
97- ? notebook . activeCellIndex
98- : codeCellModel . executionCount ;
99- const notebookName = notebook . title . label . replace (
100- / \. [ ^ / . ] + $ / ,
101- ''
102- ) ;
103- displayNotification (
104- cellDuration ,
105- cellNumber ,
106- notebookName ,
107- reportCellNumber ,
108- reportCellExecutionTime
109- ) ;
110- }
111- } else {
112- alert (
113- 'Notebook Cell Timing needs to be enabled for Jupyterlab Notifications to work. ' +
114- 'Please go to Settings -> Advanced Settings Editor -> Notebook and update setting to {"recordTiming": true}'
115- ) ;
116- }
117- }
129+ const { cell, notebook, success, error } = args ;
130+ const cellEndTime = new Date ( ) ;
131+ triggerNotification ( cell ,
132+ notebook ,
133+ cellStartTime ,
134+ cellEndTime ,
135+ minimumCellExecutionTime ,
136+ reportCellNumber ,
137+ reportCellExecutionTime ,
138+ cellNumberType ,
139+ ! success ,
140+ error ) ;
118141 }
119142 } ) ;
120143 }
0 commit comments