All files / src index.tsx

0% Statements 0/98
0% Branches 0/24
0% Functions 0/14
0% Lines 0/98

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
import React from 'react';
import {
  ILayoutRestorer,
  JupyterFrontEnd,
  JupyterFrontEndPlugin
} from '@jupyterlab/application';
import {
  MainAreaWidget,
  ReactWidget,
  WidgetTracker
} from '@jupyterlab/apputils';
import { FileBrowser, IFileBrowserFactory } from '@jupyterlab/filebrowser';
import { ILauncher } from '@jupyterlab/launcher';
import { Contents } from '@jupyterlab/services';
import { IStatusBar } from '@jupyterlab/statusbar';
import { ITranslator } from '@jupyterlab/translation';
 
import { Poll } from '@lumino/polling';
import { Signal } from '@lumino/signaling';
import { RunningJobsIndicator } from './components/running-jobs-indicator';
 
import { SchedulerService } from './handler';
import { NotebookJobsListingModel } from './model';
import { CreateJobFormState } from './create-job-form';
import { JobsPanelView, NotebookJobsPanel } from './notebook-jobs-panel';
import {
  calendarAddOnIcon,
  calendarMonthIcon,
  eventNoteIcon
} from './components/icons';
 
namespace CommandIDs {
  export const deleteJob = 'scheduling:delete-job';
  export const runNotebook = 'scheduling:run-notebook';
  export const showNotebookJobs = 'scheduling:show-notebook-jobs';
  export const stopJob = 'scheduling:stop-job';
}
 
export const NotebookJobsPanelId = 'notebook-jobs-panel';
 
/**
 * Initialization data for the jupyterlab-scheduler extension.
 */
const plugin: JupyterFrontEndPlugin<void> = {
  id: 'jupyterlab-scheduler:plugin',
  requires: [IFileBrowserFactory, ITranslator, ILayoutRestorer],
  optional: [IStatusBar, ILauncher],
  autoStart: true,
  activate: activatePlugin
};
 
type NotebookJobsPluginType = typeof plugin;
 
function getSelectedItem(widget: FileBrowser | null): Contents.IModel | null {
  Iif (widget === null) {
    return null;
  }
 
  // Get the first selected item.
  const firstItem = widget.selectedItems().next();
  Iif (firstItem === null || firstItem === undefined) {
    return null;
  }
 
  return firstItem;
}
 
function getSelectedFilePath(widget: FileBrowser | null): string | null {
  const selectedItem = getSelectedItem(widget);
  Iif (selectedItem === null) {
    return null;
  }
  return selectedItem.path;
}
 
function getSelectedFileName(widget: FileBrowser | null): string | null {
  const selectedItem = getSelectedItem(widget);
  Iif (selectedItem === null) {
    return null;
  }
  return selectedItem.name;
}
 
let scheduledJobsListingModel: NotebookJobsListingModel | null = null;
 
async function getNotebookJobsListingModel(): Promise<NotebookJobsListingModel> {
  Iif (scheduledJobsListingModel) {
    return scheduledJobsListingModel;
  }
 
  const api = new SchedulerService({});
 
  const jobsResponse = await api.getJobs({});
  scheduledJobsListingModel = new NotebookJobsListingModel(jobsResponse.jobs);
  return scheduledJobsListingModel;
}
 
