jupyterlab-pioneer 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -40,12 +40,12 @@ To add a data exporter, users should assign a callable function along with funct
40
40
 
41
41
  This extension provides 4 default exporters.
42
42
 
43
- - [`console_exporter`](https://github.com/educational-technology-collective/jupyterlab-pioneer/blob/main/jupyterlab_pioneer/handlers.py#L10), which sends telemetry data to the browser console
44
- - [`command_line_exporter`](https://github.com/educational-technology-collective/jupyterlab-pioneer/blob/main/jupyterlab_pioneer/handlers.py#L33), which sends telemetry data to the python console jupyter is running on
45
- - [`file_exporter`](https://github.com/educational-technology-collective/jupyterlab-pioneer/blob/main/jupyterlab_pioneer/handlers.py#L55), which saves telemetry data to local file
46
- - [`remote_exporter`](https://github.com/educational-technology-collective/jupyterlab-pioneer/blob/main/jupyterlab_pioneer/handlers.py#L81), which sends telemetry data to a remote http endpoint
43
+ - [`console_exporter`](https://github.com/educational-technology-collective/jupyterlab-pioneer/blob/main/jupyterlab_pioneer/default_exporters.py#L9), which sends telemetry data to the browser console
44
+ - [`command_line_exporter`](https://github.com/educational-technology-collective/jupyterlab-pioneer/blob/main/jupyterlab_pioneer/default_exporters.py#L32), which sends telemetry data to the python console jupyter is running on
45
+ - [`file_exporter`](https://github.com/educational-technology-collective/jupyterlab-pioneer/blob/main/jupyterlab_pioneer/default_exporters.py#L57), which saves telemetry data to local file
46
+ - [`remote_exporter`](https://github.com/educational-technology-collective/jupyterlab-pioneer/blob/main/jupyterlab_pioneer/default_exporters.py#L85), which sends telemetry data to a remote http endpoint
47
47
 
48
- Additionally, users can import default exporters or write customized exporters in the configuration file.
48
+ Additionally, users can write customized exporters in the configuration file.
49
49
 
50
50
  ### Configuration file name & path
51
51
 
@@ -57,16 +57,29 @@ Check jupyter server [doc](https://jupyter-server.readthedocs.io/en/latest/opera
57
57
 
58
58
  ### Syntax
59
59
 
60
- `activateEvents`: An array of the ids of the events. Only valid events (1. has an id associated with the event class, and 2. the event id is included in `activatedEvents`) will be activated.
60
+ `activateEvents`: An array of active events. Each active event in the array should have the following structure:
61
61
 
62
- `logNotebookContentEvents`: An array of the ids of the events. The extension will export the entire notebook content only for valid events (1. has an id associated with the event class, and 2. the event id is included in `logNotebookContentEvents`).
62
+ ```python
63
+ {
64
+ 'name': string # string, event name
65
+ 'logWholeNotebook': # boolean, whether to export the entire notebook content when event is triggered
66
+ }
67
+ ```
68
+
69
+ The extension would only generate and export data for valid events ( 1. that have an id associated with the event class, 2. and the event name is included in `activeEvents`
70
+ ).
71
+ The extension will export the entire notebook content only for valid events with the `logWholeNotebook` flag == True.
63
72
 
64
- `exporters`: An array of exporters. Each exporter should have the following structure:
73
+ `exporters`: An array of exporters. Each exporter in the array should have the following structure:
65
74
 
66
75
  ```python
67
76
  {
68
- exporter: # a callable exporter function. Need to contain 'path' for file_exporter, 'url' for remote_exporter.
69
- args: # arguments passed to the exporter function
77
+ 'type': # one of 'console_exporter', 'command_line_exporter',
78
+ # 'file_exporter', 'remote_exporter',
79
+ # or 'custom_exporter'.
80
+ 'args': # arguments passed to the exporter function.
81
+ # It needs to contain 'path' for file_exporter, 'url' for remote_exporter.
82
+ 'activeEvents': # exporter's local active_events config will override global activeEvents config
70
83
  }
71
84
  ```
72
85
 
package/lib/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
2
  import { NotebookPanel } from '@jupyterlab/notebook';
3
3
  import { Token } from '@lumino/coreutils';
4
+ import { Exporter } from './types';
4
5
  export declare const IJupyterLabPioneer: Token<IJupyterLabPioneer>;
5
6
  export interface IJupyterLabPioneer {
6
7
  /**
@@ -10,10 +11,10 @@ export interface IJupyterLabPioneer {
10
11
  * @param {Object} eventDetail An object containing event details
11
12
  * @param {Boolean} logNotebookContent A boolean indicating whether to log the entire notebook or not
12
13
  */
13
- publishEvent(notebookPanel: NotebookPanel, eventDetail: Object, logNotebookContent?: Boolean): Promise<void>;
14
+ publishEvent(notebookPanel: NotebookPanel, eventDetail: Object, logWholeNotebook?: Boolean, exporter?: Exporter): Promise<void>;
14
15
  }
15
16
  declare class JupyterLabPioneer implements IJupyterLabPioneer {
16
- publishEvent(notebookPanel: NotebookPanel, eventDetail: Object, logNotebookContent?: Boolean): Promise<void>;
17
+ publishEvent(notebookPanel: NotebookPanel, eventDetail: Object, logWholeNotebook?: Boolean, exporter?: Exporter): Promise<void>;
17
18
  }
18
19
  declare const plugin: JupyterFrontEndPlugin<JupyterLabPioneer>;
19
20
  export default plugin;
package/lib/index.js CHANGED
@@ -1,28 +1,26 @@
1
1
  import { INotebookTracker } from '@jupyterlab/notebook';
2
2
  import { Token } from '@lumino/coreutils';
3
3
  import { requestAPI } from './handler';
4
- // import { Router } from './router';
5
4
  import { producerCollection } from './producer';
6
5
  const PLUGIN_ID = 'jupyterlab-pioneer:plugin';
7
6
  export const IJupyterLabPioneer = new Token(PLUGIN_ID);
8
7
  class JupyterLabPioneer {
9
- async publishEvent(notebookPanel, eventDetail, logNotebookContent) {
8
+ async publishEvent(notebookPanel, eventDetail, logWholeNotebook, exporter) {
10
9
  var _a, _b;
11
10
  if (!notebookPanel) {
12
11
  throw Error('router is listening to a null notebook panel');
13
12
  }
14
- // Construct data
15
13
  const requestBody = {
16
14
  eventDetail: eventDetail,
17
15
  notebookState: {
18
16
  sessionID: (_a = notebookPanel === null || notebookPanel === void 0 ? void 0 : notebookPanel.sessionContext.session) === null || _a === void 0 ? void 0 : _a.id,
19
17
  notebookPath: notebookPanel === null || notebookPanel === void 0 ? void 0 : notebookPanel.context.path,
20
- notebookContent: logNotebookContent
18
+ notebookContent: logWholeNotebook
21
19
  ? (_b = notebookPanel === null || notebookPanel === void 0 ? void 0 : notebookPanel.model) === null || _b === void 0 ? void 0 : _b.toJSON()
22
20
  : null // decide whether to log the entire notebook
23
- }
21
+ },
22
+ exporter: exporter
24
23
  };
25
- // Send data to exporters
26
24
  const response = await requestAPI('export', {
27
25
  method: 'POST',
28
26
  body: JSON.stringify(requestBody)
@@ -38,15 +36,34 @@ const plugin = {
38
36
  activate: async (app, notebookTracker) => {
39
37
  const version = await requestAPI('version');
40
38
  console.log(`${PLUGIN_ID}: ${version}`);
41
- const config = await requestAPI('config');
39
+ const config = (await requestAPI('config'));
42
40
  const pioneer = new JupyterLabPioneer();
43
41
  notebookTracker.widgetAdded.connect(async (_, notebookPanel) => {
42
+ var _a;
44
43
  await notebookPanel.revealed;
45
44
  await notebookPanel.sessionContext.ready;
46
- producerCollection.forEach(producer => {
47
- if (config.activeEvents.includes(producer.id)) {
48
- new producer().listen(notebookPanel, pioneer, config.logNotebookContentEvents.includes(producer.id));
49
- }
45
+ const activeEvents = config.activeEvents;
46
+ const exporters = ((_a = notebookPanel.content.model) === null || _a === void 0 ? void 0 : _a.getMetadata('exporters')) ||
47
+ config.exporters;
48
+ const processedExporters = activeEvents && activeEvents.length
49
+ ? exporters.map(e => {
50
+ if (!e.activeEvents) {
51
+ e.activeEvents = activeEvents;
52
+ return e;
53
+ }
54
+ else {
55
+ return e;
56
+ }
57
+ })
58
+ : exporters.filter(e => e.activeEvents && e.activeEvents.length);
59
+ console.log(processedExporters);
60
+ processedExporters.forEach(exporter => {
61
+ producerCollection.forEach(producer => {
62
+ var _a;
63
+ if ((_a = exporter.activeEvents) === null || _a === void 0 ? void 0 : _a.map(o => o.name).includes(producer.id)) {
64
+ new producer().listen(notebookPanel, pioneer, exporter);
65
+ }
66
+ });
50
67
  });
51
68
  });
52
69
  return pioneer;
package/lib/producer.d.ts CHANGED
@@ -1,53 +1,58 @@
1
1
  import { NotebookPanel } from '@jupyterlab/notebook';
2
2
  import { IJupyterLabPioneer } from './index';
3
- export declare class NotebookOpenEventProducer {
3
+ import { Exporter } from './types';
4
+ export declare class ActiveCellChangeEventProducer {
4
5
  static id: string;
5
- private produced;
6
- listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, logNotebookContentEvent: boolean): Promise<void>;
6
+ listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, exporter: Exporter): void;
7
7
  }
8
- export declare class NotebookScrollProducer {
8
+ export declare class CellAddEventProducer {
9
9
  static id: string;
10
- private timeout;
11
- listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, logNotebookContentEvent: boolean): void;
10
+ listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, exporter: Exporter): void;
12
11
  }
13
- export declare class NotebookVisibleEventProducer {
12
+ export declare class CellEditEventProducer {
14
13
  static id: string;
15
- listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, logNotebookContentEvent: boolean): void;
14
+ listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, exporter: Exporter): void;
16
15
  }
17
- export declare class NotebookHiddenEventProducer {
16
+ export declare class CellExecuteEventProducer {
17
+ static id: string;
18
+ listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, exporter: Exporter): void;
19
+ }
20
+ export declare class CellRemoveEventProducer {
18
21
  static id: string;
19
- listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, logNotebookContentEvent: boolean): void;
22
+ listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, exporter: Exporter): void;
20
23
  }
21
24
  export declare class ClipboardCopyEventProducer {
22
25
  static id: string;
23
- listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, logNotebookContentEvent: boolean): void;
26
+ listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, exporter: Exporter): void;
24
27
  }
25
28
  export declare class ClipboardCutEventProducer {
26
29
  static id: string;
27
- listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, logNotebookContentEvent: boolean): void;
30
+ listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, exporter: Exporter): void;
28
31
  }
29
32
  export declare class ClipboardPasteEventProducer {
30
33
  static id: string;
31
- listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, logNotebookContentEvent: boolean): void;
34
+ listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, exporter: Exporter): void;
32
35
  }
33
- export declare class ActiveCellChangeEventProducer {
36
+ export declare class NotebookHiddenEventProducer {
34
37
  static id: string;
35
- listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, logNotebookContentEvent: boolean): void;
38
+ listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, exporter: Exporter): void;
36
39
  }
37
- export declare class NotebookSaveEventProducer {
40
+ export declare class NotebookOpenEventProducer {
38
41
  static id: string;
39
- listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, logNotebookContentEvent: boolean): void;
42
+ private produced;
43
+ listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, exporter: Exporter): Promise<void>;
40
44
  }
41
- export declare class CellExecuteEventProducer {
45
+ export declare class NotebookSaveEventProducer {
42
46
  static id: string;
43
- listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, logNotebookContentEvent: boolean): void;
47
+ listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, exporter: Exporter): void;
44
48
  }
45
- export declare class CellAddEventProducer {
49
+ export declare class NotebookScrollProducer {
46
50
  static id: string;
47
- listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, logNotebookContentEvent: boolean): void;
51
+ private timeout;
52
+ listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, exporter: Exporter): void;
48
53
  }
49
- export declare class CellRemoveEventProducer {
54
+ export declare class NotebookVisibleEventProducer {
50
55
  static id: string;
51
- listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, logNotebookContentEvent: boolean): void;
56
+ listen(notebookPanel: NotebookPanel, pioneer: IJupyterLabPioneer, exporter: Exporter): void;
52
57
  }
53
- export declare const producerCollection: (typeof NotebookVisibleEventProducer)[];
58
+ export declare const producerCollection: (typeof ActiveCellChangeEventProducer)[];
package/lib/producer.js CHANGED
@@ -1,103 +1,156 @@
1
1
  import { NotebookActions } from '@jupyterlab/notebook';
2
+ import { EditorView } from '@codemirror/view';
2
3
  import { requestAPI } from './handler';
3
- class NotebookOpenEventProducer {
4
- constructor() {
5
- this.produced = false;
6
- }
7
- async listen(notebookPanel, pioneer, logNotebookContentEvent) {
8
- if (!this.produced) {
9
- const event = {
10
- eventName: NotebookOpenEventProducer.id,
11
- eventTime: Date.now(),
12
- eventInfo: {
13
- environ: await requestAPI('environ')
14
- }
15
- };
16
- await pioneer.publishEvent(notebookPanel, event, logNotebookContentEvent);
17
- this.produced = true;
18
- }
4
+ class ActiveCellChangeEventProducer {
5
+ listen(notebookPanel, pioneer, exporter) {
6
+ notebookPanel.content.activeCellChanged.connect(async (_, cell) => {
7
+ var _a, _b;
8
+ if (cell && notebookPanel.content.widgets) {
9
+ const activatedCell = {
10
+ id: cell === null || cell === void 0 ? void 0 : cell.model.id,
11
+ index: notebookPanel.content.widgets.findIndex(value => value === cell)
12
+ };
13
+ const event = {
14
+ eventName: ActiveCellChangeEventProducer.id,
15
+ eventTime: Date.now(),
16
+ eventInfo: {
17
+ cells: [activatedCell] // activated cell
18
+ }
19
+ };
20
+ await pioneer.publishEvent(notebookPanel, event, (_b = (_a = exporter.activeEvents) === null || _a === void 0 ? void 0 : _a.find(o => o.name == ActiveCellChangeEventProducer.id)) === null || _b === void 0 ? void 0 : _b.logWholeNotebook, exporter);
21
+ }
22
+ });
19
23
  }
20
24
  }
21
- NotebookOpenEventProducer.id = 'NotebookOpenEvent';
22
- export { NotebookOpenEventProducer };
23
- const getVisibleCells = (notebookPanel) => {
24
- const visibleCells = [];
25
- for (let index = 0; index < notebookPanel.content.widgets.length; index++) {
26
- const cell = notebookPanel.content.widgets[index];
27
- const cellTop = cell.node.offsetTop;
28
- const cellBottom = cell.node.offsetTop + cell.node.offsetHeight;
29
- const viewTop = notebookPanel.content.node.scrollTop;
30
- const viewBottom = notebookPanel.content.node.scrollTop +
31
- notebookPanel.content.node.clientHeight;
32
- if (cellTop <= viewBottom && cellBottom >= viewTop) {
33
- visibleCells.push({
34
- id: cell.model.id,
35
- index: index
36
- });
37
- }
38
- }
39
- return visibleCells;
40
- };
41
- class NotebookScrollProducer {
42
- constructor() {
43
- this.timeout = 0;
25
+ ActiveCellChangeEventProducer.id = 'ActiveCellChangeEvent';
26
+ export { ActiveCellChangeEventProducer };
27
+ class CellAddEventProducer {
28
+ listen(notebookPanel, pioneer, exporter) {
29
+ var _a;
30
+ (_a = notebookPanel.content.model) === null || _a === void 0 ? void 0 : _a.cells.changed.connect(async (_, args) => {
31
+ var _a, _b;
32
+ if (args.type === 'add') {
33
+ const addedCell = {
34
+ id: args.newValues[0].id,
35
+ index: args.newIndex
36
+ };
37
+ const event = {
38
+ eventName: CellAddEventProducer.id,
39
+ eventTime: Date.now(),
40
+ eventInfo: {
41
+ cells: [addedCell]
42
+ }
43
+ };
44
+ await pioneer.publishEvent(notebookPanel, event, (_b = (_a = exporter.activeEvents) === null || _a === void 0 ? void 0 : _a.find(o => o.name == CellAddEventProducer.id)) === null || _b === void 0 ? void 0 : _b.logWholeNotebook, exporter);
45
+ }
46
+ });
44
47
  }
45
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
46
- notebookPanel.content.node.addEventListener('scroll', async (e) => {
47
- e.stopPropagation();
48
- clearTimeout(this.timeout);
49
- await new Promise(resolve => (this.timeout = window.setTimeout(resolve, 1500))); // wait 1.5 seconds before preceding
48
+ }
49
+ CellAddEventProducer.id = 'CellAddEvent';
50
+ export { CellAddEventProducer };
51
+ class CellEditEventProducer {
52
+ listen(notebookPanel, pioneer, exporter) {
53
+ var _a, _b;
54
+ const sendDoc = async (_, cell) => {
55
+ var _a, _b, _c, _d;
56
+ await (cell === null || cell === void 0 ? void 0 : cell.ready); // wait until cell is ready, to prevent errors when creating new cells
57
+ const editor = cell === null || cell === void 0 ? void 0 : cell.editor;
50
58
  const event = {
51
- eventName: NotebookScrollProducer.id,
59
+ eventName: CellEditEventProducer.id,
52
60
  eventTime: Date.now(),
53
61
  eventInfo: {
54
- cells: getVisibleCells(notebookPanel)
62
+ index: notebookPanel.content.widgets.findIndex(value => value === cell),
63
+ doc: (_b = (_a = editor === null || editor === void 0 ? void 0 : editor.state) === null || _a === void 0 ? void 0 : _a.doc) === null || _b === void 0 ? void 0 : _b.toJSON() // send entire cell content if this is a new cell
55
64
  }
56
65
  };
57
- await pioneer.publishEvent(notebookPanel, event, logNotebookContentEvent);
58
- });
66
+ await pioneer.publishEvent(notebookPanel, event, (_d = (_c = exporter.activeEvents) === null || _c === void 0 ? void 0 : _c.find(o => o.name == CellEditEventProducer.id)) === null || _d === void 0 ? void 0 : _d.logWholeNotebook, exporter);
67
+ };
68
+ const addDocChangeListener = async (cell) => {
69
+ await (cell === null || cell === void 0 ? void 0 : cell.ready); // wait until cell is ready, to prevent errors when creating new cells
70
+ const editor = cell === null || cell === void 0 ? void 0 : cell.editor;
71
+ editor === null || editor === void 0 ? void 0 : editor.injectExtension(EditorView.updateListener.of(async (v) => {
72
+ if (v.docChanged) {
73
+ const event = {
74
+ eventName: CellEditEventProducer.id,
75
+ eventTime: Date.now(),
76
+ eventInfo: {
77
+ index: notebookPanel.content.widgets.findIndex(value => value === cell),
78
+ changes: v.changes.toJSON() // send changes
79
+ }
80
+ };
81
+ await pioneer.publishEvent(notebookPanel, event, false, // do not log whole notebook for doc changes
82
+ exporter);
83
+ }
84
+ }));
85
+ };
86
+ (_a = notebookPanel === null || notebookPanel === void 0 ? void 0 : notebookPanel.content) === null || _a === void 0 ? void 0 : _a.widgets.forEach(cell => {
87
+ addDocChangeListener(cell);
88
+ }); // add listener to existing cells
89
+ sendDoc(notebookPanel.content, notebookPanel.content.activeCell); // send initial active cell content
90
+ (_b = notebookPanel.content.model) === null || _b === void 0 ? void 0 : _b.cells.changed.connect(async (_, args) => {
91
+ var _a;
92
+ if (args.type === 'add') {
93
+ addDocChangeListener((_a = notebookPanel === null || notebookPanel === void 0 ? void 0 : notebookPanel.content) === null || _a === void 0 ? void 0 : _a.widgets[args.newIndex]);
94
+ }
95
+ }); // add doc change listener to cells created after initialization
96
+ notebookPanel.content.activeCellChanged.connect(sendDoc); // send active cell content when active cell changes
59
97
  }
60
98
  }
61
- NotebookScrollProducer.id = 'NotebookScrollEvent';
62
- export { NotebookScrollProducer };
63
- class NotebookVisibleEventProducer {
64
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
65
- document.addEventListener('visibilitychange', async () => {
66
- if (document.visibilityState === 'visible' && document.contains(notebookPanel.node)) {
99
+ CellEditEventProducer.id = 'CellEditEvent';
100
+ export { CellEditEventProducer };
101
+ class CellExecuteEventProducer {
102
+ listen(notebookPanel, pioneer, exporter) {
103
+ NotebookActions.executed.connect(async (_, args) => {
104
+ var _a, _b;
105
+ if (notebookPanel.content === args.notebook) {
106
+ const executedCell = {
107
+ id: args.cell.model.id,
108
+ index: args.notebook.widgets.findIndex(value => value == args.cell)
109
+ };
67
110
  const event = {
68
- eventName: NotebookVisibleEventProducer.id,
111
+ eventName: CellExecuteEventProducer.id,
69
112
  eventTime: Date.now(),
70
113
  eventInfo: {
71
- cells: getVisibleCells(notebookPanel)
114
+ cells: [executedCell],
115
+ success: args.success,
116
+ kernelError: args.success ? null : args.error
72
117
  }
73
118
  };
74
- await pioneer.publishEvent(notebookPanel, event, logNotebookContentEvent);
119
+ await pioneer.publishEvent(notebookPanel, event, (_b = (_a = exporter.activeEvents) === null || _a === void 0 ? void 0 : _a.find(o => o.name == CellExecuteEventProducer.id)) === null || _b === void 0 ? void 0 : _b.logWholeNotebook, exporter);
75
120
  }
76
121
  });
77
122
  }
78
123
  }
79
- NotebookVisibleEventProducer.id = 'NotebookVisibleEvent';
80
- export { NotebookVisibleEventProducer };
81
- class NotebookHiddenEventProducer {
82
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
83
- document.addEventListener('visibilitychange', async (e) => {
84
- if (document.visibilityState === 'hidden' && document.contains(notebookPanel.node)) {
124
+ CellExecuteEventProducer.id = 'CellExecuteEvent';
125
+ export { CellExecuteEventProducer };
126
+ class CellRemoveEventProducer {
127
+ listen(notebookPanel, pioneer, exporter) {
128
+ var _a;
129
+ (_a = notebookPanel.content.model) === null || _a === void 0 ? void 0 : _a.cells.changed.connect(async (_, args) => {
130
+ var _a, _b;
131
+ if (args.type === 'remove') {
132
+ const removedCell = {
133
+ newIndex: args.newIndex,
134
+ oldIndex: args.oldIndex
135
+ };
85
136
  const event = {
86
- eventName: NotebookHiddenEventProducer.id,
137
+ eventName: CellRemoveEventProducer.id,
87
138
  eventTime: Date.now(),
88
- eventInfo: null
139
+ eventInfo: {
140
+ cells: [removedCell]
141
+ }
89
142
  };
90
- await pioneer.publishEvent(notebookPanel, event, logNotebookContentEvent);
143
+ await pioneer.publishEvent(notebookPanel, event, (_b = (_a = exporter.activeEvents) === null || _a === void 0 ? void 0 : _a.find(o => o.name == CellRemoveEventProducer.id)) === null || _b === void 0 ? void 0 : _b.logWholeNotebook, exporter);
91
144
  }
92
145
  });
93
146
  }
94
147
  }
95
- NotebookHiddenEventProducer.id = 'NotebookHiddenEvent';
96
- export { NotebookHiddenEventProducer };
148
+ CellRemoveEventProducer.id = 'CellRemoveEvent';
149
+ export { CellRemoveEventProducer };
97
150
  class ClipboardCopyEventProducer {
98
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
151
+ listen(notebookPanel, pioneer, exporter) {
99
152
  notebookPanel.node.addEventListener('copy', async () => {
100
- var _a, _b;
153
+ var _a, _b, _c, _d;
101
154
  const cell = {
102
155
  id: (_a = notebookPanel.content.activeCell) === null || _a === void 0 ? void 0 : _a.model.id,
103
156
  index: notebookPanel.content.widgets.findIndex(value => value === notebookPanel.content.activeCell)
@@ -111,16 +164,16 @@ class ClipboardCopyEventProducer {
111
164
  selection: text
112
165
  }
113
166
  };
114
- await pioneer.publishEvent(notebookPanel, event, logNotebookContentEvent);
167
+ await pioneer.publishEvent(notebookPanel, event, (_d = (_c = exporter.activeEvents) === null || _c === void 0 ? void 0 : _c.find(o => o.name == ClipboardCopyEventProducer.id)) === null || _d === void 0 ? void 0 : _d.logWholeNotebook, exporter);
115
168
  });
116
169
  }
117
170
  }
118
171
  ClipboardCopyEventProducer.id = 'ClipboardCopyEvent';
119
172
  export { ClipboardCopyEventProducer };
120
173
  class ClipboardCutEventProducer {
121
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
174
+ listen(notebookPanel, pioneer, exporter) {
122
175
  notebookPanel.node.addEventListener('cut', async () => {
123
- var _a, _b;
176
+ var _a, _b, _c, _d;
124
177
  const cell = {
125
178
  id: (_a = notebookPanel.content.activeCell) === null || _a === void 0 ? void 0 : _a.model.id,
126
179
  index: notebookPanel.content.widgets.findIndex(value => value === notebookPanel.content.activeCell)
@@ -134,16 +187,16 @@ class ClipboardCutEventProducer {
134
187
  selection: text
135
188
  }
136
189
  };
137
- await pioneer.publishEvent(notebookPanel, event, logNotebookContentEvent);
190
+ await pioneer.publishEvent(notebookPanel, event, (_d = (_c = exporter.activeEvents) === null || _c === void 0 ? void 0 : _c.find(o => o.name == ClipboardCutEventProducer.id)) === null || _d === void 0 ? void 0 : _d.logWholeNotebook, exporter);
138
191
  });
139
192
  }
140
193
  }
141
194
  ClipboardCutEventProducer.id = 'ClipboardCutEvent';
142
195
  export { ClipboardCutEventProducer };
143
196
  class ClipboardPasteEventProducer {
144
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
197
+ listen(notebookPanel, pioneer, exporter) {
145
198
  notebookPanel.node.addEventListener('paste', async (e) => {
146
- var _a;
199
+ var _a, _b, _c;
147
200
  const cell = {
148
201
  id: (_a = notebookPanel.content.activeCell) === null || _a === void 0 ? void 0 : _a.model.id,
149
202
  index: notebookPanel.content.widgets.findIndex(value => value === notebookPanel.content.activeCell)
@@ -157,131 +210,141 @@ class ClipboardPasteEventProducer {
157
210
  selection: text
158
211
  }
159
212
  };
160
- await pioneer.publishEvent(notebookPanel, event, logNotebookContentEvent);
213
+ await pioneer.publishEvent(notebookPanel, event, (_c = (_b = exporter.activeEvents) === null || _b === void 0 ? void 0 : _b.find(o => o.name == ClipboardPasteEventProducer.id)) === null || _c === void 0 ? void 0 : _c.logWholeNotebook, exporter);
161
214
  });
162
215
  }
163
216
  }
164
217
  ClipboardPasteEventProducer.id = 'ClipboardPasteEvent';
165
218
  export { ClipboardPasteEventProducer };
166
- class ActiveCellChangeEventProducer {
167
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
168
- notebookPanel.content.activeCellChanged.connect(async (_, cell) => {
169
- if (cell && notebookPanel.content.widgets) {
170
- const activatedCell = {
171
- id: cell === null || cell === void 0 ? void 0 : cell.model.id,
172
- index: notebookPanel.content.widgets.findIndex(value => value === cell)
173
- };
219
+ class NotebookHiddenEventProducer {
220
+ listen(notebookPanel, pioneer, exporter) {
221
+ document.addEventListener('visibilitychange', async (e) => {
222
+ var _a, _b;
223
+ if (document.visibilityState === 'hidden' &&
224
+ document.contains(notebookPanel.node)) {
174
225
  const event = {
175
- eventName: ActiveCellChangeEventProducer.id,
226
+ eventName: NotebookHiddenEventProducer.id,
176
227
  eventTime: Date.now(),
177
- eventInfo: {
178
- cells: [activatedCell] // activated cell
179
- }
228
+ eventInfo: null
180
229
  };
181
- await pioneer.publishEvent(notebookPanel, event, logNotebookContentEvent);
230
+ await pioneer.publishEvent(notebookPanel, event, (_b = (_a = exporter.activeEvents) === null || _a === void 0 ? void 0 : _a.find(o => o.name == NotebookHiddenEventProducer.id)) === null || _b === void 0 ? void 0 : _b.logWholeNotebook, exporter);
182
231
  }
183
232
  });
184
233
  }
185
234
  }
186
- ActiveCellChangeEventProducer.id = 'ActiveCellChangeEvent';
187
- export { ActiveCellChangeEventProducer };
235
+ NotebookHiddenEventProducer.id = 'NotebookHiddenEvent';
236
+ export { NotebookHiddenEventProducer };
237
+ class NotebookOpenEventProducer {
238
+ constructor() {
239
+ this.produced = false;
240
+ }
241
+ async listen(notebookPanel, pioneer, exporter) {
242
+ var _a, _b;
243
+ if (!this.produced) {
244
+ const event = {
245
+ eventName: NotebookOpenEventProducer.id,
246
+ eventTime: Date.now(),
247
+ eventInfo: {
248
+ environ: await requestAPI('environ')
249
+ }
250
+ };
251
+ await pioneer.publishEvent(notebookPanel, event, (_b = (_a = exporter.activeEvents) === null || _a === void 0 ? void 0 : _a.find(o => o.name == NotebookOpenEventProducer.id)) === null || _b === void 0 ? void 0 : _b.logWholeNotebook, exporter);
252
+ this.produced = true;
253
+ }
254
+ }
255
+ }
256
+ NotebookOpenEventProducer.id = 'NotebookOpenEvent';
257
+ export { NotebookOpenEventProducer };
188
258
  class NotebookSaveEventProducer {
189
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
259
+ listen(notebookPanel, pioneer, exporter) {
190
260
  notebookPanel.context.saveState.connect(async (_, saveState) => {
261
+ var _a, _b;
191
262
  if (saveState.match('completed')) {
192
263
  const event = {
193
264
  eventName: NotebookSaveEventProducer.id,
194
265
  eventTime: Date.now(),
195
266
  eventInfo: null
196
267
  };
197
- await pioneer.publishEvent(notebookPanel, event, logNotebookContentEvent);
268
+ await pioneer.publishEvent(notebookPanel, event, (_b = (_a = exporter.activeEvents) === null || _a === void 0 ? void 0 : _a.find(o => o.name == NotebookSaveEventProducer.id)) === null || _b === void 0 ? void 0 : _b.logWholeNotebook, exporter);
198
269
  }
199
270
  });
200
271
  }
201
272
  }
202
273
  NotebookSaveEventProducer.id = 'NotebookSaveEvent';
203
274
  export { NotebookSaveEventProducer };
204
- class CellExecuteEventProducer {
205
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
206
- NotebookActions.executed.connect(async (_, args) => {
207
- if (notebookPanel.content === args.notebook) {
208
- const executedCell = {
209
- id: args.cell.model.id,
210
- index: args.notebook.widgets.findIndex(value => value == args.cell)
211
- };
212
- const event = {
213
- eventName: CellExecuteEventProducer.id,
214
- eventTime: Date.now(),
215
- eventInfo: {
216
- cells: [executedCell],
217
- success: args.success,
218
- kernelError: args.success ? null : args.error
219
- }
220
- };
221
- await pioneer.publishEvent(notebookPanel, event, logNotebookContentEvent);
222
- }
223
- });
275
+ const getVisibleCells = (notebookPanel) => {
276
+ const visibleCells = [];
277
+ for (let index = 0; index < notebookPanel.content.widgets.length; index++) {
278
+ const cell = notebookPanel.content.widgets[index];
279
+ const cellTop = cell.node.offsetTop;
280
+ const cellBottom = cell.node.offsetTop + cell.node.offsetHeight;
281
+ const viewTop = notebookPanel.content.node.scrollTop;
282
+ const viewBottom = notebookPanel.content.node.scrollTop +
283
+ notebookPanel.content.node.clientHeight;
284
+ if (cellTop <= viewBottom && cellBottom >= viewTop) {
285
+ visibleCells.push({
286
+ id: cell.model.id,
287
+ index: index
288
+ });
289
+ }
224
290
  }
225
- }
226
- CellExecuteEventProducer.id = 'CellExecuteEvent';
227
- export { CellExecuteEventProducer };
228
- class CellAddEventProducer {
229
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
230
- var _a;
231
- (_a = notebookPanel.content.model) === null || _a === void 0 ? void 0 : _a.cells.changed.connect(async (_, args) => {
232
- if (args.type === 'add') {
233
- const addedCell = {
234
- id: args.newValues[0].id,
235
- index: args.newIndex
236
- };
237
- const event = {
238
- eventName: CellAddEventProducer.id,
239
- eventTime: Date.now(),
240
- eventInfo: {
241
- cells: [addedCell]
242
- }
243
- };
244
- await pioneer.publishEvent(notebookPanel, event, logNotebookContentEvent);
245
- }
291
+ return visibleCells;
292
+ };
293
+ class NotebookScrollProducer {
294
+ constructor() {
295
+ this.timeout = 0;
296
+ }
297
+ listen(notebookPanel, pioneer, exporter) {
298
+ notebookPanel.content.node.addEventListener('scroll', async (e) => {
299
+ var _a, _b;
300
+ e.stopPropagation();
301
+ clearTimeout(this.timeout);
302
+ await new Promise(resolve => (this.timeout = window.setTimeout(resolve, 1500))); // wait 1.5 seconds before preceding
303
+ const event = {
304
+ eventName: NotebookScrollProducer.id,
305
+ eventTime: Date.now(),
306
+ eventInfo: {
307
+ cells: getVisibleCells(notebookPanel)
308
+ }
309
+ };
310
+ await pioneer.publishEvent(notebookPanel, event, (_b = (_a = exporter.activeEvents) === null || _a === void 0 ? void 0 : _a.find(o => o.name == NotebookScrollProducer.id)) === null || _b === void 0 ? void 0 : _b.logWholeNotebook, exporter);
246
311
  });
247
312
  }
248
313
  }
249
- CellAddEventProducer.id = 'CellAddEvent';
250
- export { CellAddEventProducer };
251
- class CellRemoveEventProducer {
252
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
253
- var _a;
254
- (_a = notebookPanel.content.model) === null || _a === void 0 ? void 0 : _a.cells.changed.connect(async (_, args) => {
255
- if (args.type === 'remove') {
256
- const removedCell = {
257
- newIndex: args.newIndex,
258
- oldIndex: args.oldIndex
259
- };
314
+ NotebookScrollProducer.id = 'NotebookScrollEvent';
315
+ export { NotebookScrollProducer };
316
+ class NotebookVisibleEventProducer {
317
+ listen(notebookPanel, pioneer, exporter) {
318
+ document.addEventListener('visibilitychange', async () => {
319
+ var _a, _b;
320
+ if (document.visibilityState === 'visible' &&
321
+ document.contains(notebookPanel.node)) {
260
322
  const event = {
261
- eventName: CellRemoveEventProducer.id,
323
+ eventName: NotebookVisibleEventProducer.id,
262
324
  eventTime: Date.now(),
263
325
  eventInfo: {
264
- cells: [removedCell]
326
+ cells: getVisibleCells(notebookPanel)
265
327
  }
266
328
  };
267
- await pioneer.publishEvent(notebookPanel, event, logNotebookContentEvent);
329
+ await pioneer.publishEvent(notebookPanel, event, (_b = (_a = exporter.activeEvents) === null || _a === void 0 ? void 0 : _a.find(o => o.name == NotebookVisibleEventProducer.id)) === null || _b === void 0 ? void 0 : _b.logWholeNotebook, exporter);
268
330
  }
269
331
  });
270
332
  }
271
333
  }
272
- CellRemoveEventProducer.id = 'CellRemoveEvent';
273
- export { CellRemoveEventProducer };
334
+ NotebookVisibleEventProducer.id = 'NotebookVisibleEvent';
335
+ export { NotebookVisibleEventProducer };
274
336
  export const producerCollection = [
275
- NotebookOpenEventProducer,
276
- NotebookScrollProducer,
277
- NotebookVisibleEventProducer,
278
- NotebookHiddenEventProducer,
337
+ ActiveCellChangeEventProducer,
338
+ CellAddEventProducer,
339
+ CellExecuteEventProducer,
340
+ CellRemoveEventProducer,
341
+ CellEditEventProducer,
279
342
  ClipboardCopyEventProducer,
280
343
  ClipboardCutEventProducer,
281
344
  ClipboardPasteEventProducer,
282
- ActiveCellChangeEventProducer,
345
+ NotebookHiddenEventProducer,
346
+ NotebookOpenEventProducer,
283
347
  NotebookSaveEventProducer,
284
- CellExecuteEventProducer,
285
- CellAddEventProducer,
286
- CellRemoveEventProducer
348
+ NotebookScrollProducer,
349
+ NotebookVisibleEventProducer
287
350
  ];
package/lib/types.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ export interface ActiveEvent {
2
+ name: string;
3
+ logWholeNotebook: boolean;
4
+ }
5
+ export interface ExporterArgs {
6
+ id: string;
7
+ url?: string;
8
+ params?: Object;
9
+ path?: string;
10
+ env?: Object[];
11
+ }
12
+ export interface Exporter {
13
+ type: string;
14
+ args?: ExporterArgs;
15
+ activeEvents?: ActiveEvent[];
16
+ }
17
+ export interface Config {
18
+ activeEvents: ActiveEvent[];
19
+ exporters: Exporter[];
20
+ }
package/lib/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyterlab-pioneer",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "A JupyterLab extension.",
5
5
  "keywords": [
6
6
  "jupyter",
package/lib/router.d.ts DELETED
@@ -1,11 +0,0 @@
1
- import { NotebookPanel } from '@jupyterlab/notebook';
2
- export declare class Router {
3
- /**
4
- * Send event data to exporters defined in the configuration file.
5
- *
6
- * @param {NotebookPanel} notebookPanel The notebook panel the extension currently listens to.
7
- * @param {Object} eventDetail An object containing event details
8
- * @param {Boolean} logNotebookContent A boolean indicating whether to log the entire notebook or not
9
- */
10
- publishEvent(notebookPanel: NotebookPanel, eventDetail: Object, logNotebookContent?: Boolean): Promise<void>;
11
- }
package/lib/router.js DELETED
@@ -1,33 +0,0 @@
1
- import { requestAPI } from './handler';
2
- export class Router {
3
- /**
4
- * Send event data to exporters defined in the configuration file.
5
- *
6
- * @param {NotebookPanel} notebookPanel The notebook panel the extension currently listens to.
7
- * @param {Object} eventDetail An object containing event details
8
- * @param {Boolean} logNotebookContent A boolean indicating whether to log the entire notebook or not
9
- */
10
- async publishEvent(notebookPanel, eventDetail, logNotebookContent) {
11
- var _a, _b;
12
- if (!notebookPanel) {
13
- throw Error('router is listening to a null notebook panel');
14
- }
15
- // Construct data
16
- const requestBody = {
17
- eventDetail: eventDetail,
18
- notebookState: {
19
- sessionID: (_a = notebookPanel === null || notebookPanel === void 0 ? void 0 : notebookPanel.sessionContext.session) === null || _a === void 0 ? void 0 : _a.id,
20
- notebookPath: notebookPanel === null || notebookPanel === void 0 ? void 0 : notebookPanel.context.path,
21
- notebookContent: logNotebookContent
22
- ? (_b = notebookPanel === null || notebookPanel === void 0 ? void 0 : notebookPanel.model) === null || _b === void 0 ? void 0 : _b.toJSON()
23
- : null // decide whether to log the entire notebook
24
- }
25
- };
26
- // Send data to exporters
27
- const response = await requestAPI('export', {
28
- method: 'POST',
29
- body: JSON.stringify(requestBody)
30
- });
31
- console.log(response);
32
- }
33
- }