UNPKG

17.6 kBJavaScriptView Raw
1// Copyright (c) Jupyter Development Team.
2// Distributed under the terms of the Modified BSD License.
3import { translateKernelStatuses } from '@jupyterlab/apputils';
4import { nullTranslator } from '@jupyterlab/translation';
5import React from 'react';
6import { ProgressCircle } from '@jupyterlab/statusbar';
7import { circleIcon, offlineBoltIcon, VDomModel, VDomRenderer } 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, label: trans.__('Kernel status') }));
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, { ...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, { ...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('jp-mod-highlighted');
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 (KernelMessage.isStatusMsg(message) &&
180 message.content.execution_state === 'restarting') {
181 this._restartHandler(nb);
182 }
183 else if (message.header.msg_type === 'execute_input') {
184 // A cell code starts executing.
185 this._startTimer(nb);
186 }
187 };
188 (_b = (_a = context.session) === null || _a === void 0 ? void 0 : _a.kernel) === null || _b === void 0 ? void 0 : _b.anyMessage.connect(handleKernelMsg);
189 (_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));
190 const kernelChangedSlot = (_, kernelData) => {
191 if (state) {
192 this._resetTime(state);
193 this.stateChanged.emit(void 0);
194 if (kernelData.newValue) {
195 kernelData.newValue.anyMessage.connect(handleKernelMsg);
196 }
197 }
198 };
199 context.kernelChanged.connect(kernelChangedSlot);
200 context.disposed.connect(ctx => ctx.kernelChanged.disconnect(kernelChangedSlot));
201 }
202 }
203 }
204 /**
205 * The current activated notebook in model.
206 */
207 get currentNotebook() {
208 return this._currentNotebook;
209 }
210 /**
211 * The display options for progress bar and elapsed time.
212 */
213 get displayOption() {
214 return this._displayOption;
215 }
216 /**
217 * Set the display options for progress bar and elapsed time.
218 *
219 * @param options - Options to be used
220 */
221 set displayOption(options) {
222 this._displayOption = options;
223 }
224 /**
225 * Get the execution state associated with a notebook.
226 *
227 * @param nb - The notebook used to identify execution
228 * state.
229 *
230 * @returns - The associated execution state.
231 */
232 executionState(nb) {
233 return this._notebookExecutionProgress.get(nb);
234 }
235 /**
236 * Schedule switch to idle status and clearing of the timer.
237 *
238 * ### Note
239 *
240 * To keep track of cells executed under 1 second,
241 * the execution state is marked as `needReset` 1 second after executing
242 * these cells. This `Timeout` will be cleared if there is any cell
243 * scheduled after that.
244 */
245 _scheduleSwitchToIdle(state) {
246 window.setTimeout(() => {
247 state.executionStatus = 'idle';
248 clearInterval(state.interval);
249 this.stateChanged.emit(void 0);
250 }, 150);
251 state.timeout = window.setTimeout(() => {
252 state.needReset = true;
253 }, 1000);
254 }
255 /**
256 * The function is called on kernel's idle status message.
257 * It is used to keep track of number of executed
258 * cells or Comm custom messages and the status of kernel.
259 *
260 * @param nb - The notebook which contains the executed code cell.
261 * @param msg_id - The id of message.
262 */
263 _cellExecutedCallback(nb, msg_id) {
264 const state = this._notebookExecutionProgress.get(nb);
265 if (state && state.scheduledCell.has(msg_id)) {
266 state.scheduledCell.delete(msg_id);
267 if (state.scheduledCell.size === 0) {
268 this._scheduleSwitchToIdle(state);
269 }
270 }
271 }
272 /**
273 * The function is called on kernel's restarting status message.
274 * It is used to clear the state tracking the number of executed
275 * cells.
276 *
277 * @param nb - The notebook which contains the executed code cell.
278 */
279 _restartHandler(nb) {
280 const state = this._notebookExecutionProgress.get(nb);
281 if (state) {
282 state.scheduledCell.clear();
283 this._scheduleSwitchToIdle(state);
284 }
285 }
286 /**
287 * This function is called on kernel's `execute_input` message to start
288 * the elapsed time counter.
289 *
290 * @param nb - The notebook which contains the scheduled execution request.
291 */
292 _startTimer(nb) {
293 const state = this._notebookExecutionProgress.get(nb);
294 if (!state) {
295 return;
296 }
297 if (state.scheduledCell.size > 0) {
298 if (state.executionStatus !== 'busy') {
299 state.executionStatus = 'busy';
300 clearTimeout(state.timeout);
301 this.stateChanged.emit(void 0);
302 state.interval = window.setInterval(() => {
303 this._tick(state);
304 }, 1000);
305 }
306 }
307 else {
308 this._resetTime(state);
309 }
310 }
311 /**
312 * The function is called on kernel's `execute_request` message or Comm message, it is
313 * used to keep track number of scheduled cell or Comm execution message
314 * and the status of kernel.
315 *
316 * @param nb - The notebook which contains the scheduled code.
317 * cell
318 * @param msg_id - The id of message.
319 */
320 _cellScheduledCallback(nb, msg_id) {
321 const state = this._notebookExecutionProgress.get(nb);
322 if (state && !state.scheduledCell.has(msg_id)) {
323 if (state.needReset) {
324 this._resetTime(state);
325 }
326 state.scheduledCell.add(msg_id);
327 state.scheduledCellNumber += 1;
328 }
329 }
330 /**
331 * Increment the executed time of input execution state
332 * and emit `stateChanged` signal to re-render the indicator.
333 *
334 * @param data - the state to be updated.
335 */
336 _tick(data) {
337 data.totalTime += 1;
338 this.stateChanged.emit(void 0);
339 }
340 /**
341 * Reset the input execution state.
342 *
343 * @param data - the state to be rested.
344 */
345 _resetTime(data) {
346 data.totalTime = 0;
347 data.scheduledCellNumber = 0;
348 data.executionStatus = 'idle';
349 data.scheduledCell = new Set();
350 clearTimeout(data.timeout);
351 clearInterval(data.interval);
352 data.needReset = false;
353 }
354 get renderFlag() {
355 return this._renderFlag;
356 }
357 updateRenderOption(options) {
358 if (this.displayOption.showOnToolBar) {
359 if (!options.showOnToolBar) {
360 this._renderFlag = false;
361 }
362 else {
363 this._renderFlag = true;
364 }
365 }
366 this.displayOption.showProgress = options.showProgress;
367 this.stateChanged.emit(void 0);
368 }
369 }
370 ExecutionIndicator.Model = Model;
371 function createExecutionIndicatorItem(panel, translator, loadSettings) {
372 const toolbarItem = new ExecutionIndicator(translator);
373 toolbarItem.model.displayOption = {
374 showOnToolBar: true,
375 showProgress: true
376 };
377 toolbarItem.model.attachNotebook({
378 content: panel.content,
379 context: panel.sessionContext
380 });
381 if (loadSettings) {
382 loadSettings
383 .then(settings => {
384 const updateSettings = (newSettings) => {
385 toolbarItem.model.updateRenderOption(getSettingValue(newSettings));
386 };
387 settings.changed.connect(updateSettings);
388 updateSettings(settings);
389 toolbarItem.disposed.connect(() => {
390 settings.changed.disconnect(updateSettings);
391 });
392 })
393 .catch((reason) => {
394 console.error(reason.message);
395 });
396 }
397 return toolbarItem;
398 }
399 ExecutionIndicator.createExecutionIndicatorItem = createExecutionIndicatorItem;
400 function getSettingValue(settings) {
401 let showOnToolBar = true;
402 let showProgress = true;
403 const configValues = settings.get('kernelStatus').composite;
404 if (configValues) {
405 showOnToolBar = !configValues.showOnStatusBar;
406 showProgress = configValues.showProgress;
407 }
408 return { showOnToolBar, showProgress };
409 }
410 ExecutionIndicator.getSettingValue = getSettingValue;
411})(ExecutionIndicator || (ExecutionIndicator = {}));
412//# sourceMappingURL=executionindicator.js.map
\No newline at end of file