jupyterlab-pioneer 0.1.5 → 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,13 +1,20 @@
1
1
  import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
+ import { NotebookPanel } from '@jupyterlab/notebook';
2
3
  import { Token } from '@lumino/coreutils';
3
- import { Router } from './router';
4
+ import { Exporter } from './types';
4
5
  export declare const IJupyterLabPioneer: Token<IJupyterLabPioneer>;
5
6
  export interface IJupyterLabPioneer {
6
- router: Router;
7
+ /**
8
+ * Send event data to exporters defined in the configuration file.
9
+ *
10
+ * @param {NotebookPanel} notebookPanel The notebook panel the extension currently listens to.
11
+ * @param {Object} eventDetail An object containing event details
12
+ * @param {Boolean} logNotebookContent A boolean indicating whether to log the entire notebook or not
13
+ */
14
+ publishEvent(notebookPanel: NotebookPanel, eventDetail: Object, logWholeNotebook?: Boolean, exporter?: Exporter): Promise<void>;
7
15
  }
8
16
  declare class JupyterLabPioneer implements IJupyterLabPioneer {
9
- router: Router;
10
- constructor();
17
+ publishEvent(notebookPanel: NotebookPanel, eventDetail: Object, logWholeNotebook?: Boolean, exporter?: Exporter): Promise<void>;
11
18
  }
12
19
  declare const plugin: JupyterFrontEndPlugin<JupyterLabPioneer>;
13
20
  export default plugin;
package/lib/index.js CHANGED
@@ -1,13 +1,31 @@
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
- constructor() {
10
- this.router = new Router();
8
+ async publishEvent(notebookPanel, eventDetail, logWholeNotebook, exporter) {
9
+ var _a, _b;
10
+ if (!notebookPanel) {
11
+ throw Error('router is listening to a null notebook panel');
12
+ }
13
+ const requestBody = {
14
+ eventDetail: eventDetail,
15
+ notebookState: {
16
+ sessionID: (_a = notebookPanel === null || notebookPanel === void 0 ? void 0 : notebookPanel.sessionContext.session) === null || _a === void 0 ? void 0 : _a.id,
17
+ notebookPath: notebookPanel === null || notebookPanel === void 0 ? void 0 : notebookPanel.context.path,
18
+ notebookContent: logWholeNotebook
19
+ ? (_b = notebookPanel === null || notebookPanel === void 0 ? void 0 : notebookPanel.model) === null || _b === void 0 ? void 0 : _b.toJSON()
20
+ : null // decide whether to log the entire notebook
21
+ },
22
+ exporter: exporter
23
+ };
24
+ const response = await requestAPI('export', {
25
+ method: 'POST',
26
+ body: JSON.stringify(requestBody)
27
+ });
28
+ console.log(response);
11
29
  }
12
30
  }
