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!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
8.0KB

  1. import { h, Component, createRef } from 'preact';
  2. import WBNavbarCommon from 'wb-navbar-common';
  3. import WBArvadosCrumbs from 'wb-arvados-crumbs';
  4. import WBToolboxDialog from 'wb-toolbox-dialog';
  5. import WBBrowseDialog from 'wb-browse-dialog';
  6. import makeArvadosRequest from 'make-arvados-request';
  7. import linkState from 'linkstate';
  8. function createInputsTemplate(workflow) {
  9. const g = JSON.parse(workflow.definition)['$graph'];
  10. const main = g.find(it => (it.id === '#main'));
  11. let res = '';
  12. main.inputs.map(it => {
  13. let id = it.id.split('/');
  14. id = id[id.length - 1];
  15. if (it.label) res += ' // ' + it.label + '\n';
  16. if (it.doc) res += ' // ' + it.doc + '\n';
  17. res += ' // Type: ' + ((typeof(it.type) === 'string') ?
  18. it.type : JSON.stringify(it.type)) + '\n\n';
  19. res += ' \'' + id + '\': \'\',\n\n';
  20. //res += ' // ' + ' '.repeat(id.length) + '^^^^\n\n';
  21. });
  22. /* let res = main.inputs.map(it => { it.value = null; return it; });
  23. res = JSON.stringify(res, null, 2);
  24. res = res.split('\n');
  25. res = res.map((ln, i) => (i == 0 ? ln : ' ' + ln));
  26. res = res.join('\n'); */
  27. return res;
  28. }
  29. function uuidsToCwlObjects(spec) {
  30. if (typeof(spec) === 'string') {
  31. if (/^[a-z0-9]{5}-[a-z0-9]{5}-[a-z0-9]{15}/.exec(spec)) {
  32. return {
  33. 'class': 'Directory',
  34. 'location': 'keep:' + spec
  35. };
  36. } else if (/^[a-f0-9]{32}\+[0-9]+/.exec(spec)) {
  37. return {
  38. 'class': 'Directory',
  39. 'location': 'keep:' + spec
  40. };
  41. } else {
  42. return spec;
  43. }
  44. } else if (typeof(spec) === 'object') {
  45. const res = (spec instanceof Array) ? [] : {};
  46. Object.keys(spec).map(k => (res[k] = uuidsToCwlObjects(spec[k])));
  47. return res;
  48. } else {
  49. return spec;
  50. }
  51. }
  52. class WBLaunchWorkflowPage extends Component {
  53. constructor(...args) {
  54. super(...args);
  55. this.state.browseDialogId = uuid.v4();
  56. this.state.insertDialogId = uuid.v4();
  57. this.state.insertManyDialogId = uuid.v4();
  58. this.state.realBrowseDialogId = uuid.v4();
  59. this.inputsTextArea = createRef();
  60. }
  61. componentDidMount() {
  62. let { app, workflowUuid } = this.props;
  63. let { arvHost, arvToken } = app.state;
  64. let prom = makeArvadosRequest(arvHost, arvToken,
  65. '/arvados/v1/workflows/' + workflowUuid);
  66. prom = prom.then(xhr => this.setState({
  67. 'workflow': xhr.response,
  68. 'defaultProcessName': xhr.response.name + ' ' + (new Date().toISOString()),
  69. 'defaultProcessDescription': xhr.response.description,
  70. 'inputsFunctionText': '(() => {\n return {\n' +
  71. createInputsTemplate(xhr.response) +
  72. ' };\n})()'
  73. }));
  74. }
  75. render({ app, workflowUuid },
  76. { workflow, projectUuid, processName, processDescription,
  77. defaultProcessName, defaultProcessDescription,
  78. inputsFunctionText, browseDialogId,
  79. insertDialogId, insertManyDialogId,
  80. realBrowseDialogId, inputsPreview }) {
  81. return (
  82. <div>
  83. <WBNavbarCommon app={ app } />
  84. <WBToolboxDialog app={ app } id={ browseDialogId }
  85. items={ app.state.toolboxItems } onAccepted={ value =>
  86. this.setState({ 'projectUuid': value }) } />
  87. <WBToolboxDialog app={ app } id={ insertDialogId }
  88. items={ app.state.toolboxItems }
  89. onAccepted={ value => {
  90. const t = this.inputsTextArea.current;
  91. const start = t.selectionStart;
  92. const end = t.selectionEnd;
  93. this.setState({
  94. 'inputsFunctionText': t.value.substr(0, start) + value +
  95. t.value.substr(end)
  96. });
  97. } } />
  98. <WBToolboxDialog app={ app } id={ insertManyDialogId }
  99. items={ app.state.toolboxItems } selectMany={ true }
  100. onAccepted={ values => alert(values) } />
  101. <WBBrowseDialog app={ app } id={ realBrowseDialogId } />
  102. { workflow ?
  103. (<form class="container-fluid">
  104. <h1>Launch Workflow</h1>
  105. <div class="form-group">
  106. <label>Workflow</label>
  107. <WBArvadosCrumbs app={ app } uuid={ workflowUuid } />
  108. </div>
  109. <div class="form-group">
  110. <label for="projectUuid">Project UUID</label>
  111. <div>
  112. { projectUuid ? (
  113. <WBArvadosCrumbs app={ app } uuid={ projectUuid } />
  114. ) : null }
  115. <div class="input-group mb-3">
  116. <input type="text" class="form-control" id="projectUuid"
  117. placeholder="Enter Project UUID" aria-label="Project UUID"
  118. aria-describedby="button-addon2" value={ projectUuid }
  119. onChange={ linkState(this, 'projectUuid') } />
  120. <div class="input-group-append">
  121. <button class="btn btn-primary" type="button"
  122. id="button-addon2" onclick={ e => { e.preventDefault();
  123. $('#' + browseDialogId).modal(); } }>Browse</button>
  124. </div>
  125. </div>
  126. </div>
  127. </div>
  128. <div class="form-check">
  129. <input type="checkbox" class="form-check-input" id="createSubproject" />
  130. <label class="form-check-label" for="createSubproject">Create subproject</label>
  131. </div>
  132. <div class="form-group">
  133. <label for="processName">Process Name</label>
  134. <input type="text" class="form-control" id="processName"
  135. placeholder={ defaultProcessName } value={ processName }
  136. onChange={ linkState(this, 'processName') }/>
  137. </div>
  138. <div class="form-group">
  139. <label for="processDescription">Process Description</label>
  140. <input type="text" class="form-control" id="processDescription"
  141. placeholder={ defaultProcessDescription } value={ processDescription }
  142. onChange={ linkState(this, 'processDescription') } />
  143. </div>
  144. <div class="form-group">
  145. <label for="inputs">Inputs</label>
  146. <div>
  147. <div class="mb-2">
  148. <button class="btn btn-primary mr-2" onclick={ e => {
  149. e.preventDefault();
  150. $('#' + insertDialogId).modal();
  151. } }>Insert</button>
  152. <button class="btn btn-primary mr-2" onclick={ e => {
  153. e.preventDefault();
  154. $('#' + insertManyDialogId).modal();
  155. } }>Insert Many</button>
  156. <button class="btn btn-primary mr-2" onclick={ e => {
  157. e.preventDefault();
  158. $('#' + realBrowseDialogId).modal();
  159. } }>Insert Browse</button>
  160. </div>
  161. <textarea class="form-control" ref={ this.inputsTextArea } id="inputs"
  162. style="font-family: monospace;" rows="20"
  163. value={ inputsFunctionText }
  164. onChange={ linkState(this, 'inputsFunctionText') }></textarea>
  165. <div class="my-2">
  166. <button class="btn btn-primary" onclick={ e => {
  167. e.preventDefault();
  168. try {
  169. let inputsVal = eval(inputsFunctionText);
  170. inputsVal = uuidsToCwlObjects(inputsVal);
  171. this.setState({ 'inputsPreview': JSON.stringify(inputsVal, null, 2) });
  172. } catch (exc) {
  173. this.setState({ 'inputsPreview': exc });
  174. }
  175. } }>Preview</button>
  176. </div>
  177. <textarea class="form-control" readonly="readonly"
  178. style="font-family: monospace;" rows="10"
  179. value={ inputsPreview }></textarea>
  180. </div>
  181. </div>
  182. </form>) : <div>Loading...</div> }
  183. </div>
  184. );
  185. }
  186. }
  187. export default WBLaunchWorkflowPage;