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!
Browse Source

Added WBCollectionListing, started implementing WBArvadosCollection.

master
parent
commit
7ffca1b48c
8 changed files with 221 additions and 18 deletions
  1. +1
    -0
      frontend/package.json
  2. +1
    -0
      frontend/rollup.config.js
  3. +1
    -0
      frontend/src/html/index.html
  4. +128
    -0
      frontend/src/js/component/wb-collection-listing.js
  5. +50
    -0
      frontend/src/js/misc/wb-arvados-collection.js
  6. +2
    -1
      frontend/src/js/page/wb-app.js
  7. +18
    -5
      frontend/src/js/page/wb-browse.js
  8. +20
    -12
      frontend/src/js/widget/wb-pagination.js

+ 1
- 0
frontend/package.json View File

@@ -2,6 +2,7 @@
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "^5.12.0", "@fortawesome/fontawesome-free": "^5.12.0",
"bootstrap": "^4.4.1", "bootstrap": "^4.4.1",
"filesize": "^6.0.1",
"jquery": "^3.4.1", "jquery": "^3.4.1",
"js-uuid": "0.0.6", "js-uuid": "0.0.6",
"linkstate": "^1.1.1", "linkstate": "^1.1.1",


+ 1
- 0
frontend/rollup.config.js View File

@@ -42,6 +42,7 @@ export default {
'node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff': 'dist/webfonts/fa-brands-400.woff', 'node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff': 'dist/webfonts/fa-brands-400.woff',
'node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff2': 'dist/webfonts/fa-brands-400.woff2', 'node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff2': 'dist/webfonts/fa-brands-400.woff2',
'node_modules/js-uuid/js-uuid.js': 'dist/js/js-uuid.js', 'node_modules/js-uuid/js-uuid.js': 'dist/js/js-uuid.js',
'node_modules/filesize/lib/filesize.js': 'dist/js/filesize.js',
verbose: true verbose: true
}), }),
buble({jsx: 'h'}), buble({jsx: 'h'}),


+ 1
- 0
frontend/src/html/index.html View File

@@ -8,6 +8,7 @@
<script language="javascript" src="/js/bootstrap.min.js"></script> <script language="javascript" src="/js/bootstrap.min.js"></script>
<script language="javascript" src="/js/fontawesome.min.js"></script> <script language="javascript" src="/js/fontawesome.min.js"></script>
<script language="javascript" src="/js/js-uuid.js"></script> <script language="javascript" src="/js/js-uuid.js"></script>
<script language="javascript" src="/js/filesize.js"></script>
</head> </head>
<body> <body>
<script language="javascript" src="/js/app.min.js"></script> <script language="javascript" src="/js/app.min.js"></script>


+ 128
- 0
frontend/src/js/component/wb-collection-listing.js View File