13
31
  const plugin = {
@@ -18,15 +36,34 @@ const plugin = {
18
36
  activate: async (app, notebookTracker) => {
19
37
  const version = await requestAPI('version');
20
38
  console.log(`${PLUGIN_ID}: ${version}`);
21
- const config = await requestAPI('config');
39
+ const config = (await requestAPI('config'));
22
40
  const pioneer = new JupyterLabPioneer();
23
41
  notebookTracker.widgetAdded.connect(async (_, notebookPanel) => {
24
- await notebookPanel.sessionContext.ready; // wait until session id is created
25
- await pioneer.router.loadNotebookPanel(notebookPanel);
26
- producerCollection.forEach(producer => {
27
- if (config.activeEvents.includes(producer.id)) {
28
- new producer().listen(notebookPanel, pioneer, config.logNotebookContentEvents.includes(producer.id));
29
- }
42
+ var _a;
43
+ await notebookPanel.revealed;
44
+ await notebookPanel.sessionContext.ready;
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
+ });
30
67
  });
31
68
  });
32
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, 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, 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(_: any, 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,102 +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(_, 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.router.publishEvent(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.router.publishEvent(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') {
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.router.publishEvent(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(_, pioneer, logNotebookContentEvent) {
83
- document.addEventListener('visibilitychange', async (e) => {
84
- if (document.visibilityState === 'hidden') {
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,
87
- eventTime: Date.now()
137
+ eventName: CellRemoveEventProducer.id,
138
+ eventTime: Date.now(),
139
+ eventInfo: {
140
+ cells: [removedCell]
141
+ }
88
142
  };
89
- await pioneer.router.publishEvent(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);
90
144
  }
91
145
  });
92
146
  }
93
147
  }
94
- NotebookHiddenEventProducer.id = 'NotebookHiddenEvent';
95
- export { NotebookHiddenEventProducer };
148
+ CellRemoveEventProducer.id = 'CellRemoveEvent';
149
+ export { CellRemoveEventProducer };
96
150
  class ClipboardCopyEventProducer {
97
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
151
+ listen(notebookPanel, pioneer, exporter) {
98
152
  notebookPanel.node.addEventListener('copy', async () => {
99
- var _a, _b;
153
+ var _a, _b, _c, _d;
100
154
  const cell = {
101
155
  id: (_a = notebookPanel.content.activeCell) === null || _a === void 0 ? void 0 : _a.model.id,
102
156
  index: notebookPanel.content.widgets.findIndex(value => value === notebookPanel.content.activeCell)
@@ -110,16 +164,16 @@ class ClipboardCopyEventProducer {
110
164
  selection: text
111
165
  }
112
166
  };
113
- await pioneer.router.publishEvent(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);
114
168
  });
115
169
  }
116
170
  }
117
171
  ClipboardCopyEventProducer.id = 'ClipboardCopyEvent';
118
172
  export { ClipboardCopyEventProducer };
119
173
  class ClipboardCutEventProducer {
120
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
174
+ listen(notebookPanel, pioneer, exporter) {
121
175
  notebookPanel.node.addEventListener('cut', async () => {
122
- var _a, _b;
176
+ var _a, _b, _c, _d;
123
177
  const cell = {
124
178
  id: (_a = notebookPanel.content.activeCell) === null || _a === void 0 ? void 0 : _a.model.id,
125
179
  index: notebookPanel.content.widgets.findIndex(value => value === notebookPanel.content.activeCell)
@@ -133,16 +187,16 @@ class ClipboardCutEventProducer {
133
187
  selection: text
134
188
  }
135
189
  };
136
- await pioneer.router.publishEvent(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);
137
191
  });
138
192
  }
139
193
  }
140
194
  ClipboardCutEventProducer.id = 'ClipboardCutEvent';
141
195
  export { ClipboardCutEventProducer };
142
196
  class ClipboardPasteEventProducer {
143
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
197
+ listen(notebookPanel, pioneer, exporter) {
144
198
  notebookPanel.node.addEventListener('paste', async (e) => {
145
- var _a;
199
+ var _a, _b, _c;
146
200
  const cell = {
147
201
  id: (_a = notebookPanel.content.activeCell) === null || _a === void 0 ? void 0 : _a.model.id,
148
202
  index: notebookPanel.content.widgets.findIndex(value => value === notebookPanel.content.activeCell)
@@ -156,128 +210,141 @@ class ClipboardPasteEventProducer {
156
210
  selection: text
157
211
  }
158
212
  };
159
- await pioneer.router.publishEvent(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);
160
214
  });
161
215
  }
162
216
  }
163
217
  ClipboardPasteEventProducer.id = 'ClipboardPasteEvent';
164
218
  export { ClipboardPasteEventProducer };
165
- class ActiveCellChangeEventProducer {
166
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
167
- notebookPanel.content.activeCellChanged.connect(async (_, cell) => {
168
- if (cell && notebookPanel.content.widgets) {
169
- const activatedCell = {
170
- id: cell === null || cell === void 0 ? void 0 : cell.model.id,
171
- index: notebookPanel.content.widgets.findIndex(value => value === cell)
172
- };
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)) {
173
225
  const event = {
174
- eventName: ActiveCellChangeEventProducer.id,
226
+ eventName: NotebookHiddenEventProducer.id,
175
227
  eventTime: Date.now(),
176
- eventInfo: {
177
- cells: [activatedCell] // activated cell
178
- }
228
+ eventInfo: null
179
229
  };
180
- await pioneer.router.publishEvent(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);
181
231
  }
182
232
  });
183
233
  }
184
234
  }
185
- ActiveCellChangeEventProducer.id = 'ActiveCellChangeEvent';
186
- 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 };
187
258
  class NotebookSaveEventProducer {
188
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
259
+ listen(notebookPanel, pioneer, exporter) {
189
260
  notebookPanel.context.saveState.connect(async (_, saveState) => {
261
+ var _a, _b;
190
262
  if (saveState.match('completed')) {
191
263
  const event = {
192
264
  eventName: NotebookSaveEventProducer.id,
193
- eventTime: Date.now()
265
+ eventTime: Date.now(),
266
+ eventInfo: null
194
267
  };
195
- await pioneer.router.publishEvent(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);
196
269
  }
197
270
  });
198
271
  }
199
272
  }
200
273
  NotebookSaveEventProducer.id = 'NotebookSaveEvent';
201
274
  export { NotebookSaveEventProducer };
202
- class CellExecuteEventProducer {
203
- listen(_, pioneer, logNotebookContentEvent) {
204
- NotebookActions.executed.connect(async (_, args) => {
205
- const executedCell = {
206
- id: args.cell.model.id,
207
- index: args.notebook.widgets.findIndex(value => value == args.cell)
208
- };
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
+ }
290
+ }
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
209
303
  const event = {
210
- eventName: CellExecuteEventProducer.id,
304
+ eventName: NotebookScrollProducer.id,
211
305
  eventTime: Date.now(),
212
306
  eventInfo: {
213
- cells: [executedCell],
214
- success: args.success,
215
- kernelError: args.success ? null : args.error
307
+ cells: getVisibleCells(notebookPanel)
216
308
  }
217
309
  };
218
- await pioneer.router.publishEvent(event, logNotebookContentEvent);
219
- });
220
- }
221
- }
222
- CellExecuteEventProducer.id = 'CellExecuteEvent';
223
- export { CellExecuteEventProducer };
224
- class CellAddEventProducer {
225
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
226
- var _a;
227
- (_a = notebookPanel.content.model) === null || _a === void 0 ? void 0 : _a.cells.changed.connect(async (_, args) => {
228
- if (args.type === 'add') {
229
- const addedCell = {
230
- id: args.newValues[0].id,
231
- index: args.newIndex
232
- };
233
- const event = {
234
- eventName: CellAddEventProducer.id,
235
- eventTime: Date.now(),
236
- eventInfo: {
237
- cells: [addedCell]
238
- }
239
- };
240
- await pioneer.router.publishEvent(event, logNotebookContentEvent);
241
- }
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);
242
311
  });
