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.

210 lines
7.5KB

  1. import { h, Component } from 'preact';
  2. import WBTable from 'wb-table';
  3. import WBBreadcrumbs from 'wb-breadcrumbs';
  4. import WBPagination from 'wb-pagination';
  5. import makeArvadosRequest from 'make-arvados-request';
  6. import wbDownloadFile from 'wb-download-file';
  7. import WBManifestWorkerWrapper from 'wb-manifest-worker-wrapper';
  8. function unescapeName(name) {
  9. return name.replace(/(\\\\|\\[0-9]{3})/g,
  10. (_, $1) => ($1 === '\\\\' ? '\\' : String.fromCharCode(parseInt($1.substr(1), 8))));
  11. }
  12. class WBCollectionContent extends Component {
  13. constructor(...args) {
  14. super(...args);
  15. this.state.rows = [];
  16. this.state.manifestWorker = new WBManifestWorkerWrapper();
  17. this.state.loaded = 0;
  18. this.state.total = 0;
  19. this.state.mode = 'manifestDownload';
  20. this.state.parsedStreams = 0;
  21. this.state.totalStreams = 1;
  22. }
  23. getUrl(params) {
  24. let res = '/collection-browse/' +
  25. ('uuid' in params ? params.uuid : this.props.uuid) + '/' +
  26. encodeURIComponent('collectionPath' in params ? params.collectionPath : this.props.collectionPath) + '/' +
  27. ('page' in params ? params.page : this.props.page);
  28. return res;
  29. }
  30. componentDidMount() {
  31. let { arvHost, arvToken } = this.props.app.state;
  32. let { uuid, collectionPath } = this.props;
  33. let { manifestWorker } = this.state;
  34. let select = [ 'manifest_text' ];
  35. let prom = makeArvadosRequest(arvHost, arvToken,
  36. '/arvados/v1/collections/' + uuid +
  37. '?select=' + encodeURIComponent(JSON.stringify(select)),
  38. { 'onProgress': e => {
  39. this.setState({ 'loaded': e.loaded, 'total': e.total });
  40. } });
  41. prom = prom.then(xhr => {
  42. const streams = xhr.response.manifest_text.split('\n');
  43. const paths = streams.filter(s => s).map(s => {
  44. const n = s.indexOf(' ');
  45. return unescapeName(s.substr(0, n));
  46. });
  47. let prom_1 = new Promise(accept => accept());
  48. prom_1 = prom_1.then(() => {
  49. this.setState({
  50. 'totalStreams': streams.length,
  51. 'parsedStreams': 0,
  52. 'mode': 'manifestParse'
  53. });
  54. return manifestWorker.postMessage([ 'precreatePaths', paths ]);
  55. });
  56. for (let i = 0; i < streams.length; i++) {
  57. prom_1 = prom_1.then(() => manifestWorker.postMessage([ 'parseStream', streams[i] ]));
  58. prom_1 = prom_1.then(() => manifestWorker.postMessage([
  59. 'listDirectory', '.' + this.props.collectionPath, true
  60. ]));
  61. prom_1 = prom_1.then(e => {
  62. this.prepareRows(e.data[1]);
  63. this.setState({ 'parsedStreams': (i + 1) });
  64. });
  65. }
  66. prom_1 = prom_1.then(() => manifestWorker.postMessage([ 'listDirectory',
  67. '.' + this.props.collectionPath, true ]));
  68. prom_1 = prom_1.then(e => {
  69. this.state.mode = 'browsingReady';
  70. this.prepareRows(e.data[1]);
  71. });
  72. return prom_1;
  73. });
  74. }
  75. componentWillReceiveProps(nextProps) {
  76. this.props = nextProps;
  77. const { manifestWorker, mode } = this.state;
  78. const { collectionPath } = this.props;
  79. if (mode === 'browsingReady') {
  80. let prom = manifestWorker.postMessage([ 'listDirectory', '.' + collectionPath, true ]);
  81. prom = prom.then(e => this.prepareRows(e.data[1]));
  82. }
  83. }
  84. prepareRows(listing) {
  85. let { manifestWorker, mode } = this.state;
  86. let { collectionPath, page, itemsPerPage, app } = this.props;
  87. let { arvHost, arvToken } = app.state;
  88. const numPages = Math.ceil(listing.length / itemsPerPage);
  89. listing = listing.slice(page * itemsPerPage,
  90. page * itemsPerPage + itemsPerPage);
  91. this.setState({
  92. 'numPages': numPages,
  93. 'rows': listing.map(item => (
  94. (item[0] === 'd') ? [
  95. (<a href={ this.getUrl({ 'collectionPath': collectionPath + '/' + item[1], 'page': 0 }) }>{ item[1] }/</a>),
  96. 'Directory',
  97. null,
  98. (<div></div>)
  99. ] : [
  100. item[1],
  101. 'File',
  102. filesize(item[2]),
  103. ( (mode === 'browsingReady') ? (
  104. <div>
  105. <button class="btn btn-outline-primary mx-1" title="Download"
  106. onclick={ () => manifestWorker.postMessage([ 'getFile',
  107. '.' + collectionPath + '/' + item[1] ]).then(e => {
  108. const file = e.data[1];
  109. const blob = new Blob([
  110. JSON.stringify([ arvHost, arvToken, item[1], file ])
  111. ]);
  112. const blocksBlobUrl = URL.createObjectURL(blob);
  113. window.open('/download/' + encodeURIComponent(blocksBlobUrl), '_blank');
  114. }) }><i class="fas fa-download"></i></button>
  115. <button class="btn btn-outline-primary mx-1" title="View"
  116. onclick={ () => {
  117. alert('Not implemented.')
  118. } }><i class="far fa-eye"></i></button>
  119. { item[1].toLowerCase().endsWith('.nii') ? (
  120. <button class="btn btn-outline-primary mx-1" title="View Image"
  121. onclick={ () => manifestWorker.postMessage([ 'getFile',
  122. '.' + collectionPath + '/' + item[1] ]).then(e => {
  123. const file = e.data[1];
  124. const blob = new Blob([
  125. JSON.stringify({ 'name': item[1], 'file': file })
  126. ]);
  127. const blocksBlobUrl = URL.createObjectURL(blob);
  128. window.open('/image-viewer/' + encodeURIComponent(blocksBlobUrl), '_blank');
  129. }) }><i class="far fa-eye"></i></button>
  130. ) : null }
  131. </div>
  132. ) : null)
  133. ]
  134. ))
  135. });
  136. }
  137. render({ collectionPath, page }, { manifestReader, rows,
  138. numPages, loaded, total, mode, parsedStreams, totalStreams }) {
  139. return (
  140. <div>
  141. <WBBreadcrumbs items={ ('.' + collectionPath).split('/') } />
  142. { (mode === 'manifestDownload') ?
  143. (
  144. <div class="container-fluid">
  145. <div>Downloading manifest: { filesize(loaded) }</div>
  146. <div class="progress">
  147. <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar"
  148. aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"></div>
  149. </div>
  150. </div>
  151. ) : (
  152. <div>
  153. { mode === 'manifestParse' ? (
  154. <div class="container-fluid mb-2">
  155. <div>Parsing manifest: { parsedStreams }/{ totalStreams }</div>
  156. <div class="progress">
  157. <div class="progress-bar progress-bar-striped progress-bar-animated bg-success" role="progressbar"
  158. aria-valuenow={ totalStreams } aria-valuemin="0" aria-valuemax={ parsedStreams } style={ 'width: ' + Math.round(parsedStreams * 100 / totalStreams) + '%' }></div>
  159. </div>
  160. </div>
  161. ) : null }
  162. <WBTable columns={ [ 'Name', 'Type', 'Size', 'Actions' ] }
  163. rows={ rows } />
  164. <WBPagination activePage={ page } numPages={ numPages }
  165. getPageUrl={ page => this.getUrl({ 'page': page }) } />
  166. </div>
  167. ) }
  168. </div>
  169. );
  170. }
  171. }
  172. WBCollectionContent.defaultProps = {
  173. 'collectionPath': '',
  174. 'page': 0,
  175. 'itemsPerPage': 20
  176. };
  177. export default WBCollectionContent;