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.

142 lines
5.5KB

  1. //
  2. // Copyright (C) Stanislaw Adaszewski, 2020
  3. // Contact: s.adaszewski@gmail.com
  4. // Website: https://adared.ch/wba
  5. // License: GNU Affero General Public License, Version 3
  6. //
  7. import { h, Component } from 'preact';
  8. import WBTable from 'wb-table';
  9. import makeArvadosRequest from 'make-arvados-request';
  10. import wbProcessStateName from 'wb-process-state-name';
  11. function getAll(makeRequest) {
  12. let prom = makeRequest(0);
  13. prom = prom.then(xhr => {
  14. const { items, limit, items_available } = xhr.response;
  15. let res = [].concat(items);
  16. let prom_1 = new Promise(accept => accept());
  17. for (let ofs = limit; ofs < items_available; ofs += limit) {
  18. prom_1 = prom_1.then(() => makeRequest(ofs));
  19. prom_1 = prom_1.then(xhr_1 => {
  20. res = res.concat(xhr_1.response.items);
  21. });
  22. }
  23. prom_1 = prom_1.then(() => res);
  24. return prom_1;
  25. });
  26. return prom;
  27. }
  28. class WBProcessDashboard extends Component {
  29. constructor(...args) {
  30. super(...args);
  31. this.state.rows = Array(5).fill(Array(6).fill('-'));
  32. }
  33. fetchData() {
  34. const { app, parentProcessUuid } = this.props;
  35. const { arvHost, arvToken } = app.state;
  36. let prom = new Promise(accept => accept());
  37. if (parentProcessUuid) {
  38. prom = prom.then(() => {
  39. return makeArvadosRequest(arvHost, arvToken,
  40. '/arvados/v1/container_requests/' + encodeURIComponent(parentProcessUuid));
  41. });
  42. prom = prom.then(xhr => {
  43. const cr = xhr.response;
  44. if (!cr.container_uuid)
  45. return [];
  46. const filters = [ [ 'requesting_container_uuid', '=', cr.container_uuid ] ];
  47. return getAll(ofs =>
  48. makeArvadosRequest(arvHost, arvToken,
  49. '/arvados/v1/container_requests?filters=' +
  50. encodeURIComponent(JSON.stringify(filters)) +
  51. '&order=' + encodeURIComponent(JSON.stringify(['uuid asc'])) +
  52. '&offset=' + ofs));
  53. });
  54. } else {
  55. prom = prom.then(() => {
  56. return getAll(ofs => makeArvadosRequest(arvHost, arvToken,
  57. '/arvados/v1/container_requests?order=' +
  58. encodeURIComponent(JSON.stringify(['uuid asc'])) + '&offset=' + ofs));
  59. });
  60. }
  61. let crlist;
  62. prom = prom.then(crl => {
  63. crlist = crl;
  64. const uuids = crlist.map(a => a.container_uuid);
  65. // uuids = uuids.slice(0, 2);
  66. // crlist.map(a => ( crdict[a.uuid] = a));
  67. const filters = [ [ 'uuid', 'in', uuids ] ];
  68. return getAll(ofs => makeArvadosRequest(arvHost, arvToken,
  69. '/arvados/v1/containers?filters=' + encodeURIComponent(JSON.stringify(filters)) +
  70. '&order=' + encodeURIComponent(JSON.stringify([ 'uuid asc' ])) +
  71. '&offset=' + ofs));
  72. });
  73. prom = prom.then(cl => {
  74. cl.map(a => (crlist.find(b => (b.container_uuid === a.uuid)).container = a));
  75. crlist.map(a => (a.wb_state = wbProcessStateName(a, a.container)));
  76. const stats = {};
  77. for (let state in { 'Pending': 1, 'Running': 1, 'Complete': 1, 'Failed': 1, 'Cancelled': 1 }) {
  78. const f = crlist.filter(a => (a.wb_state === state));
  79. stats[state] = { 'Count': f.length };
  80. if (state === 'Pending')
  81. f.map(a => (a.wb_wait_time = (new Date() - new Date(a.created_at)) / 3.6e6));
  82. else
  83. f.map(a => (a.wb_wait_time = Math.max(0, (a.container ? new Date(a.container.started_at) : new Date(0)) - new Date(a.created_at)) / 3.6e6));
  84. f.sort((a, b) => (a.wb_wait_time - b.wb_wait_time));
  85. stats[state]['Shortest Wait Time'] = f.length ? (f[0].wb_wait_time.toFixed(2) + ' hours') : '-';
  86. stats[state]['Longest Wait Time'] = f.length ? (f[f.length - 1].wb_wait_time.toFixed(2) + ' hours') : '-';
  87. if (state === 'Pending')
  88. f.map(a => (a.wb_run_time = 0));
  89. else if (state === 'Running')
  90. f.map(a => (a.wb_run_time = (new Date() - new Date(a.container.started_at)) / 3.6e6));
  91. else
  92. f.map(a => (a.wb_run_time = Math.max(0, a.container ? new Date(a.container.finished_at) - new Date(a.container.started_at) : 0) / 3.6e6));
  93. f.sort((a, b) => (a.wb_run_time - b.wb_run_time));
  94. stats[state]['Shortest Run Time'] = f.length ? (f[0].wb_run_time.toFixed(2) + ' hours') : '-';
  95. stats[state]['Longest Run Time'] = f.length ? (f[f.length - 1].wb_run_time.toFixed(2) + ' hours') : '-';
  96. }
  97. const rows = [];
  98. for (let st in { 'Count': 1, 'Shortest Wait Time': 1, 'Longest Wait Time': 1,
  99. 'Shortest Run Time': 1, 'Longest Run Time': 1}) {
  100. rows.push([st].concat(['Pending', 'Running', 'Complete', 'Failed', 'Cancelled'].map(a => stats[a][st])));
  101. }
  102. this.setState({ rows });
  103. });
  104. }
  105. componentDidMount() {
  106. if (!this.props.lazy)
  107. this.fetchData();
  108. }
  109. componentWillReceiveProps(nextProps) {
  110. if (nextProps.parentProcessUuid === this.props.parentProcessUuid)
  111. return;
  112. if (this.props.lazy) {
  113. this.setState({ rows: Array(5).fill(Array(6).fill('-')) });
  114. return;
  115. }
  116. this.props = nextProps;
  117. this.fetchData();
  118. }
  119. render({ lazy }, { rows }) {
  120. return (
  121. <div>
  122. <WBTable columns={ [ 'State', 'Pending', 'Running', 'Complete', 'Failed', 'Cancelled' ] }
  123. rows={ rows } verticalHeader={ true } />
  124. { lazy ? (
  125. <a href="#" onclick={ e => { e.preventDefault(); this.fetchData(); } }>Refresh</a>
  126. ) : null }
  127. </div>
  128. );
  129. }
  130. }
  131. export default WBProcessDashboard;