@@ -0,0 +1,128 @@
import { h, Component } from 'preact';
import { route } from 'preact-router';
import makeArvadosRequest from 'make-arvados-request';
import WBTable from 'wb-table';
import WBPagination from 'wb-pagination';
import urlForObject from 'url-for-object';
import arvadosTypeName from 'arvados-type-name';
import arvadosObjectName from 'arvados-object-name';
class WBCollectionListing extends Component {
constructor(...args) {
super(...args);
this.state.rows = [];
this.state.numPages = 0;
}
componentDidMount() {
this.fetchItems();
}
prepareRows(items, ownerLookup) {
return items.map(item => [
(<div>
<div>
<a href={ urlForObject(item) }>{ item['name'] }</a>
</div>
<div>{ item['uuid'] }</div>
</div>),
item['description'],
(<div>
<div>
{ ownerLookup[item.owner_uuid] ? (
<a href={ urlForObject(ownerLookup[item.owner_uuid]) }>
{ arvadosObjectName(ownerLookup[item.owner_uuid]) }
</a>
) : 'Not Found' }
</div>
<div>{ item.owner_uuid }</div>
</div>),
item['file_count'],
filesize(item['file_size_total'])
]);
}
fetchItems() {
let { arvHost, arvToken } = this.props.app.state;
let { activePage, itemsPerPage, ownerUuid } = this.props;
let filters = [];
if (ownerUuid)
filters.push([ 'owner_uuid', '=', ownerUuid ]);
let prom = makeArvadosRequest(arvHost, arvToken,
'/arvados/v1/collections?filters=' + encodeURIComponent(JSON.stringify(filters)) +
'&limit=' + encodeURIComponent(itemsPerPage) +
'&offset=' + encodeURIComponent(itemsPerPage * activePage));
let collections;
let numPages
prom = prom.then(xhr => {
collections = xhr.response['items'];
numPages = Math.ceil(xhr.response['items_available'] / xhr.response['limit']);
let owners = {};
collections.map(c => {
let typeName = arvadosTypeName(c.owner_uuid);
if (!(typeName in owners))
owners[typeName] = [];
owners[typeName].push(c.owner_uuid);
});
let lookup = {};
let prom_1 = new Promise(accept => accept());
for (let typeName in owners) {
let filters_1 = [
['uuid', 'in', owners[typeName]]
];
prom_1 = prom_1.then(() => makeArvadosRequest(arvHost, arvToken,
'/arvados/v1/' + typeName + 's?filters=' +
encodeURIComponent(JSON.stringify(filters_1))));
prom_1 = prom_1.then(xhr => xhr.response.items.map(item => (
lookup[item.uuid] = item)));
}
prom_1 = prom_1.then(() => lookup);
return prom_1;
});
//let ownerLookup = {};
//prom = prom.then(lookup => (ownerLookup = lookup));
prom = prom.then(ownerLookup =>
this.setState({
'numPages': numPages,
'rows': this.prepareRows(collections, ownerLookup)
}));
}
componentWillReceiveProps(nextProps, nextState) {
this.props = nextProps;
this.fetchItems();
}
render({ app, ownerUuid, activePage, getPageUrl }, { rows, numPages }) {
return (
<div>
<WBTable columns={ [ 'Name', 'Description', 'Owner', 'File Count', 'Total Size' ] }
rows={ rows } />
<WBPagination numPages={ numPages }
activePage={ activePage }
getPageUrl={ getPageUrl } />
</div>
);
}
}
WBCollectionListing.defaultProps = {
'itemsPerPage': 100,
'ownerUuid': null
};
export default WBCollectionListing;

+ 50
- 0
frontend/src/js/misc/wb-arvados-collection.js View File

@@ -0,0 +1,50 @@
import makeArvadosRequest from 'make-arvados-request';
class WBArvadosCollection {
constructor(arvHost, arvToken, uuid) {
this.arvHost = arvHost;
this.arvToken = arvToken;
this.uuid = uuid;
this.meta = null;
}
fetchMeta() {
let prom = makeArvadosRequest(this.arvHost, this.arvToken,
'/arvados/v1/collections/' + this.uuid);
prom = prom.then(xhr => {
this.meta = xhr.response;
});
return prom;
}
parseManifest() {
if (this.meta === null)
throw Error('You must call fetchMeta() first and wait for the returned Promise.');
let manifest = this.meta.manifest_text;
let streams = manifest.split('\n');
this.content = streams.map(s => {
let tokens = s.split(' ');
let streamName = tokens[0];
let rx = /^[a-f0-9]{32}\+[0-9]+/;
let n = tokens.map(t => rx.exec(t));
n = n.indexOf(null);
let locators = tokens.slice(1, n)
let fileTokens = tokens.slice(n);
let fileNames = fileTokens.map(t => t.split(':')[2]);
let fileSizes = {};
fileTokens.map(t => {
let [ start, end, name ] = t.split(':');
if (!(name in fileSizes))
fileSizes[name] = 0;
fileSizes[name] += Number(end) - Number(start);
});
fileSizes = fileNames.map(n => fileSizes[n]);
return [ streamName, fileNames, fileSizes ];
});
return this.content;
}
}
export WBArvadosCollection;

