Skip to content
This repository has been archived by the owner on Apr 10, 2024. It is now read-only.

2.3 release #59

Merged
merged 18 commits into from
Jun 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions dps_info/documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## DPS Info Extension Documentation & Architecture
### Architecture
<img alt="architecture diagram" src="dps_info_architecture.png" width="350">

#### sourcefiles
- index.ts: instantiate & export jupyter extensions
- activate.ts: contains all extension activate functions
- panel.ts: contains skeleton code for a basic side panel in jupyter lab, wth a commented-out example activate function (extension must be instantiated and exported as well)
- jobinfo.ts: 1) creates a widget for listing DPS jobs associated with the user in table; 2) creates a main area jupyter widget for easy UI in listing, describing, and executing algorithms; (1) and (2) are NOT synchronized; the table in (1) is organized as:

| Job Id | Status | Algorithm |
| ------ | ------ | --------- |

- funcs.ts: common functions across classes & API calls in jobinfo.ts
- request.ts: class and functions utility for making http requests and reading their responses
Binary file added dps_info/dps_info_architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ env | grep _ >> /etc/environment

# Add conda bin to path
export PATH=$PATH:/opt/conda/bin
cp /root/.bashrc ~/.bash_profile

jupyter lab --ip=0.0.0.0 --port=3100 --allow-root --NotebookApp.token='' --LabApp.base_url=$PREVIEW_URL --no-browser --debug
9 changes: 2 additions & 7 deletions submit_jobs/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,9 @@
#### sourcefiles
- index.ts: instantiate & export jupyter extensions
- fields.json: required parameters for each DPS/MAS function that calls the MAAP API
- funcs.ts: common functions across classes & API calls; includes extension activate functions (except for side panels)
- activate.ts: contains all extension activate functions
- funcs.ts: common functions across classes & API calls
- selector.ts: widget that creates a dropdown menu for selecting from a prepopulated list of choices
- widgets.ts: contains common widgets for executing DPS/MAS cals to MAAP API; includes 2 dialog popup functions widgets depend on
- panel.ts: contains skeleton code for a basic side panel in jupyter lab, wth a commented-out example activate function (extension must be instantiated and exported as well)
- jobinfo.ts: 1) creates a widget for listing DPS jobs associated with the user in table; 2) creates a main area jupyter widget for easy UI in listing, describing, and executing algorithms; format in (1):

| Job Id | Status | Algorithm |
| ------ | ------ | --------- |

