1 |
|
2 |
|
3 | import { translateKernelStatuses } from '@jupyterlab/apputils';
|
4 | import { nullTranslator } from '@jupyterlab/translation';
|
5 | import React from 'react';
|
6 | import { ProgressCircle } from '@jupyterlab/statusbar';
|
7 | import { circleIcon, offlineBoltIcon, VDomModel, VDomRenderer } from '@jupyterlab/ui-components';
|
8 | import { KernelMessage } from '@jupyterlab/services';
|
9 |
|
10 |
|
11 |
|
12 | export 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 |
|
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 |
|
79 |
|
80 | export class ExecutionIndicator extends VDomRenderer {
|
81 | |
82 |
|
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 |
|
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 |
|
107 |
|
108 | (function (ExecutionIndicator) {
|
109 | |
110 |
|
111 |
|
112 | class Model extends VDomModel {
|
113 | constructor() {
|
114 | super();
|
115 | |
116 |
|
117 |
|
118 | this._notebookExecutionProgress = new WeakMap();
|
119 | this._displayOption = { showOnToolBar: true, showProgress: true };
|
120 | this._renderFlag = true;
|
121 | }
|
122 | |
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
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 |
|
170 | this._cellScheduledCallback(nb, msgId);
|
171 | }
|
172 | else if (KernelMessage.isStatusMsg(message) &&
|
173 | message.content.execution_state === 'idle') {
|
174 |
|
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 |
|
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 |
|
206 |
|
207 | get currentNotebook() {
|
208 | return this._currentNotebook;
|
209 | }
|
210 | |
211 |
|
212 |
|
213 | get displayOption() {
|
214 | return this._displayOption;
|
215 | }
|
216 | |
217 |
|
218 |
|
219 |
|
220 |
|
221 | set displayOption(options) {
|
222 | this._displayOption = options;
|
223 | }
|
224 | |
225 |
|
226 |
|
227 |
|
228 |
|
229 |
|
230 |
|
231 |
|
232 | executionState(nb) {
|
233 | return this._notebookExecutionProgress.get(nb);
|
234 | }
|
235 | |
236 |
|
237 |
|
238 |
|
239 |
|
240 |
|
241 |
|
242 |
|
243 |
|
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 |
|
257 |
|
258 |
|
259 |
|
260 |
|
261 |
|
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 |
|
274 |
|
275 |
|
276 |
|
277 |
|
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 |
|
288 |
|
289 |
|
290 |
|
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 |
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 |
|
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 |
|
332 |
|
333 |
|
334 |
|
335 |
|
336 | _tick(data) {
|
337 | data.totalTime += 1;
|
338 | this.stateChanged.emit(void 0);
|
339 | }
|
340 | |
341 |
|
342 |
|
343 |
|
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 |
|
\ | No newline at end of file |