Skip to content

Commit

Permalink
Merge pull request #1764 from nextstrain/download-json
Browse files Browse the repository at this point in the history
Add download JSON button
  • Loading branch information
jameshadfield authored Apr 18, 2024
2 parents 2750924 + b07e540 commit 6bec286
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 2 deletions.
9 changes: 9 additions & 0 deletions src/components/download/downloadButtons.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { getNumSelectedTips } from "../../util/treeVisibilityHelpers";
const RectangularTreeIcon = withTheme(icons.RectangularTree);
const PanelsGridIcon = withTheme(icons.PanelsGrid);
const MetaIcon = withTheme(icons.Meta);
const DatasetIcon = withTheme(icons.Dataset);
const iconWidth = 25;

/**
Expand Down Expand Up @@ -47,6 +48,14 @@ export const DownloadButtons = ({dispatch, t, tree, entropy, metadata, colorBy,
<p/>
{partialData ? `Currently ${selectedTipsCount}/${totalTipCount} tips are displayed and will be downloaded.` : `Currently the entire dataset (${totalTipCount} tips) will be downloaded.`}
</div>
{!gisaidProvenance && (
<Button
name="Auspice (Nextstrain) JSON"
description={`The main Auspice dataset JSON(s) for the current view`}
icon={<DatasetIcon width={iconWidth} selected />}
onClick={() => helpers.auspiceJSON(dispatch)}
/>
)}
<Button
name={`${temporal ? 'TimeTree' : 'Tree'} (Newick)`}
description={`Phylogenetic tree in Newick format with branch lengths in units of ${temporal?'years':'divergence'}.`}
Expand Down
36 changes: 34 additions & 2 deletions src/components/download/helperFunctions.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
/* eslint no-restricted-syntax: 0 */
import { unparse } from "papaparse";
import { infoNotification, warningNotification } from "../../actions/notifications";
import { errorNotification, infoNotification, warningNotification } from "../../actions/notifications";
import { spaceBetweenTrees } from "../tree/tree";
import { getTraitFromNode, getDivFromNode, getFullAuthorInfoFromNode, getVaccineFromNode, getAccessionFromNode } from "../../util/treeMiscHelpers";
import { numericToCalendar } from "../../util/dateHelpers";
import { NODE_VISIBLE, nucleotide_gene } from "../../util/globals";
import { datasetSummary } from "../info/datasetSummary";
import { isColorByGenotype } from "../../util/getGenotype";
import { EmptyNewickTreeCreated } from "../../util/exceptions";
import { getDatasetNamesFromUrl, Dataset } from "../../actions/loadData";

export const isPaperURLValid = (d) => {
return (
Expand Down Expand Up @@ -56,7 +57,8 @@ const MIME = {
text: "text/plain;charset=utf-8;",
csv: 'text/csv;charset=utf-8;',
tsv: `text/tab-separated-values;charset=utf-8;`,
svg: "image/svg+xml;charset=utf-8"
svg: "image/svg+xml;charset=utf-8",
json: "application/json",
};

const treeToNexus = (tree, colorings, colorBy, temporal) => {
Expand Down Expand Up @@ -616,3 +618,33 @@ export const entropyTSV = (dispatch, filePrefix, entropy) => {
write(filename, MIME.tsv, createTsvString(objectsToWrite, headerFields));
dispatch(infoNotification({message: `Diversity data exported to ${filename}`}));
};


/**
* Write out Auspice JSON(s) for the current view. We do this by re-fetching the original
* JSON because we don't keep a copy of the unprocessed data around.
*
* Sidecar files are not fetched, but we can also download them if desired.
*
* Note that we are not viewing a narrative, as the download button functionality is disabled
* for narratives.
*/
export const auspiceJSON = (dispatch) => {
const filenames = [];
for (const datasetName of getDatasetNamesFromUrl(window.location.pathname)) {
if (!datasetName) continue; // e.g. no 2nd tree
const filename = datasetName.replace('/', '_') + '.json';
filenames.push(filename);
const dataset = new Dataset(datasetName);
dataset.fetchMain(); // initialises dataset.main (a promise)
dataset.main.then((jsonContents) => {
write(filename, MIME.json, JSON.stringify(jsonContents));
}).catch((err) => {
// I think this error path should be rarely (never!) encountered, because the fetch call has
// worked to load the dataset in the first place...
console.error(`Error fetching JSON for ${datasetName}: ${err}`);
dispatch(errorNotification({message: `Error preparing ${filename} JSON for download (see console for more info)`}));
});
}
dispatch(infoNotification({message: `Preparing Auspice JSON(s) for download: ${filenames.join(', ')}`}));
};
17 changes: 17 additions & 0 deletions src/components/framework/svg-icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,20 @@ export const PanelsFull = ({theme, selected, width}) => {
</svg>
);
};


export const Dataset = ({theme, selected, width}) => {
const stroke = selected ? theme.selectedColor : theme.unselectedColor;
return (
<svg width={width} height={width + 5}>
<g transform="translate(0,7)">
<svg width={width} height={width} viewBox="0 0 30 30 ">
<g id="Group" stroke="none" strokeWidth="3" fill="none" fillRule="evenodd">
<path stroke={stroke} d={`M 11 3 L 1 15 L 11 27`}/>
<path stroke={stroke} d={`M 19 3 L 29 15 L 19 27`}/>
</g>
</svg>
</g>
</svg>
);
};

0 comments on commit 6bec286

Please sign in to comment.