- request.ts: class and functions utility for making http requests and reading their responses
- dialog.ts: customize more dialog types & popup functions from the base class/function in @jupyterlab/apputils
80 changes: 44 additions & 36 deletions submit_jobs/src/activate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ILauncher } from '@jupyterlab/launcher';
import { IFileBrowserFactory } from "@jupyterlab/filebrowser";
import { IMainMenu } from '@jupyterlab/mainmenu';
import { Menu } from '@phosphor/widgets';
import { INotification } from 'jupyterlab_toastify';
import { InputWidget, RegisterWidget, popupText } from './widgets';
import { ProjectSelector } from './selector';
import { popup, popupResult } from './dialogs';
Expand Down Expand Up @@ -68,47 +69,54 @@ export function activateRegisterAlgorithm(
// send request to defaultvalueshandler
let getValuesFn = function(resp:Object) {
console.log('getValuesFn');
let configPath = resp['config_path'] as string;
let defaultValues = resp['default_values'] as Object;
let prevConfig = resp['previous_config'] as boolean;
console.log(resp['status_code']);
if (resp['status_code'] != 200) {
// error
popupText(resp['result'],'Error Registering Algorithm');
INotification.error(resp['result']);
} else {
let configPath = resp['config_path'] as string;
let defaultValues = resp['default_values'] as Object;
let prevConfig = resp['previous_config'] as boolean;

if (defaultValues['inputs'] == undefined) {
defaultValues['inputs'] = [];
}
if (defaultValues['description'] == undefined) {
defaultValues['description'] = '';
}

console.log(defaultValues);
if (defaultValues['inputs'] == undefined) {
defaultValues['inputs'] = [];
}
if (defaultValues['description'] == undefined) {
defaultValues['description'] = '';
}

let subtext = 'Auto-generated algorithm configuration:';
if (prevConfig) {
subtext = 'Current algorithm configuration:';
}
console.log(defaultValues);

// register function to be called
// popup read-only default values
let registerfn = function() {
console.log('registerfn testing');
let w = new RegisterWidget(registerFields,username,defaultValues,subtext,configPath);
w.setPredefinedFields(defaultValues);
console.log(w);
popup(w);
}
let subtext = 'Auto-generated algorithm configuration:';
if (prevConfig) {
subtext = 'Current algorithm configuration:';
}

// check if algorithm already exists
// ok -> call registeralgorithmhandler
// cancel -> edit template at algorithm_config.yaml (config_path)
algorithmExists(defaultValues['algo_name'],defaultValues['version'],defaultValues['environment']).then((algoExists) => {
console.log('algo Exists');
console.log(algoExists);
if (algoExists != undefined && algoExists) {
popupText('WARNING Algorithm name and version already exists. \n If you continue, the previously registered algorithm \nwill be LOST','Overwrite Algorithm?',registerfn);
// ask user if they want to continue
} else {
registerfn()
// register function to be called
// popup read-only default values
let registerfn = function() {
console.log('registerfn testing');
let w = new RegisterWidget(registerFields,username,defaultValues,subtext,configPath);
w.setPredefinedFields(defaultValues);
console.log(w);
popup(w);
}
});

// check if algorithm already exists
// ok -> call registeralgorithmhandler
// cancel -> edit template at algorithm_config.yaml (config_path)
algorithmExists(defaultValues['algo_name'],defaultValues['version'],defaultValues['environment']).then((algoExists) => {
console.log('algo Exists');
console.log(algoExists);
if (algoExists != undefined && algoExists) {
popupText('WARNING Algorithm name and version already exists. \n If you continue, the previously registered algorithm \nwill be LOST','Overwrite Algorithm?',registerfn);
// ask user if they want to continue
} else {
registerfn()
}
});
}
};
inputRequest('defaultValues','Register Algorithm',{'code_path':path},getValuesFn);
},
Expand Down
10 changes: 6 additions & 4 deletions submit_jobs/src/funcs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { PageConfig } from '@jupyterlab/coreutils'
import { INotification } from 'jupyterlab_toastify';
import { request, RequestResult } from './request';
import { popupResultText } from './widgets';

Expand Down Expand Up @@ -49,10 +50,8 @@ export function inputRequest(endpt:string,title:string,inputs:{[k:string]:string
// add params
for (let key in inputs) {
var fieldValue = inputs[key];

if(key !== 'proxy-ticket')
fieldValue = fieldValue.toLowerCase();

// if(key !== 'proxy-ticket')
// fieldValue = fieldValue.toLowerCase();
requestUrl.searchParams.append(key.toLowerCase(), fieldValue);
}
console.log(requestUrl.href);
Expand All @@ -70,6 +69,9 @@ export function inputRequest(endpt:string,title:string,inputs:{[k:string]:string
console.log('fn defined');
fn(json_response);
}
} else {
var json_response:any = res.json();
INotification.error(json_response['result']);
}
});
}
Expand Down
34 changes: 20 additions & 14 deletions submit_jobs/src/widgets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,14 @@ export class InputWidget extends Widget {
let json_response:any = res.json();
// console.log(json_response);
me._responseText = me._responseText + '\n' + json_response['result'];
if (json_response['status_code'] != 200) {
INotification.error(me._responseText);
}
} else {
me._responseText = "Error Sending Request.";
let json_response:any = res.json();
// console.log(json_response);
me._responseText = "Error Sending Request:\n" + json_response['result'];
INotification.error(me._responseText);
}
console.log("updating");
me.updateSearchResults();
Expand Down Expand Up @@ -382,13 +388,13 @@ export class RegisterWidget extends InputWidget {
subtxt.style.flexDirection = 'column';
subtxt.innerHTML = subtext;
this.node.appendChild(subtxt);
this.node.appendChild(document.createElement("BR"));
this.node.appendChild(document.createElement('BR'));
}

