IF YOU WOULD LIKE TO GET AN ACCOUNT, please write an email to s dot adaszewski at gmail dot com. User accounts are meant only to report issues and/or generate pull requests. This is a purpose-specific Git hosting for ADARED projects. Thank you for your understanding!
Переглянути джерело

Still working on launching

master
Stanislaw Adaszewski 4 роки тому
джерело
коміт
82c7cf7031
4 змінених файлів з 218 додано та 40 видалено
  1. +2
    -2
      frontend/src/js/component/wb-name-and-uuid.js
  2. +79
    -9
      frontend/src/js/dialog/wb-toolbox-dialog.js
  3. +12
    -3
      frontend/src/js/page/wb-app.js
  4. +125
    -26
      frontend/src/js/page/wb-launch-workflow-page.js

+ 2
- 2
frontend/src/js/component/wb-name-and-uuid.js Переглянути файл

@@ -75,7 +75,7 @@ class WBNameAndUuid extends Component {
}
}
render({ uuid }, { error, item }) {
render({ uuid, onLinkClicked }, { error, item }) {
if (!uuid)
return (
<div><i>{ String(uuid) }</i></div>
@@ -85,7 +85,7 @@ class WBNameAndUuid extends Component {
<div>
<div>
{ error ? error : (item ? (
<a href={ urlForObject(item) }>{ arvadosObjectName(item) }</a>
<a href={ urlForObject(item) } onclick={ onLinkClicked }>{ arvadosObjectName(item) }</a>
) : 'Loading...') }
</div>
<div>


+ 79
- 9
frontend/src/js/dialog/wb-toolbox-dialog.js Переглянути файл

@@ -8,6 +8,7 @@ class WBToolboxDialog extends Component {
constructor(...args) {
super(...args);
this.state.rows = [];
this.state.selectedValues = {};
}
componentDidMount() {
@@ -20,8 +21,9 @@ class WBToolboxDialog extends Component {
}
fetchRows() {
const { items } = this.props;
const { items, id, selectMany, onAccepted } = this.props;
const { arvHost, arvToken } = this.props.app.state;
const { selectedValues } = this.state;
let prom = wbFetchObjects(arvHost, arvToken,
items);
let lookup;
@@ -31,22 +33,46 @@ class WBToolboxDialog extends Component {
let ownerLookup;
prom = prom.then(lkup => (ownerLookup = lkup));
prom = prom.then(() => {
const rows = items.map(uuid => {
const rows = items.map((uuid, idx) => {
const it = lookup[uuid];
const ow = ownerLookup[it.owner_uuid];
return [
( <div><input type="checkbox" /></div> ),
( <WBNameAndUuid uuid={ uuid } lookup={ lookup } /> ),
let r = [];
if (selectMany)
r.push();
r = r.concat([
selectMany ? (
<div>
<input type="checkbox" checked={ (uuid in selectedValues) }
onChange={ e => {
if (e.target.value === 'on')
selectedValues[uuid] = true;
else
delete selectedValues[uuid];
} } /> { '\u00A0' }
</div>
) : (
<button class="btn btn-outline-primary" title="Use"
onclick={ () => {
$('#' + id).modal('hide');
onAccepted(uuid);
} }>
<i class="fas fa-hand-pointer"></i>
</button>
),
( <WBNameAndUuid uuid={ uuid } lookup={ lookup }
onLinkClicked={ () => $('#' + id).modal('hide') } /> ),
it.kind,
wbFormatDate(it.created_at),
( <WBNameAndUuid uuid={ it.owner_uuid } lookup={ ownerLookup } /> )
];
( <WBNameAndUuid uuid={ it.owner_uuid } lookup={ ownerLookup }
onLinkClicked={ () => $('#' + id).modal('hide') } /> )
]);
return r;
});
this.setState({ rows });
});
}
render({ id }, { rows }) {
render({ id, selectMany, onAccepted, items, app }, { rows, selectedValues }) {
return (
<div class="modal" id={ id } tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
@@ -59,12 +85,52 @@ class WBToolboxDialog extends Component {
</div>
<div class="modal-body">
<div class="mb-2">
{ selectMany ? (
<button class="btn btn-outline-primary mr-2" onclick={ () => {
items.map(uuid => (selectedValues[uuid] = true));
this.fetchRows();
} }>
Select All
</button>
) : null }
{ selectMany ? (
<button class="btn btn-outline-primary mr-2" onclick={ () => {
this.setState({ 'selectedValues' : {} });
this.fetchRows();
} }>
Select None
</button>
) : null }
<button class="btn btn-outline-primary mr-2" onclick={ () => {
app.clearToolbox();
this.props.items = [];
this.fetchRows();
} } >
Clear Toolbox
</button>
<button class="btn btn-outline-primary mr-2" onclick={ () => {
app.loadToolbox();
this.props.items = app.state.toolboxItems;
this.fetchRows();
} } >
Refresh Toolbox
</button>
</div>
<WBTable columns={ [ '', 'Name', 'Kind', 'Created At', 'Owner' ] }
rows={ rows } />
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary">Accept</button>
{ selectMany ? (
<button type="button" class="btn btn-primary" onclick={
() => {
$('#' + id).modal('hide');
onAccepted(items.filter(uuid => (uuid in selectedValues)));
}
}>Accept</button>
) : null }
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
</div>
</div>
@@ -74,4 +140,8 @@ class WBToolboxDialog extends Component {
}
}
WBToolboxDialog.defaultProps = {
'onAccepted': () => {}
};
export default WBToolboxDialog;

+ 12
- 3
frontend/src/js/page/wb-app.js Переглянути файл

@@ -19,8 +19,7 @@ class WBApp extends Component {
this.state.arvToken = window.localStorage['arvToken'];
if ('currentUser' in window.localStorage)
this.state.currentUser = JSON.parse(window.localStorage['currentUser']);
this.state.toolboxItems = ('toolboxItems' in window.localStorage) ?
JSON.parse(window.localStorage['toolboxItems']) : [];
this.loadToolbox();
}
navbarItemUrl(item) {
@@ -51,6 +50,16 @@ class WBApp extends Component {
JSON.stringify(this.state.toolboxItems);
}
clearToolbox() {
this.state.toolboxItems = [];
delete window.localStorage['toolboxItems'];
}
loadToolbox() {
this.state.toolboxItems = ('toolboxItems' in window.localStorage) ?
JSON.parse(window.localStorage['toolboxItems']) : [];
}
render() {
return (
<Router>
@@ -74,7 +83,7 @@ class WBApp extends Component {
<WBWorkflowView path="/workflow/:uuid" app={ this } />
<WBLaunchWorkflowPage path="/workflow-launch/:workflowUuid/:projectUuid?" app={ this } />
<WBLaunchWorkflowPage path="/workflow-launch/:workflowUuid" app={ this } />
</Router>
);
}


+ 125
- 26
frontend/src/js/page/wb-launch-workflow-page.js Переглянути файл

@@ -1,4 +1,4 @@
import { h, Component } from 'preact';
import { h, Component, createRef } from 'preact';
import WBNavbarCommon from 'wb-navbar-common';
import WBArvadosCrumbs from 'wb-arvados-crumbs';
import WBToolboxDialog from 'wb-toolbox-dialog';
@@ -8,27 +8,57 @@ import linkState from 'linkstate';
function createInputsTemplate(workflow) {
const g = JSON.parse(workflow.definition)['$graph'];
const main = g.find(it => (it.id === '#main'));
/* let res = '';
let res = '';
main.inputs.map(it => {
let id = it.id.split('/');
id = id[id.length - 1];
if (it.label) res += ' // ' + it.label + '\n';
if (it.doc) res += ' //' + it.doc + '\n';
res += ' ' + it.type
res += '\'' + it.id + '\': null'
}); */
let res = main.inputs.map(it => { it.value = null; return it; });
if (it.label) res += ' // ' + it.label + '\n';
if (it.doc) res += ' // ' + it.doc + '\n';
res += ' // Type: ' + ((typeof(it.type) === 'string') ?
it.type : JSON.stringify(it.type)) + '\n\n';
res += ' \'' + id + '\': \'\',\n\n';
//res += ' // ' + ' '.repeat(id.length) + '^^^^\n\n';
});
/* let res = main.inputs.map(it => { it.value = null; return it; });
res = JSON.stringify(res, null, 2);
res = res.split('\n');
res = res.map((ln, i) => (i == 0 ? ln : ' ' + ln));
res = res.join('\n');
res = res.join('\n'); */
return res;
}
function uuidsToCwlObjects(spec) {
if (typeof(spec) === 'string') {
if (/^[a-z0-9]{5}-[a-z0-9]{5}-[a-z0-9]{15}/.exec(spec)) {
return {
'class': 'Directory',
'location': 'keep:' + spec
};
} else if (/^[a-f0-9]{32}\+[0-9]+/.exec(spec)) {
return {
'class': 'Directory',
'location': 'keep:' + spec
};
} else {
return spec;
}
} else if (typeof(spec) === 'object') {
const res = (spec instanceof Array) ? [] : {};
Object.keys(spec).map(k => (res[k] = uuidsToCwlObjects(spec[k])));
return res;
} else {
return spec;
}
}
class WBLaunchWorkflowPage extends Component {
constructor(...args) {
super(...args);
this.state.toolboxDialogId = uuid.v4();
this.state.browseDialogId = uuid.v4();
this.state.insertDialogId = uuid.v4();
this.state.insertManyDialogId = uuid.v4();
this.inputsTextArea = createRef();
}
componentDidMount() {
@@ -39,24 +69,44 @@ class WBLaunchWorkflowPage extends Component {
'/arvados/v1/workflows/' + workflowUuid);
prom = prom.then(xhr => this.setState({
'workflow': xhr.response,
'processName': xhr.response.name,
'processDescription': xhr.response.description,
'inputsFunctionText': '(() => {\n return ' +
'defaultProcessName': xhr.response.name + ' ' + (new Date().toISOString()),
'defaultProcessDescription': xhr.response.description,
'inputsFunctionText': '(() => {\n return {\n' +
createInputsTemplate(xhr.response) +
';\n})()'
' };\n})()'
}));
}
render({ app, projectUuid, workflowUuid },
{ workflow, processName, processDescription,
inputsFunctionText, toolboxDialogId }) {
render({ app, workflowUuid },
{ workflow, projectUuid, processName, processDescription,
defaultProcessName, defaultProcessDescription,
inputsFunctionText, browseDialogId,
insertDialogId, insertManyDialogId,
inputsPreview }) {
return (
<div>
<WBNavbarCommon app={ app } />
<WBToolboxDialog app={ app } id={ toolboxDialogId }
items={ app.state.toolboxItems } />
<WBToolboxDialog app={ app } id={ browseDialogId }
items={ app.state.toolboxItems } onAccepted={ value =>
this.setState({ 'projectUuid': value }) } />
<WBToolboxDialog app={ app } id={ insertDialogId }
items={ app.state.toolboxItems }
onAccepted={ value => {
const t = this.inputsTextArea.current;
const start = t.selectionStart;
const end = t.selectionEnd;
this.setState({
'inputsFunctionText': t.value.substr(0, start) + value +
t.value.substr(end)
});
} } />
<WBToolboxDialog app={ app } id={ insertManyDialogId }
items={ app.state.toolboxItems } selectMany={ true }
onAccepted={ values => alert(values) } />
{ workflow ?
(<form class="container-fluid">
@@ -69,8 +119,24 @@ class WBLaunchWorkflowPage extends Component {
<div class="form-group">
<label for="projectUuid">Project UUID</label>
<input type="email" class="form-control" id="projectUuid" placeholder="Enter project uuid" />
<button class="btn btn-primary" onclick={ e => { e.preventDefault(); $('#' + toolboxDialogId).modal(); } }>Browse</button>
<div>
{ projectUuid ? (
<WBArvadosCrumbs app={ app } uuid={ projectUuid } />
) : null }
<div class="input-group mb-3">
<input type="text" class="form-control" id="projectUuid"
placeholder="Enter Project UUID" aria-label="Project UUID"
aria-describedby="button-addon2" value={ projectUuid }
onChange={ linkState(this, 'projectUuid') } />
<div class="input-group-append">
<button class="btn btn-primary" type="button"
id="button-addon2" onclick={ e => { e.preventDefault();
$('#' + browseDialogId).modal(); } }>Browse</button>
</div>
</div>
</div>
</div>
<div class="form-check">
@@ -81,22 +147,55 @@ class WBLaunchWorkflowPage extends Component {
<div class="form-group">
<label for="processName">Process Name</label>
<input type="text" class="form-control" id="processName"
placeholder="Enter process name" value={ processName }
placeholder={ defaultProcessName } value={ processName }
onChange={ linkState(this, 'processName') }/>
</div>
<div class="form-group">
<label for="processDescription">Process Description</label>
<input type="text" class="form-control" id="processDescription"
placeholder="Enter process name" value={ processDescription }
placeholder={ defaultProcessDescription } value={ processDescription }
onChange={ linkState(this, 'processDescription') } />
</div>
<div class="form-group">
<label for="inputs">Inputs</label>
<textarea class="form-control" id="inputs"
style="font-family: monospace;" rows="20"
value={ inputsFunctionText }></textarea>
<div>
<div class="mb-2">
<button class="btn btn-primary mr-2" onclick={ e => {
e.preventDefault();
$('#' + insertDialogId).modal();
} }>Insert</button>
<button class="btn btn-primary mr-2" onclick={ e => {
e.preventDefault();
$('#' + insertManyDialogId).modal();
} }>Insert Many</button>
</div>
<textarea class="form-control" ref={ this.inputsTextArea } id="inputs"
style="font-family: monospace;" rows="20"
value={ inputsFunctionText }
onChange={ linkState(this, 'inputsFunctionText') }></textarea>
<div class="my-2">
<button class="btn btn-primary" onclick={ e => {
e.preventDefault();
try {
let inputsVal = eval(inputsFunctionText);
inputsVal = uuidsToCwlObjects(inputsVal);
this.setState({ 'inputsPreview': JSON.stringify(inputsVal, null, 2) });
} catch (exc) {
this.setState({ 'inputsPreview': exc });
}
} }>Preview</button>
</div>
<textarea class="form-control" readonly="readonly"
style="font-family: monospace;" rows="10"
value={ inputsPreview }></textarea>
</div>
</div>
</form>) : <div>Loading...</div> }
</div>


Завантаження…
Відмінити
Зберегти