+ 2
- 1
frontend/src/js/page/wb-app.js View File

@@ -58,7 +58,8 @@ class WBApp extends Component {
<WBBrowse path="/browse/:ownerUuid?/:activePage?/:objTypeTab?/:collectionPage?/:processPage?/:workflowPage?" <WBBrowse path="/browse/:ownerUuid?/:activePage?/:objTypeTab?/:collectionPage?/:processPage?/:workflowPage?"
appCallbacks={ this.appCallbacks } appCallbacks={ this.appCallbacks }
appState={ this.appState } />
appState={ this.appState }
app={ this } />
<WBProcessView path="/process/:uuid" app={ this } /> <WBProcessView path="/process/:uuid" app={ this } />
</Router> </Router>


+ 18
- 5
frontend/src/js/page/wb-browse.js View File

@@ -6,19 +6,26 @@ import WBInlineSearch from 'wb-inline-search';
import WBProjectCrumbs from 'wb-project-crumbs'; import WBProjectCrumbs from 'wb-project-crumbs';
import WBTabs from 'wb-tabs'; import WBTabs from 'wb-tabs';
import WBProcessListing from 'wb-process-listing'; import WBProcessListing from 'wb-process-listing';
import WBCollectionListing from 'wb-collection-listing';
class WBBrowse extends Component { class WBBrowse extends Component {
route(params) {
route('/browse/' +
getUrl(params) {
let res = '/browse/' +
('ownerUuid' in params ? params.ownerUuid : (this.props.ownerUuid || '')) + '/' + ('ownerUuid' in params ? params.ownerUuid : (this.props.ownerUuid || '')) + '/' +
('activePage' in params ? params.activePage : (this.props.activePage || '')) + '/' + ('activePage' in params ? params.activePage : (this.props.activePage || '')) + '/' +
('objTypeTab' in params ? params.objTypeTab : (this.props.objTypeTab || '')) + '/' + ('objTypeTab' in params ? params.objTypeTab : (this.props.objTypeTab || '')) + '/' +
('collectionPage' in params ? params.collectionPage : (this.props.collectionPage || '')) + '/' + ('collectionPage' in params ? params.collectionPage : (this.props.collectionPage || '')) + '/' +
('processPage' in params ? params.processPage : (this.props.processPage || '')) + '/' + ('processPage' in params ? params.processPage : (this.props.processPage || '')) + '/' +
('workflowPage' in params ? params.workflowPage : (this.props.workflowPage || '')));
('workflowPage' in params ? params.workflowPage : (this.props.workflowPage || ''));
return res;
}
route(params) {
route(this.getUrl(params));
} }
render({ ownerUuid, activePage, appCallbacks, appState,
render({ ownerUuid, activePage, appCallbacks, appState, app,
objTypeTab, collectionPage, processPage, workflowPage }) { objTypeTab, collectionPage, processPage, workflowPage }) {
return ( return (
@@ -51,13 +58,19 @@ class WBBrowse extends Component {
{ {
(!objTypeTab || objTypeTab === 'collection') ? ( (!objTypeTab || objTypeTab === 'collection') ? (
null
<WBCollectionListing app={ app }
ownerUuid={ ownerUuid }
itemsPerPage="20"
activePage={ Number(collectionPage || 0) }
getPageUrl={ i => this.getUrl({ 'collectionPage': i }) } />
) : (objTypeTab === 'process' ? ( ) : (objTypeTab === 'process' ? (
<WBProcessListing appState={ appState } <WBProcessListing appState={ appState }
ownerUuid={ ownerUuid } ownerUuid={ ownerUuid }
itemsPerPage="20" itemsPerPage="20"
activePage={ Number(processPage || 0) } activePage={ Number(processPage || 0) }
onPageChanged={ i => this.route({ 'processPage': i }) } /> onPageChanged={ i => this.route({ 'processPage': i }) } />
) : (objTypeTab === 'workflow' ? ( ) : (objTypeTab === 'workflow' ? (
null null
) : null)) ) : null))


+ 20
- 12
frontend/src/js/widget/wb-pagination.js View File

@@ -1,7 +1,7 @@
import { h, Component } from 'preact'; import { h, Component } from 'preact';
class WBPagination extends Component { class WBPagination extends Component {
renderVisiblePages(numPages, activePage, chunkSize, onPageChanged) {
renderVisiblePages(numPages, activePage, chunkSize, onPageChanged, getPageUrl) {
let visible = {}; let visible = {};
let begActChnk = activePage - Math.floor(chunkSize / 2); let begActChnk = activePage - Math.floor(chunkSize / 2);
@@ -23,8 +23,8 @@ class WBPagination extends Component {
res.push(( res.push((
<li class={ activePage === 0 ? "page-item disabled" : "page-item" }> <li class={ activePage === 0 ? "page-item disabled" : "page-item" }>
<a class="page-link" href="#"
onclick={ e => { e.preventDefault(); onPageChanged(activePage - 1); } }>Previous</a>
<a class="page-link" href={ getPageUrl(activePage - 1) }
onclick={ e => this.changePage(e, activePage - 1) }>Previous</a>
</li> </li>
)); ));
@@ -33,35 +33,42 @@ class WBPagination extends Component {
if (i > prev + 1) if (i > prev + 1)
res.push(( res.push((
<li class="page-item"> <li class="page-item">
<a class="page-link" href="#"
onclick={ e => { e.preventDefault(); onPageChanged(i - 1); } }>...</a>
<a class="page-link" href={ getPageUrl(i - 1) }
onclick={ e => this.changePage(e, i - 1) }>...</a>
</li> </li>
)); ));
prev = i; prev = i;
res.push(( res.push((
<li class={ i === activePage ? "page-item active" : "page-item" }> <li class={ i === activePage ? "page-item active" : "page-item" }>
<a class="page-link" href="#"
onclick={ e => { e.preventDefault(); onPageChanged(i); } }>{ i + 1 }</a>
<a class="page-link" href={ getPageUrl(i) }
onclick={ e => this.changePage(e, i) }>{ i + 1 }</a>
</li> </li>
)); ));
} }
res.push(( res.push((
<li class={ activePage >= numPages - 1 ? "page-item disabled" : "page-item" }> <li class={ activePage >= numPages - 1 ? "page-item disabled" : "page-item" }>
<a class="page-link" href="#"
onclick={ e => { e.preventDefault(); onPageChanged(activePage + 1); } }>Next</a>
<a class="page-link" href={ getPageUrl(activePage + 1) }
onclick={ e => this.changePage(e, activePage + 1) }>Next</a>
</li> </li>
)); ));
return res; return res;
} }
render({ numPages, activePage, chunkSize, onPageChanged }) {
changePage(e, pageIdx) {
if (this.props.onPageChanged) {
e.preventDefault();
this.props.onPageChanged(pageIdx);
}
}
render({ numPages, activePage, chunkSize, onPageChanged, getPageUrl }) {
return ( return (
<nav aria-label="Pagination"> <nav aria-label="Pagination">
<ul class="pagination"> <ul class="pagination">
{ this.renderVisiblePages(numPages, activePage, chunkSize, onPageChanged) }
{ this.renderVisiblePages(numPages, activePage, chunkSize, onPageChanged, getPageUrl) }
</ul> </ul>
</nav> </nav>
); );
@@ -69,7 +76,8 @@ class WBPagination extends Component {
} }
WBPagination.defaultProps = { WBPagination.defaultProps = {
'chunkSize': 5
'chunkSize': 5,
'getPageUrl': () => ('#')
}; };
export default WBPagination; export default WBPagination;

Loading…
Cancel
Save