for (var field of this.fields) {
// textarea for inputs field in register
if (field == "inputs") {
var fieldLabel = document.createElement("Label");
if (field == 'inputs') {
var fieldLabel = document.createElement('Label');
fieldLabel.innerHTML = field;
this.node.appendChild(fieldLabel);

Expand All @@ -413,7 +419,7 @@ export class RegisterWidget extends InputWidget {
this.node.appendChild(fieldInputs);

} else {
var fieldLabel = document.createElement("Label");
var fieldLabel = document.createElement('Label');
fieldLabel.innerHTML = field;
this.node.appendChild(fieldLabel);

Expand All @@ -429,7 +435,7 @@ export class RegisterWidget extends InputWidget {
}

// BREAK
var x = document.createElement("BR");
var x = document.createElement('BR');
this.node.appendChild(x)

// footer text - edit config at path
Expand Down Expand Up @@ -496,7 +502,7 @@ export class WidgetResult extends Widget {
// update panel text on resolution of result popup
getValue() {
console.log('checking popup resolution fn');
if (this.okfn != undefined) {
if (typeof this.okfn === "function") {
console.log(this.okfn);
try{
this.okfn();
Expand All @@ -515,18 +521,18 @@ export function popupResultText(result:string,title:string,fn?:any,isXML?:boolea
body.style.display = 'flex';
body.style.flexDirection = 'column';

var textarea = document.createElement("div");
var textarea = document.createElement('div');
textarea.id = 'result-text';
textarea.style.display = 'flex';
textarea.style.flexDirection = 'column';
var format = require('xml-formatter');

// console.log(result);
if ( isXML == undefined || (! isXML) ){
textarea.innerHTML = "<pre>" + result + "</pre>";
if ( isXML === undefined || (! isXML) ){
textarea.innerHTML = '<pre>' + result + '</pre>';
// console.log(textarea);
} else {
var xml = "<root><content><p>"+result+"</p></content></root>";
var xml = '<root><content><p>'+result+'</p></content></root>';
var options = {indentation: ' ', stripComments: true, collapseContent: false};
var formattedXML = format(xml,options);
textarea.innerHTML = formattedXML;
Expand All @@ -543,16 +549,16 @@ export function popupText(result:string,title:string,fn?:any) {
body.style.display = 'flex';
body.style.flexDirection = 'column';

var textarea = document.createElement("div");
var textarea = document.createElement('div');
textarea.id = 'result-text';
textarea.style.display = 'flex';
textarea.style.flexDirection = 'column';

// console.log(result);
textarea.innerHTML = "<pre>" + result + "</pre>";
textarea.innerHTML = '<pre>' + result + '</pre>';
body.appendChild(textarea);
// console.log(body);
if (fn == undefined) {
if (fn === undefined) {
popupTitle(new Widget({node:body}),title);
} else {
popupTitle(new WidgetResult(body,fn),title);
Expand Down
33 changes: 23 additions & 10 deletions submit_jobs/submit_jobs/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def get(self,**params):

# only description and inputs are allowed to be empty
for f in ['algo_name','version','environment','run_command','repository_url','docker_url']:
if config[f] == '':
if config[f] == '' or config[f] == None:
self.finish({"status_code": 412, "result": "Error: Register field {} cannot be empty".format(f)})
return

Expand Down Expand Up @@ -151,19 +151,21 @@ def get(self,**params):
os.chdir(proj_path)

# get git status
git_status_out = subprocess.check_output("git status --branch --porcelain", shell=True).decode("utf-8")
logger.debug(git_status_out)
try:
git_status_out = subprocess.check_output("git status --branch --porcelain", shell=True).decode("utf-8")
logger.debug(git_status_out)

# is there a git repo?
if 'not a git repository' in git_status_out:
self.finish({"status_code": 412, "result": "Error: \n{}".format(git_status_out)})
except:
# subprocess could also error out (nonzero exit code)
self.finish({"status_code": 412, "result": "Error: \nThe code you want to register is not saved in a git repository."})
return

git_status = git_status_out.splitlines()[1:]
git_status = [e.strip() for e in git_status]

# filter for unsaved python files
unsaved = list(filter(lambda e: ( (e.split('.')[-1] in ['ipynb','py','sh','jl']) and (e[0] in ['M','?']) ), git_status))
# filter for unsaved python, julia, matlab shell files
unsaved = list(filter(lambda e: ( (e.split('.')[-1] in ['ipynb','py','sh','jl','r','m','mat']) and (e[0] in ['M','?']) ), git_status))
if len(unsaved) != 0:
self.finish({"status_code": 412, "result": "Error: Notebook(s) and/or script(s) have not been committed\n{}".format('\n'.join(unsaved))})
return
Expand Down Expand Up @@ -1221,8 +1223,19 @@ def get(self):
proj_path = '/projects/'+params['code_path']
proj_path = '/'.join(proj_path.split('/')[:-1])
os.chdir(proj_path)
repo_url = subprocess.check_output("git remote get-url origin", shell=True).decode('utf-8').strip()
# logger.debug(repo_url)

# try to get git remote url
try:
repo_url = subprocess.check_output("git remote get-url origin", shell=True).decode('utf-8').strip()
logger.debug(repo_url)
print('reop url is {}'.format(repo_url))

# is there a git repo?
except:
# subprocess could also error out (nonzero exit code)
self.finish({"status_code": 412, "result": "Error: \nThe code you want to register is not saved in a git repository."})
return


vals = {}
code_path = params['code_path']
Expand Down Expand Up @@ -1265,7 +1278,7 @@ def get(self):
logger.debug(settings)

# outputs: algo_name, version, environment, repository_url, dockerfile_path
self.finish({"status_code": 200, "default_values":settings, "config_path":config_path, "previous_config":prev_config})
self.finish({"status_code": 200, "result": "Got default values.", "default_values":settings, "config_path":config_path, "previous_config":prev_config})

class ListJobsHandler(IPythonHandler):
# inputs: username
Expand Down
Binary file modified submit_jobs/submit_jobs_architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.