async function activatePlugin(
  app: JupyterFrontEnd,
  browserFactory: IFileBrowserFactory,
  translator: ITranslator,
  restorer: ILayoutRestorer,
  statusBar: IStatusBar | null,
  launcher: ILauncher | null
): Promise<void> {
  const { commands } = app;
  const trans = translator.load('jupyterlab');
  const { tracker } = browserFactory;
  const api = new SchedulerService({});
  let mainAreaWidget: MainAreaWidget<NotebookJobsPanel>;
  let widgetTracker = new WidgetTracker<MainAreaWidget<NotebookJobsPanel>>({
    namespace: 'jupyterlab-scheduler'
  });
  restorer.restore(widgetTracker, {
    command: CommandIDs.showNotebookJobs,
    name: () => 'jupyterlab-scheduler'
  });
 
  const model = await getNotebookJobsListingModel();
 
  const jobsPanel = new NotebookJobsPanel({
    app,
    model,
    updateCreateJobFormSignal: _signal,
    translator
  });
  jobsPanel.title.icon = calendarMonthIcon;
  jobsPanel.title.caption = trans.__('Notebook Jobs');
  jobsPanel.node.setAttribute('role', 'region');
  jobsPanel.node.setAttribute('aria-label', trans.__('Notebook Jobs'));
 
  commands.addCommand(CommandIDs.deleteJob, {
    execute: async args => {
      const id = args['id'] as string;
      await api.deleteJob(id);
    },
    // TODO: Use args to name command dynamically
    label: trans.__('Delete Job')
  });
 
  const showJobsPane = async (view: JobsPanelView) => {
    Iif (!mainAreaWidget || mainAreaWidget.isDisposed) {
      // Create a new widget
      mainAreaWidget = new MainAreaWidget<NotebookJobsPanel>({
        content: jobsPanel
      });
      mainAreaWidget.content.view = view;
      mainAreaWidget.id = NotebookJobsPanelId;
      mainAreaWidget.title.icon = calendarMonthIcon;
      mainAreaWidget.title.label = trans.__('Notebook Jobs');
      mainAreaWidget.title.closable = true;
    }
 
    Iif (!widgetTracker.has(mainAreaWidget)) {
      // Track the state of the widget for later restoration
      widgetTracker.add(mainAreaWidget);
    }
 
    Iif (!mainAreaWidget.isAttached) {
      app.shell.add(mainAreaWidget, 'main');
    }
 
    mainAreaWidget.content.view = view;
    mainAreaWidget.content.update();
    app.shell.activateById(mainAreaWidget.id);
  };
 
  commands.addCommand(CommandIDs.showNotebookJobs, {
    execute: async () => showJobsPane('JobsList'),
    label: trans.__('Show Notebook Jobs'),
    icon: eventNoteIcon
  });
 
  commands.addCommand(CommandIDs.runNotebook, {
    execute: async () => {
      await showJobsPane('CreateJobForm');
 
      const widget = tracker.currentWidget;
      const filePath = getSelectedFilePath(widget) ?? '';
      const fileName = getSelectedFileName(widget) ?? '';
 
      // Update the job form inside the notebook jobs widget
      const newState: CreateJobFormState = {
        inputFile: filePath,
        jobName: fileName,
        outputPath: '',
        environment: ''
      };
 
      _signal.emit(newState);
    },
    label: trans.__('Create Notebook Job'),
    icon: calendarAddOnIcon
  });
 
  commands.addCommand(CommandIDs.stopJob, {
    execute: async args => {
      const id = args['id'] as string;
      await api.setJobStatus(id, 'STOPPED');
    },
    // TODO: Use args to name command dynamically
    label: trans.__('Stop Job')
  });
 
  Iif (!statusBar) {
    // Automatically disable if statusbar missing
    return;
  }
 
  statusBar.registerStatusItem('jupyterlab-scheduler:status', {
    align: 'middle',
    item: ReactWidget.create(
      <RunningJobsIndicator
        onClick={async () => showJobsPane('JobsList')}
        model={model}
      />
    )
  });
 
  const statusPoll = new Poll({
    factory: async () => {
      const jobCount = await api.getJobsCount('IN_PROGRESS');
      model.updateJobsCount(jobCount);
    },
    frequency: { interval: 1000, backoff: false }
  });
  statusPoll.start();
 
  // Add to launcher
  Iif (launcher) {
    launcher.add({
      command: CommandIDs.showNotebookJobs
    });
  }
 
  console.log('JupyterLab extension jupyterlab-scheduler is activated!');
}
 
let _signal: Signal<NotebookJobsPluginType, CreateJobFormState> = new Signal<
  NotebookJobsPluginType,
  CreateJobFormState
>(plugin);
 
export default plugin;