| @@ -0,0 +1,45 @@ | |||||
| import { h, Component } from 'preact'; | |||||
| import WBTable from 'wb-table'; | |||||
| import WBBreadcrumbs from 'wb-breadcrumbs'; | |||||
| import { WBManifestReader } from 'wb-collection-manifest'; | |||||
| import makeArvadosRequest from 'make-arvados-request'; | |||||
| class WBCollectionContent extends Component { | |||||
| constructor(...args) { | |||||
| super(...args); | |||||
| this.state.path = '.'; | |||||
| this.state.rows = []; | |||||
| this.state.manifestReader = null; | |||||
| } | |||||
| componentDidMount() { | |||||
| let { arvHost, arvToken } = this.props.app.state; | |||||
| let { uuid } = this.props; | |||||
| let select = [ 'manifest_text' ]; | |||||
| let prom = makeArvadosRequest(arvHost, arvToken, | |||||
| '/arvados/v1/collections/' + uuid + | |||||
| '?select=' + encodeURIComponent(JSON.stringify(select))); | |||||
| prom = prom.then(xhr => { | |||||
| this.state.manifestReader = new WBManifestReader(xhr.response.manifest_text); | |||||
| this.prepareRows(); | |||||
| }); | |||||
| } | |||||
| prepareRows() { | |||||
| this.setState({}); | |||||
| } | |||||
| render({}, { rows }) { | |||||
| return ( | |||||
| <div> | |||||
| <WBBreadcrumbs items={ [ 'a', 'b', 'c' ] } /> | |||||
| <WBTable columns={ [ 'Name', 'Size', 'Actions' ] } | |||||
| rows={ rows } /> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| } | |||||
| export default WBCollectionContent; | |||||
| @@ -42,7 +42,7 @@ class WBArvadosCollection { | |||||
| fileSizes = fileNames.map(n => fileSizes[n]); | fileSizes = fileNames.map(n => fileSizes[n]); | ||||
| return [ streamName, fileNames, fileSizes ]; | return [ streamName, fileNames, fileSizes ]; | ||||
| }); | }); | ||||
| return this.content; | return this.content; | ||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,114 @@ | |||||
| class CMDirectory { | |||||
| constructor() { | |||||
| this.directories = {}; | |||||
| this.files = {}; | |||||
| } | |||||
| } | |||||
| class CMFile { | |||||
| constructor() { | |||||
| this.blockRefs = []; | |||||
| this.size = 0; | |||||
| } | |||||
| append(locators, position, size) { | |||||
| if (size === 0) | |||||
| return; | |||||
| let cum = 0; | |||||
| let locHashes = locators.map(loc => loc[0]); | |||||
| let locSizes = locators.map(loc => loc[1]); | |||||
| let locPositions = locators.map(loc => { | |||||
| let res = cum; | |||||
| cum += loc[1]; | |||||
| return res; | |||||
| }); | |||||
| let used = locators.map((_, i) => (locPositions[i] + locSizes[i] > position && | |||||
| locPositions[i] < position + size)); | |||||
| let startBlock = used.indexOf(true); | |||||
| let endBlock = used.lastIndexOf(true) + 1; | |||||
| console.log('startBlock: ' + startBlock + ', endBlock: ' + endBlock); | |||||
| if (startBlock === -1) | |||||
| return; | |||||
| let blockRefs = []; | |||||
| let runPos = position; | |||||
| let runSize = size; | |||||
| for (let i = startBlock; i < endBlock; i++) { | |||||
| let blockPos = runPos - locPositions[i]; | |||||
| let blockSize = Math.min(runSize, locSizes[i] - blockPos); | |||||
| blockRefs.push([ locHashes[i], blockPos, blockSize ]); | |||||
| runPos += blockSize; | |||||
| runSize -= blockSize; | |||||
| } | |||||
| this.blockRefs = this.blockRefs.concat(blockRefs); | |||||
| this.size += size; | |||||
| } | |||||
| } | |||||
| class WBManifestReader { | |||||
| constructor(manifest_text) { | |||||
| this.rootDir = new CMDirectory(); | |||||
| if (!manifest_text) | |||||
| return; | |||||
| this.parse(manifest_text); | |||||
| } | |||||
| makeDir(parent, name) { | |||||
| if (!(name in parent.directories)) | |||||
| parent.directories[name] = new CMDirectory(); | |||||
| return parent.directories[name]; | |||||
| } | |||||
| makePath(path) { | |||||
| if (typeof(path) === 'string') | |||||
| path = path.split('/'); | |||||
| let dir = this.rootDir; | |||||
| for (let i = 1; i < path.length; i++) | |||||
| dir = this.makeDir(dir, path[i]); | |||||
| return dir; | |||||
| } | |||||
| appendFile(streamName, locators, position, size, fileName) { | |||||
| let path = streamName + '/' + fileName; | |||||
| path = path.split('/'); | |||||
| let dir = this.makePath(path.slice(0, path.length - 1)); | |||||
| if (!(fileName in dir.files)) | |||||
| dir.files[fileName] = new CMFile(); | |||||
| dir.files[fileName].append(locators, position, size); | |||||
| } | |||||
| parse(manifest_text) { | |||||
| let rx = /^[a-f0-9]{32}\+[0-9]+/; | |||||
| let streams = manifest_text.split('\n'); | |||||
| streams.map(s => { | |||||
| let tokens = s.split(' '); | |||||
| let streamName = tokens[0]; | |||||
| let n = tokens.map(t => rx.exec(t)); | |||||
| n = n.indexOf(null, 1); | |||||
| let locators = tokens.slice(1, n); | |||||
| locators = locators.map(loc => [ loc, Number(loc.split('+')[1]) ]); | |||||
| let fileTokens = tokens.slice(n); | |||||
| fileTokens.map(t => { | |||||
| let [ position, size, fileName ] = t.split(':'); | |||||
| this.appendFile(streamName, locators, | |||||
| Number(position), Number(size), fileName); | |||||
| }); | |||||
| }); | |||||
| } | |||||
| } | |||||
| export { WBManifestReader }; | |||||
| @@ -5,6 +5,7 @@ import WBSignIn from 'wb-sign-in'; | |||||
| import WBLandingPage from 'wb-landing-page'; | import WBLandingPage from 'wb-landing-page'; | ||||
| import WBProcessView from 'wb-process-view'; | import WBProcessView from 'wb-process-view'; | ||||
| import WBCollectionView from 'wb-collection-view'; | import WBCollectionView from 'wb-collection-view'; | ||||
| import WBCollectionBrowse from 'wb-collection-browse'; | |||||
| import arvadosTypeName from 'arvados-type-name'; | import arvadosTypeName from 'arvados-type-name'; | ||||
| class WBApp extends Component { | class WBApp extends Component { | ||||
| @@ -65,6 +66,8 @@ class WBApp extends Component { | |||||
| <WBProcessView path="/process/:uuid" app={ this } /> | <WBProcessView path="/process/:uuid" app={ this } /> | ||||
| <WBCollectionView path="/collection/:uuid" app={ this } /> | <WBCollectionView path="/collection/:uuid" app={ this } /> | ||||
| <WBCollectionBrowse path='/collection-browse/:uuid' app={ this } /> | |||||
| </Router> | </Router> | ||||
| ); | ); | ||||
| } | } | ||||
| @@ -0,0 +1,24 @@ | |||||
| import { h, Component } from 'preact'; | |||||
| import WBNavbarCommon from 'wb-navbar-common'; | |||||
| import WBArvadosCrumbs from 'wb-arvados-crumbs'; | |||||
| import WBCollectionContent from 'wb-collection-content'; | |||||
| class WBCollectionBrowse extends Component { | |||||
| render({ app, uuid }, {}) { | |||||
| return ( | |||||
| <div> | |||||
| <WBNavbarCommon app={ app } /> | |||||
| <WBArvadosCrumbs app={ app } uuid={ uuid } /> | |||||
| <div class="my-2"> | |||||
| This is the collection browser for { uuid } | |||||
| </div> | |||||
| <WBCollectionContent app={ app } uuid={ uuid } /> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| } | |||||
| export default WBCollectionBrowse; | |||||