243
312
  }
244
313
  }
245
- CellAddEventProducer.id = 'CellAddEvent';
246
- export { CellAddEventProducer };
247
- class CellRemoveEventProducer {
248
- listen(notebookPanel, pioneer, logNotebookContentEvent) {
249
- var _a;
250
- (_a = notebookPanel.content.model) === null || _a === void 0 ? void 0 : _a.cells.changed.connect(async (_, args) => {
251
- if (args.type === 'remove') {
252
- const removedCell = {
253
- newIndex: args.newIndex,
254
- oldIndex: args.oldIndex
255
- };
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)) {
256
322
  const event = {
257
- eventName: CellRemoveEventProducer.id,
323
+ eventName: NotebookVisibleEventProducer.id,
258
324
  eventTime: Date.now(),
259
325
  eventInfo: {
260
- cells: [removedCell]
326
+ cells: getVisibleCells(notebookPanel)
261
327
  }
262
328
  };
263
- await pioneer.router.publishEvent(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);
264
330
  }
265
331
  });
266
332
  }
267
333
  }
268
- CellRemoveEventProducer.id = 'CellRemoveEvent';
269
- export { CellRemoveEventProducer };
334
+ NotebookVisibleEventProducer.id = 'NotebookVisibleEvent';
335
+ export { NotebookVisibleEventProducer };
270
336
  export const producerCollection = [
271
- NotebookOpenEventProducer,
272
- NotebookScrollProducer,
273
- NotebookVisibleEventProducer,
274
- NotebookHiddenEventProducer,
337
+ ActiveCellChangeEventProducer,
338
+ CellAddEventProducer,
339
+ CellExecuteEventProducer,
340
+ CellRemoveEventProducer,
341
+ CellEditEventProducer,
275
342
  ClipboardCopyEventProducer,
276
343
  ClipboardCutEventProducer,
277
344
  ClipboardPasteEventProducer,
278
- ActiveCellChangeEventProducer,
345
+ NotebookHiddenEventProducer,
346
+ NotebookOpenEventProducer,
279
347
  NotebookSaveEventProducer,
280
- CellExecuteEventProducer,
281
- CellAddEventProducer,
282
- CellRemoveEventProducer
348
+ NotebookScrollProducer,
349
+ NotebookVisibleEventProducer
283
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.5",
3
+ "version": "0.1.7",
4
4
  "description": "A JupyterLab extension.",
5
5
  "keywords": [
6
6
  "jupyter",
package/lib/router.d.ts DELETED
@@ -1,18 +0,0 @@
1
- import { NotebookPanel } from '@jupyterlab/notebook';
2
- export declare class Router {
3
- private sessionID?;
4
- private notebookPanel?;
5
- /**
6
- * Load notebookPanel.
7
- *
8
- * @param {NotebookPanel} notebookPanel
9
- */
10
- loadNotebookPanel(notebookPanel: NotebookPanel): Promise<void>;
11
- /**
12
- * Send event data to exporters defined in the configuration file.
13
- *
14
- * @param {Object} eventDetail An object containing event details
15
- * @param {Boolean} logNotebookContent A boolean indicating whether to log the entire notebook or not
16
- */
17
- publishEvent(eventDetail: Object, logNotebookContent?: Boolean): Promise<void>;
18
- }
package/lib/router.js DELETED
@@ -1,45 +0,0 @@
1
- import { requestAPI } from './handler';
2
- export class Router {
3
- /**
4
- * Load notebookPanel.
5
- *
6
- * @param {NotebookPanel} notebookPanel
7
- */
8
- async loadNotebookPanel(notebookPanel) {
9
- this.notebookPanel = notebookPanel;
10
- }
11
- /**
12
- * Send event data to exporters defined in the configuration file.
13
- *
14
- * @param {Object} eventDetail An object containing event details
15
- * @param {Boolean} logNotebookContent A boolean indicating whether to log the entire notebook or not
16
- */
17
- async publishEvent(eventDetail, logNotebookContent) {
18
- var _a, _b, _c, _d, _e, _f, _g;
19
- if (!this.notebookPanel) {
20
- throw Error('router needs to load notebookPanel first.');
21
- }
22
- // Check if session id received is equal to the stored session id
23
- if (!this.sessionID ||
24
- this.sessionID !== ((_b = (_a = this.notebookPanel) === null || _a === void 0 ? void 0 : _a.sessionContext.session) === null || _b === void 0 ? void 0 : _b.id)) {
25
- this.sessionID = (_d = (_c = this.notebookPanel) === null || _c === void 0 ? void 0 : _c.sessionContext.session) === null || _d === void 0 ? void 0 : _d.id;
26
- }
27
- // Construct data
28
- const requestBody = {
29
- eventDetail: eventDetail,
30
- notebookState: {
31
- sessionID: this.sessionID,
32
- notebookPath: (_e = this.notebookPanel) === null || _e === void 0 ? void 0 : _e.context.path,
33
- notebookContent: logNotebookContent
34
- ? (_g = (_f = this.notebookPanel) === null || _f === void 0 ? void 0 : _f.model) === null || _g === void 0 ? void 0 : _g.toJSON()
35
- : null // decide whether to log the entire notebook
36
- }
37
- };
38
- // Send data to exporters
39
- const response = await requestAPI('export', {
40
- method: 'POST',
41
- body: JSON.stringify(requestBody)
42
- });
43
- console.log(response);
44
- }
45
- }