UNPKG

16.6 kBJavaScriptView Raw
1// Copyright (c) Jupyter Development Team.
2// Distributed under the terms of the Modified BSD License.
3import { translateKernelStatuses, VDomModel, VDomRenderer } from '@jupyterlab/apputils';
4import { nullTranslator } from '@jupyterlab/translation';
5import React from 'react';
6import { interactiveItem, ProgressCircle } from '@jupyterlab/statusbar';
7import { circleIcon, offlineBoltIcon } from '@jupyterlab/ui-components';
8import { KernelMessage } from '@jupyterlab/services';
9/**
10 * A react functional component for rendering execution indicator.
11 */
12export function ExecutionIndicatorComponent(props) {
13 const translator = props.translator || nullTranslator;
14 const kernelStatuses = translateKernelStatuses(translator);
15 const trans = translator.load('jupyterlab');
16 const state = props.state;
17 const showOnToolBar = props.displayOption.showOnToolBar;
18 const showProgress = props.displayOption.showProgress;
19 const tooltipClass = showOnToolBar ? 'down' : 'up';
20 const emptyDiv = React.createElement("div", null);
21 if (!state) {
22 return emptyDiv;
23 }
24 const kernelStatus = state.kernelStatus;
25 const circleIconProps = {
26 alignSelf: 'normal',
27 height: '24px'
28 };
29 const time = state.totalTime;
30 const scheduledCellNumber = state.scheduledCellNumber || 0;
31 const remainingCellNumber = state.scheduledCell.size || 0;
32 const executedCellNumber = scheduledCellNumber - remainingCellNumber;
33 let percentage = (100 * executedCellNumber) / scheduledCellNumber;
34 let displayClass = showProgress ? '' : 'hidden';
35 if (!showProgress && percentage < 100) {
36 percentage = 0;
37 }
38 const progressBar = (percentage) => (React.createElement(ProgressCircle, { progress: percentage, width: 16, height: 24 }));
39 const titleFactory = (translatedStatus) => trans.__('Kernel status: %1', translatedStatus);
40 const reactElement = (status, circle, popup) => (React.createElement("div", { className: 'jp-Notebook-ExecutionIndicator', title: showProgress ? '' : titleFactory(kernelStatuses[status]), "data-status": status },
41 circle,
42 React.createElement("div", { className: `jp-Notebook-ExecutionIndicator-tooltip ${tooltipClass} ${displayClass}` },
43 React.createElement("span", null,
44 " ",
45 titleFactory(kernelStatuses[status]),
46 " "),
47 popup)));
48 if (state.kernelStatus === 'connecting' ||
49 state.kernelStatus === 'disconnected' ||
50 state.kernelStatus === 'unknown') {
51 return reactElement(kernelStatus, React.createElement(offlineBoltIcon.react, Object.assign({}, circleIconProps)), []);
52 }
53 if (state.kernelStatus === 'starting' ||
54 state.kernelStatus === 'terminating' ||
55 state.kernelStatus === 'restarting' ||
56 state.kernelStatus === 'initializing') {
57 return reactElement(kernelStatus, React.createElement(circleIcon.react, Object.assign({}, circleIconProps)), []);
58 }
59 if (state.executionStatus === 'busy') {
60 return reactElement('busy', progressBar(percentage), [
61 React.createElement("span", { key: 0 }, trans.__(`Executed ${executedCellNumber}/${scheduledCellNumber} cells`)),
62 React.createElement("span", { key: 1 }, trans._n('Elapsed time: %1 second', 'Elapsed time: %1 seconds', time))
63 ]);
64 }
65 else {
66 // No cell is scheduled, fall back to the status of kernel
67 const progress = state.kernelStatus === 'busy' ? 0 : 100;
68 const popup = state.kernelStatus === 'busy' || time === 0
69 ? []
70 : [
71 React.createElement("span", { key: 0 }, trans._n('Executed %1 cell', 'Executed %1 cells', scheduledCellNumber)),
72 React.createElement("span", { key: 1 }, trans._n('Elapsed time: %1 second', 'Elapsed time: %1 seconds', time))
73 ];
74 return reactElement(state.kernelStatus, progressBar(progress), popup);
75 }
76}
77/**
78 * A VDomRenderer widget for displaying the execution status.
79 */
80export class ExecutionIndicator extends VDomRenderer {
81 /**
82 * Construct the kernel status widget.
83 */
84 constructor(translator, showProgress = true) {
85 super(new ExecutionIndicator.Model());
86 this.translator = translator || nullTranslator;
87 this.addClass(interactiveItem);
88 }
89 /**
90 * Render the execution status item.
91 */
92 render() {
93 if (this.model === null || !this.model.renderFlag) {
94 return React.createElement("div", null);
95 }
96 else {
97 const nb = this.model.currentNotebook;
98 if (!nb) {
99 return (React.createElement(ExecutionIndicatorComponent, { displayOption: this.model.displayOption, state: undefined, translator: this.translator }));
100 }
101 return (React.createElement(ExecutionIndicatorComponent, { displayOption: this.model.displayOption, state: this.model.executionState(nb), translator: this.translator }));
102 }
103 }
104}
105/**
106 * A namespace for ExecutionIndicator statics.
107 */
108(function (ExecutionIndicator) {
109 /**
110 * A VDomModel for the execution status indicator.
111 */
112 class Model extends VDomModel {
113 constructor() {
114 super();
115 /**
116 * A weak map to hold execution status of multiple notebooks.
117 */
118 this._notebookExecutionProgress = new WeakMap();
119 this._displayOption = { showOnToolBar: true, showProgress: true };
120 this._renderFlag = true;
121 }
122 /**
123 * Attach a notebook with session context to model in order to keep
124 * track of multiple notebooks. If a session context is already
125 * attached, only set current activated notebook to input.
126 *
127 * @param data - The notebook and session context to be attached to model
128 */
129 attachNotebook(data) {
130 var _a, _b, _c, _d;
131 if (data && data.content && data.context) {
132 const nb = data.content;
133 const context = data.context;
134 this._currentNotebook = nb;
135 if (!this._notebookExecutionProgress.has(nb)) {
136 this._notebookExecutionProgress.set(nb, {
137 executionStatus: 'idle',
138 kernelStatus: 'idle',
139 totalTime: 0,
140 interval: 0,
141 timeout: 0,
142 scheduledCell: new Set(),
143 scheduledCellNumber: 0,
144 needReset: true
145 });
146 const state = this._notebookExecutionProgress.get(nb);
147 const contextStatusChanged = (ctx) => {
148 if (state) {
149 state.kernelStatus = ctx.kernelDisplayStatus;
150 }
151 this.stateChanged.emit(void 0);
152 };
153 context.statusChanged.connect(contextStatusChanged, this);
154 const contextConnectionStatusChanged = (ctx) => {
155 if (state) {
156 state.kernelStatus = ctx.kernelDisplayStatus;
157 }
158 this.stateChanged.emit(void 0);
159 };
160 context.connectionStatusChanged.connect(contextConnectionStatusChanged, this);
161 context.disposed.connect(ctx => {
162 ctx.connectionStatusChanged.disconnect(contextConnectionStatusChanged, this);
163 ctx.statusChanged.disconnect(contextStatusChanged, this);
164 });
165 const handleKernelMsg = (sender, msg) => {
166 const message = msg.msg;
167 const msgId = message.header.msg_id;
168 if (message.header.msg_type === 'execute_request') {
169 // A cell code is scheduled for executing
170 this._cellScheduledCallback(nb, msgId);
171 }
172 else if (KernelMessage.isStatusMsg(message) &&
173 message.content.execution_state === 'idle') {
174 // Idle status message case.
175 const parentId = message.parent_header
176 .msg_id;
177 this._cellExecutedCallback(nb, parentId);
178 }
179 else if (message.header.msg_type === 'execute_input') {
180 // A cell code starts executing.
181 this._startTimer(nb);
182 }
183 };
184 (_b = (_a = context.session) === null || _a === void 0 ? void 0 : _a.kernel) === null || _b === void 0 ? void 0 : _b.anyMessage.connect(handleKernelMsg);
185 (_d = (_c = context.session) === null || _c === void 0 ? void 0 : _c.kernel) === null || _d === void 0 ? void 0 : _d.disposed.connect(kernel => kernel.anyMessage.disconnect(handleKernelMsg));
186 const kernelChangedSlot = (_, kernelData) => {
187 if (state) {
188 this._resetTime(state);
189 this.stateChanged.emit(void 0);
190 if (kernelData.newValue) {
191 kernelData.newValue.anyMessage.connect(handleKernelMsg);
192 }
193 }
194 };
195 context.kernelChanged.connect(kernelChangedSlot);
196 context.disposed.connect(ctx => ctx.kernelChanged.disconnect(kernelChangedSlot));
197 }
198 }
199 }
200 /**
201 * The current activated notebook in model.
202 */
203 get currentNotebook() {
204 return this._currentNotebook;
205 }
206 /**
207 * The display options for progress bar and elapsed time.
208 */
209 get displayOption() {
210 return this._displayOption;
211 }
212 /**
213 * Set the display options for progress bar and elapsed time.
214 *
215 * @param options - Options to be used
216 */
217 set displayOption(options) {
218 this._displayOption = options;
219 }
220 /**
221 * Get the execution state associated with a notebook.
222 *
223 * @param nb - The notebook used to identify execution
224 * state.
225 *
226 * @return - The associated execution state.
227 */
228 executionState(nb) {
229 return this._notebookExecutionProgress.get(nb);
230 }
231 /**
232 * The function is called on kernel's idle status message.
233 * It is used to keep track number of executed
234 * cell or Comm custom messages and the status of kernel.
235 *
236 * @param nb - The notebook which contains the executed code
237 * cell.
238 * @param msg_id - The id of message.
239 *
240 * ### Note
241 *
242 * To keep track of cells executed under 1 second,
243 * the execution state is marked as `needReset` 1 second after executing
244 * these cells. This `Timeout` will be cleared if there is any cell
245 * scheduled after that.
246 */
247 _cellExecutedCallback(nb, msg_id) {
248 const state = this._notebookExecutionProgress.get(nb);
249 if (state && state.scheduledCell.has(msg_id)) {
250 state.scheduledCell.delete(msg_id);
251 if (state.scheduledCell.size === 0) {
252 window.setTimeout(() => {
253 state.executionStatus = 'idle';
254 clearInterval(state.interval);
255 this.stateChanged.emit(void 0);
256 }, 150);
257 state.timeout = window.setTimeout(() => {
258 state.needReset = true;
259 }, 1000);
260 }
261 }
262 }
263 /**
264 * This function is called on kernel's `execute_input` message to start
265 * the elapsed time counter.
266 *
267 * @param nb - The notebook which contains the scheduled execution request.
268 */
269 _startTimer(nb) {
270 const state = this._notebookExecutionProgress.get(nb);
271 if (state) {
272 if (state.executionStatus !== 'busy') {
273 state.executionStatus = 'busy';
274 clearTimeout(state.timeout);
275 this.stateChanged.emit(void 0);
276 state.interval = window.setInterval(() => {
277 this._tick(state);
278 }, 1000);
279 }
280 }
281 }
282 /**
283 * The function is called on kernel's `execute_request` message or Comm message, it is
284 * used to keep track number of scheduled cell or Comm execution message
285 * and the status of kernel.
286 *
287 * @param nb - The notebook which contains the scheduled code.
288 * cell
289 * @param msg_id - The id of message.
290 */
291 _cellScheduledCallback(nb, msg_id) {
292 const state = this._notebookExecutionProgress.get(nb);
293 if (state && !state.scheduledCell.has(msg_id)) {
294 if (state.needReset) {
295 this._resetTime(state);
296 }
297 state.scheduledCell.add(msg_id);
298 state.scheduledCellNumber += 1;
299 }
300 }
301 /**
302 * Increment the executed time of input execution state
303 * and emit `stateChanged` signal to re-render the indicator.
304 *
305 * @param data - the state to be updated.
306 */
307 _tick(data) {
308 data.totalTime += 1;
309 this.stateChanged.emit(void 0);
310 }
311 /**
312 * Reset the input execution state.
313 *
314 * @param data - the state to be rested.
315 */
316 _resetTime(data) {
317 data.totalTime = 0;
318 data.scheduledCellNumber = 0;
319 data.executionStatus = 'idle';
320 data.scheduledCell = new Set();
321 clearTimeout(data.timeout);
322 clearInterval(data.interval);
323 data.needReset = false;
324 }
325 get renderFlag() {
326 return this._renderFlag;
327 }
328 updateRenderOption(options) {
329 if (this.displayOption.showOnToolBar) {
330 if (!options.showOnToolBar) {
331 this._renderFlag = false;
332 }
333 else {
334 this._renderFlag = true;
335 }
336 }
337 this.displayOption.showProgress = options.showProgress;
338 this.stateChanged.emit(void 0);
339 }
340 }
341 ExecutionIndicator.Model = Model;
342 function createExecutionIndicatorItem(panel, translator, loadSettings) {
343 const toolbarItem = new ExecutionIndicator(translator);
344 toolbarItem.model.displayOption = {
345 showOnToolBar: true,
346 showProgress: true
347 };
348 toolbarItem.model.attachNotebook({
349 content: panel.content,
350 context: panel.sessionContext
351 });
352 if (loadSettings) {
353 loadSettings
354 .then(settings => {
355 const updateSettings = (newSettings) => {
356 toolbarItem.model.updateRenderOption(getSettingValue(newSettings));
357 };
358 settings.changed.connect(updateSettings);
359 updateSettings(settings);
360 toolbarItem.disposed.connect(() => {
361 settings.changed.disconnect(updateSettings);
362 });
363 })
364 .catch((reason) => {
365 console.error(reason.message);
366 });
367 }
368 return toolbarItem;
369 }
370 ExecutionIndicator.createExecutionIndicatorItem = createExecutionIndicatorItem;
371 function getSettingValue(settings) {
372 let showOnToolBar = true;
373 let showProgress = true;
374 const configValues = settings.get('kernelStatus').composite;
375 if (configValues) {
376 showOnToolBar = !configValues.showOnStatusBar;
377 showProgress = configValues.showProgress;
378 }
379 return { showOnToolBar, showProgress };
380 }
381 ExecutionIndicator.getSettingValue = getSettingValue;
382})(ExecutionIndicator || (ExecutionIndicator = {}));
383//# sourceMappingURL=executionindicator.js.map
\No newline at end of file