diff --git a/cli/develop.js b/cli/develop.js
index a60080019..a12766d86 100644
--- a/cli/develop.js
+++ b/cli/develop.js
@@ -10,6 +10,7 @@ const version = require('../src/version').version;
const chalk = require('chalk');
const generateWebpackConfig = require("../webpack.config.js").default;
const SUPPRESS = require('argparse').Const.SUPPRESS;
+const bodyParser = require('body-parser');
const addParser = (parser) => {
const description = `Launch auspice in development mode.
@@ -50,6 +51,11 @@ const run = (args) => {
process.env.BABEL_ENV = "development";
process.env.BABEL_EXTENSION_PATH = extensionPath;
+ // parse application/json
+ app.use(bodyParser.json({limit: '20mb'}));
+ // parse application/x-www-form-urlencoded
+ app.use(bodyParser.urlencoded({ extended: false }));
+
/* Redirects / to webpack-generated index */
app.use((req, res, next) => {
if (!/^\/__webpack_hmr|^\/charon|\.[A-Za-z0-9]{1,4}$/.test(req.path)) {
diff --git a/cli/server/handleGenomeDbs.js b/cli/server/handleGenomeDbs.js
new file mode 100644
index 000000000..b07ec6ef7
--- /dev/null
+++ b/cli/server/handleGenomeDbs.js
@@ -0,0 +1,157 @@
+const fs = require('fs');
+const path = require("path");
+const {PassThrough} = require('stream');
+const Engine = require('nedb');
+const fasta = require('bionode-fasta');
+
+const { promisify } = require('util');
+
+const readdir = promisify(fs.readdir);
+
+/*
+ All NeDB database files are stored in the subdirectory 'genomeDbs'
+ at the same level where fasta file and auspice file is located.
+*/
+const getDbPath = (fastaPath) => {
+ const dbRoot = path.join(path.dirname(fastaPath), 'genomeDbs');
+ const dbPath = path.join(dbRoot,
+ path.basename(fastaPath).replace(".fasta", ".db"));
+ return dbPath;
+};
+
+/*
+ @param: ids: an array of fasta sequence ids
+ @param: dbPath: resolvable path to NeDB database of genome sequences
+*/
+const fetchRecords = (ids, dbPath) =>
+ new Promise((resolve, reject) => {
+ console.log("dbPath: " + dbPath);
+ const db = new Engine({filename: dbPath, autoload: true});
+ if (db) {
+ console.log("db connected");
+ db.find({id: {$in: ids}}, (err, docs) => {
+ if (err) {
+ console.log('EE');
+ reject(err);
+ } else if (docs.length === 0) {
+ console.log("No record found!");
+ resolve(docs);
+ } else {
+ console.log("records: " + docs.length);
+ resolve(docs);
+ }
+ });
+ }
+ });
+
+
+/**
+ return response to a POST of fetching genome sequences by an array of ids
+ @param {string} datasetPath same as datasetDir when staring auspice
+*/
+const getGenomeDB = (datasetsPath) => {
+ return async (req, res) => { // eslint-disable-line consistent-return
+ try {
+ const prefix = req.body.prefix
+ .replace(/^\//, '')
+ .replace(/\/$/, '')
+ .split("/")
+ .join("_");
+ const dbPath = datasetsPath + '/genomeDbs/' + prefix + '.db';
+ if (!req.body.ids || req.body.ids.length === 0) {
+ res.setHeader('Content-Type', 'application/json');
+ if (fs.existsSync(dbPath)) {
+ res.end(JSON.stringify({result: true}));
+ } else {
+ res.end(JSON.stringify({result: false}));
+ }
+ return;
+ }
+ res.setHeader('Content-Type', 'text/plain');
+ const db = await fetchRecords(req.body.ids, dbPath);
+ db.forEach((v) => {
+ const wrappedSeq = v.seq.match(/.{1,80}/g).join('\n') + '\n';
+ res.write('>' + v.id + '\n');
+ res.write(wrappedSeq);
+ });
+ res.end();
+ } catch (err) {
+ console.trace(err);
+ }
+ };
+};
+
+/**
+ @param {string} dbRoot Path to directory where genome database should be saved
+ @param {string} fastaPath Path to fasta file to use as input to create database
+
+ Database will overwrite existing database files to avoid duplicates.
+ TODO: Maybe do something else to prevent unexpected data loss
+*/
+const makeDB = (dbRoot, fastaPath) => new Promise((resolve, reject) => {
+
+ process.stdin.setEncoding('utf8');
+
+ if (!fs.existsSync(dbRoot)) {
+ fs.mkdirSync(dbRoot);
+ }
+ const dbPath = getDbPath(fastaPath);
+
+ if (fs.existsSync(dbPath)) {
+ fs.unlink(dbPath, () => { console.log(`Overwrote ${dbPath} with new data!`);});
+ }
+
+ const processRecord = new PassThrough();
+ const db = new Engine({filename: dbPath, autoload: true});
+ let rc = 0;
+
+ processRecord.on('data', (rec) => {
+ const obj = JSON.parse(rec);
+ const outrec = {id: obj.id, seq: obj.seq, source: fastaPath};
+ db.insert(outrec);
+ rc++;
+ });
+
+ processRecord.on('end', () => {
+ console.log(`Total added: ${rc} seqs to ${dbPath}`);
+ if (fs.existsSync(dbPath)) {
+ resolve();
+ } else {
+ reject(`File: ${dbPath} was not created.`);
+ }
+ }
+ );
+ const rs = fs.createReadStream(fastaPath);
+ rs.pipe(fasta())
+ .pipe(processRecord);
+
+});
+
+/**
+ @param {string} path Path to datasetDir so we can create database if corresponding fasta
+ files exists for aupsice input JSON file
+*/
+const prepareDbs = async (localPath) => {
+ try {
+ const files = await readdir(localPath);
+ const v2Files = files.filter((file) => (
+ file.endsWith(".fasta")
+ ));
+ v2Files.forEach((v) => {
+ makeDB(localPath, localPath + '/' + v);
+ });
+
+
+ } catch (err) {
+ // utils.warn(`Couldn't collect available dataset files (path searched: ${locaPath})`);
+ // utils.verbose(err);
+ }
+};
+
+module.exports = {
+ fetchRecords,
+ getDbPath,
+ makeDB,
+ prepareDbs,
+ getGenomeDB
+};
diff --git a/cli/server/parseNarrative.js b/cli/server/parseNarrative.js
index a132e3201..c7843d185 100644
--- a/cli/server/parseNarrative.js
+++ b/cli/server/parseNarrative.js
@@ -60,7 +60,7 @@ const makeFrontMatterBlock = (frontMatter) => {
markdown.push(`#### License: ${license}`);
}
}
-
+
const block = new Proxy({}, blockProxyHandler);
block.url = frontMatter.dataset;
block.contents = markdown.join("\n");
diff --git a/cli/view.js b/cli/view.js
index 42d6efebb..ef5e55996 100644
--- a/cli/view.js
+++ b/cli/view.js
@@ -11,6 +11,7 @@ const utils = require("./utils");
const version = require('../src/version').version;
const chalk = require('chalk');
const SUPPRESS = require('argparse').Const.SUPPRESS;
+const bodyParser = require('body-parser');
const addParser = (parser) => {
@@ -58,12 +59,18 @@ const loadAndAddHandlers = ({app, handlersArg, datasetDir, narrativeDir}) => {
.setUpGetDatasetHandler({datasetsPath});
handlers.getNarrative = require("./server/getNarrative")
.setUpGetNarrativeHandler({narrativesPath});
+ handlers.handleGenomeDbs = require("./server/handleGenomeDbs")
+ .prepareDbs(datasetsPath); /* use sanitized datasetPath */
+ handlers.handleGenomeDbs = require("./server/handleGenomeDbs")
+ .getGenomeDB(datasetsPath); /* use sanitized datasetPath */
}
/* apply handlers */
app.get("/charon/getAvailable", handlers.getAvailable);
app.get("/charon/getDataset", handlers.getDataset);
app.get("/charon/getNarrative", handlers.getNarrative);
+ app.get("/charon/getGenomeData", handlers.handleGenomeDbs);
+ app.post("/charon/getGenomeData", handlers.handleGenomeDbs);
app.get("/charon*", (req, res) => {
res.statusMessage = "Query unhandled -- " + req.originalUrl;
utils.warn(res.statusMessage);
@@ -102,7 +109,13 @@ const run = (args) => {
const app = express();
app.set('port', process.env.PORT || 4000);
app.set('host', process.env.HOST || "localhost");
+ // parse application/json
+ app.use(bodyParser.json({limit: '20mb'}));
app.use(compression());
+ // parse application/x-www-form-urlencoded
+ app.use(bodyParser.urlencoded({ extended: false }));
+
+
app.use(nakedRedirect({reverse: true})); /* redirect www.name.org to name.org */
if (args.customBuild) {
diff --git a/docs-src/website/siteConfig.js b/docs-src/website/siteConfig.js
index 36e4fe4f0..04428abe5 100644
--- a/docs-src/website/siteConfig.js
+++ b/docs-src/website/siteConfig.js
@@ -17,7 +17,7 @@ const siteConfig = {
// Header links in the top nav bar
headerLinks: [
- {doc: 'introduction/overview', label: 'Docs'},
+ {doc: 'introduction/overview', label: 'Docs'}
// {doc: 'tutorial/overview', label: 'Tutorial'}
],
diff --git a/docs/js/scrollSpy.js b/docs/js/scrollSpy.js
index 0632e6c33..484a5d6f5 100755
--- a/docs/js/scrollSpy.js
+++ b/docs/js/scrollSpy.js
@@ -18,7 +18,7 @@
// throttle
return;
}
- timer = setTimeout(function() {
+ timer = setTimeout(function () {
timer = null;
let activeNavFound = false;
const headings = findHeadings(); // toc nav anchors
@@ -48,7 +48,7 @@
} else {
console.error('Can not find header element', {
id: next,
- heading,
+ heading
});
}
}
@@ -68,9 +68,9 @@
document.addEventListener('scroll', onScroll);
document.addEventListener('resize', onScroll);
- document.addEventListener('DOMContentLoaded', function() {
+ document.addEventListener('DOMContentLoaded', function () {
// Cache the headings once the page has fully loaded.
headingsCache = findHeadings();
onScroll();
});
-})();
+}());
diff --git a/package-lock.json b/package-lock.json
index bd385b149..7f35a5884 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "auspice",
- "version": "2.15.0",
+ "version": "2.16.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -3706,6 +3706,11 @@
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
"integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg=="
},
+ "async": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
+ "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E="
+ },
"async-each": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
@@ -4076,6 +4081,14 @@
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
"integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw=="
},
+ "binary-search-tree": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/binary-search-tree/-/binary-search-tree-0.2.5.tgz",
+ "integrity": "sha1-fbs7IQ/coIJFDa0jNMMErzm9x4Q=",
+ "requires": {
+ "underscore": "~1.4.4"
+ }
+ },
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
@@ -4090,6 +4103,40 @@
"resolved": "https://registry.npmjs.org/binomial/-/binomial-0.2.0.tgz",
"integrity": "sha1-zxhKU4nOOX8mtkxT98k0OZAhyPU="
},
+ "bionode-fasta": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/bionode-fasta/-/bionode-fasta-0.5.6.tgz",
+ "integrity": "sha1-L5jMtgQcWNL+O3sHb1Kdq5mVVB4=",
+ "requires": {
+ "concat-stream": "~1.6.0",
+ "fasta-parser": "0.1.0",
+ "minimist": "~1.2.0",
+ "pumpify": "~1.3.5",
+ "split2": "^2.1.1",
+ "through2": "~2.0.3"
+ },
+ "dependencies": {
+ "pump": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+ "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "pumpify": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.3.6.tgz",
+ "integrity": "sha512-BurGAcvezsINL5US9T9wGHHcLNrG6MCp//ECtxron3vcR+Rfx5Anqq7HbZXNJvFQli8FGVsWCAvywEJFV5Hx/Q==",
+ "requires": {
+ "duplexify": "^3.5.3",
+ "inherits": "^2.0.3",
+ "pump": "^2.0.0"
+ }
+ }
+ }
+ },
"bl": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
@@ -6770,6 +6817,57 @@
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
},
+ "fasta-parser": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/fasta-parser/-/fasta-parser-0.1.0.tgz",
+ "integrity": "sha1-rsgLPvL1DOz5wjradPZI07saRgs=",
+ "requires": {
+ "bl": "^0.9.0",
+ "pumpify": "^1.3.0",
+ "split": "^0.3.0",
+ "through2": "~0.6.0"
+ },
+ "dependencies": {
+ "bl": {
+ "version": "0.9.5",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz",
+ "integrity": "sha1-wGt5evCF6gC8Unr8jvzxHeIjIFQ=",
+ "requires": {
+ "readable-stream": "~1.0.26"
+ }
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+ },
+ "readable-stream": {
+ "version": "1.0.34",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+ },
+ "through2": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz",
+ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=",
+ "requires": {
+ "readable-stream": ">=1.0.33-1 <1.1.0-0",
+ "xtend": ">=4.0.0 <4.1.0-0"
+ }
+ }
+ }
+ },
"fb-watchman": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz",
@@ -8412,6 +8510,11 @@
"which-pm-runs": "^1.0.0"
}
},
+ "immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
+ },
"import-fresh": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz",
@@ -12394,6 +12497,14 @@
"type-check": "~0.3.2"
}
},
+ "lie": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
+ "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=",
+ "requires": {
+ "immediate": "~3.0.5"
+ }
+ },
"linspace": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/linspace/-/linspace-1.0.0.tgz",
@@ -12447,6 +12558,14 @@
}
}
},
+ "localforage": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.7.3.tgz",
+ "integrity": "sha512-1TulyYfc4udS7ECSBT2vwJksWbkwwTX8BzeUIiq8Y07Riy7bDAAnxDaPU/tWyOVmQAcWJIEIFP9lPfBGqVoPgQ==",
+ "requires": {
+ "lie": "3.1.1"
+ }
+ },
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
@@ -12939,6 +13058,18 @@
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc="
},
+ "nedb": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/nedb/-/nedb-1.8.0.tgz",
+ "integrity": "sha1-DjUCzYLABNU1WkPJ5VV3vXvZHYg=",
+ "requires": {
+ "async": "0.2.10",
+ "binary-search-tree": "0.2.5",
+ "localforage": "^1.3.0",
+ "mkdirp": "~0.5.1",
+ "underscore": "~1.4.4"
+ }
+ },
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
@@ -13530,9 +13661,9 @@
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
},
"papaparse": {
- "version": "4.6.3",
- "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-4.6.3.tgz",
- "integrity": "sha512-LRq7BrHC2kHPBYSD50aKuw/B/dGcg29omyJbKWY3KsYUZU69RKwaBHu13jGmCYBtOc4odsLCrFyk6imfyNubJQ=="
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.2.0.tgz",
+ "integrity": "sha512-ylq1wgUSnagU+MKQtNeVqrPhZuMYBvOSL00DHycFTCxownF95gpLAk1HiHdUW77N8yxRq1qHXLdlIPyBSG9NSA=="
},
"parallel-transform": {
"version": "1.2.0",
@@ -16353,7 +16484,6 @@
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
"integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=",
- "dev": true,
"requires": {
"through": "2"
}
@@ -16366,6 +16496,14 @@
"extend-shallow": "^3.0.0"
}
},
+ "split2": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz",
+ "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==",
+ "requires": {
+ "through2": "^2.0.2"
+ }
+ },
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@@ -17114,8 +17252,7 @@
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
- "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
- "dev": true
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"through2": {
"version": "2.0.5",
@@ -17327,6 +17464,11 @@
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.20.tgz",
"integrity": "sha512-8OaIKfzL5cpx8eCMAhhvTlft8GYF8b2eQr6JkCyVdrgjcytyOmPCXrqXFcUnhonRpLlh5yxEZVohm6mzaowUOw=="
},
+ "underscore": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz",
+ "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ="
+ },
"unicode-canonical-property-names-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
diff --git a/package.json b/package.json
index ff2edcf7a..047511df5 100644
--- a/package.json
+++ b/package.json
@@ -7,8 +7,8 @@
"repository": "github:nextstrain/auspice",
"homepage": "https://www.npmjs.com/package/auspice",
"engines": {
- "node": "10.8.x",
- "npm": "6.2.x"
+ "node": ">=10.8 <= 13.16",
+ "npm": ">=6.2.x"
},
"bin": {
"auspice": "./auspice.js"
@@ -54,6 +54,8 @@
"babel-plugin-strip-function-call": "^1.0.2",
"babel-plugin-styled-components": "^1.10.0",
"binomial": "^0.2.0",
+ "bionode-fasta": "^0.5.6",
+ "body-parser": "^1.19.0",
"chalk": "^2.4.1",
"clean-webpack-plugin": "^3.0.0",
"compression": "^1.7.3",
@@ -92,9 +94,10 @@
"lodash-webpack-plugin": "^0.11.5",
"marked": "^0.7.0",
"mousetrap": "^1.6.2",
+ "nedb": "^1.8.0",
"node-fetch": "^2.1.2",
"outer-product": "0.0.4",
- "papaparse": "^4.3.5",
+ "papaparse": "^5.2.0",
"prettyjson": "^1.2.1",
"prop-types": "^15.6.0",
"query-string": "^4.2.3",
diff --git a/src/components/download/downloadModal.js b/src/components/download/downloadModal.js
index 545fc76c0..87041e1bf 100644
--- a/src/components/download/downloadModal.js
+++ b/src/components/download/downloadModal.js
@@ -13,6 +13,8 @@ import { getAcknowledgments} from "../framework/footer";
import { createSummary, getNumSelectedTips } from "../info/info";
import { getFullAuthorInfoFromNode } from "../../util/treeMiscHelpers";
+import { getServerAddress } from "../../util/globals";
+
const RectangularTreeIcon = withTheme(icons.RectangularTree);
const PanelsGridIcon = withTheme(icons.PanelsGrid);
const MetaIcon = withTheme(icons.Meta);
@@ -61,7 +63,8 @@ export const publications = {
filters: state.controls.filters,
visibility: state.tree.visibility,
panelsToDisplay: state.controls.panelsToDisplay,
- panelLayout: state.controls.panelLayout
+ panelLayout: state.controls.panelLayout,
+ isGenomeAvailable: state.controls.isGenomeAvailable
}))
class DownloadModal extends React.Component {
constructor(props) {
@@ -109,6 +112,17 @@ class DownloadModal extends React.Component {
};
};
this.dismissModal = this.dismissModal.bind(this);
+ const path = `${getServerAddress()}/getGenomeData`;
+ this.state = {isGenomeAvailable: false};
+ fetch(path, {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ids: [], prefix: window.location.pathname})})
+ .then((res) => {
+ if (res.status !== 200) {
+ throw new Error(res.statusText);
+ }
+ res.json().then((json) => {
+ this.setState({isGenomeAvailable: json.result});
+ });
+ });
}
componentDidMount() {
Mousetrap.bind('d', () => {
@@ -140,9 +154,9 @@ class DownloadModal extends React.Component {
getFilePrefix() {
return "nextstrain_" +
window.location.pathname
- .replace(/^\//, '') // Remove leading slashes
- .replace(/:/g, '-') // Change ha:na to ha-na
- .replace(/\//g, '_'); // Replace slashes with spaces
+ .replace(/^\//, '') // Remove leading slashes
+ .replace(/:/g, '-') // Change ha:na to ha-na
+ .replace(/\//g, '_'); // Replace slashes with spaces
}
makeTextStringsForSVGExport() {
const x = [];
@@ -162,7 +176,7 @@ class DownloadModal extends React.Component {
}
getNumUniqueAuthors(nodes) {
const authors = nodes.map((n) => getFullAuthorInfoFromNode(n))
- .filter((a) => a && a.value);
+ .filter((a) => a && a.value);
const uniqueAuthors = new Set(authors.map((a) => a.value));
return uniqueAuthors.size;
}
@@ -188,6 +202,11 @@ class DownloadModal extends React.Component {
buttons.push(["Selected Metadata (TSV)", `Per-sample metadata for strains which are currently displayed (n = ${selectedTipsCount}/${this.props.metadata.mainTreeNumTips}).`,
(
- ({t("click outside this box to return to the app")}) + ({t("click outside this box to return to the app")})