Skip to content

Commit

Permalink
Add CSV downloads to the list view
Browse files Browse the repository at this point in the history
Add the ability to download the current list view selection as a CSV
file (sorts are retained).

Closes #96
  • Loading branch information
brianlove committed Jan 17, 2024
1 parent b6e75da commit 25cf2bc
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 8 deletions.
11 changes: 11 additions & 0 deletions web/gui-v2/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions web/gui-v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"luxon": "^3.3.0",
"plotly.js": "^2.24.3",
"react": "^18.2.0",
"react-csv": "^2.2.2",
"react-dom": "^18.2.0",
"react-plotly.js": "^2.6.0",
"react-use-query-param-string": "^2.0.10",
Expand Down
70 changes: 63 additions & 7 deletions web/gui-v2/src/components/ListViewTable.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { CSVLink } from 'react-csv';
import { getQueryParams, useQueryParamString } from 'react-use-query-param-string';
import { css } from '@emotion/react';
import {
Expand Down Expand Up @@ -541,6 +542,57 @@ const ListViewTable = ({
.map(([key, data]) => [key, <>Total: {commas(data)}</>])
);


const exportHeaders = columns.flatMap((colDef) => {
const headers = [ { key: colDef.key, label: colDef.title } ];
// Derived columns aren't able to calculate a rank
if ( colDef.type === "slider" && !colDef?.isDerived ) {
headers.push({ key: `${colDef.key}_rank`, label: `${colDef.title} rank` });
}
return headers;
});
const exportData = useMemo(() => {
return dataForTable
.map((row) => {
const entry = {};
for ( const colDef of columns ) {
if ( Object.hasOwn(colDef, "dataKey") && Object.hasOwn(colDef, "dataSubkey") ) {
const value = row[colDef.dataKey][colDef.dataSubkey];
if ( colDef.type === "slider" && !colDef?.isDerived ) {
entry[colDef.key] = value.total;
entry[`${colDef.key}_rank`] = value.rank;
} else if ( Object.hasOwn(colDef, "extract") ) {
entry[colDef.key] = colDef.extract(value, row);
}
} else if ( Object.hasOwn(colDef, "dataKey") ) {
entry[colDef.key] = row[colDef.dataKey];
} else {
entry[colDef.key] = row[colDef.key];
}
}
return entry;
})
.sort((a, b) => {
const direction = sortDir ? -1 : 1;
if ( Object.hasOwn(a, sortKey) && Object.hasOwn(b, sortKey) ) {
if ( SLIDER_COLUMNS.includes(sortKey) ) {
if ( a[sortKey] < b[sortKey] ) {
return -1 * direction;
} else if ( b[sortKey] < a[sortKey] ) {
return 1 * direction;
} else {
return 0;
}
} else {
return a[sortKey].localeCompare(b[sortKey]) * direction;
}
}
// If the sorting column isn't visible, default to sorting by company
// name (which is always visible).
return a.name.localeCompare(b.name) * direction;
});
}, [dataForTable]);

return (
<div id="table" className="list-view-table" data-testid="list-view-table">
<div css={styles.buttonBar}>
Expand All @@ -560,13 +612,17 @@ const ListViewTable = ({
Reset filters
</span>
</Button>
{/* TODO: enable once downloads are possible - see #96 and #166 */}
<Button css={styles.buttonBarButton} disabled>
<DownloadIcon />
<span className={classes([windowSize < 780 && "sr-only"])}>
Download results
</span>
</Button>
<CSVLink data={exportData} filename="eto-parat-export.csv" headers={exportHeaders}>
<Button
css={styles.buttonBarButton}
title="Download the results as a comma-separated value (CSV) file. Existing sorts will be retained."
>
<DownloadIcon />
<span className={classes([windowSize < 780 && "sr-only"])}>
Download results
</span>
</Button>
</CSVLink>
<Button css={styles.buttonBarButton} onClick={() => setDialogOpen(true)}>
<AddCircleOutlineIcon />
<span className={classes([windowSize < 650 && "sr-only"])}>
Expand Down
2 changes: 1 addition & 1 deletion web/gui-v2/src/static_data/table_columns.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ const columnDefinitions = [
key: "ai_pubs_percent",
...generateSliderColDef(
"articles",
"ai_pubs",
"ai_publications",
((_val, row) => {
return Math.round(row.articles.ai_publications.total / row.articles.all_publications.total * 1000) / 10;
}),
Expand Down

0 comments on commit 25cf2bc

Please sign in to comment.