From f73ef0bb5333461cdfbbdeda002d4c8211100b31 Mon Sep 17 00:00:00 2001 From: Theo Sanderson Date: Thu, 6 Jul 2023 22:46:04 +0100 Subject: [PATCH 1/2] Add support for nexus props --- node_modules/.yarn-integrity | 13 +++++ taxonium_component/src/hooks/useSearch.jsx | 1 - .../src/stories/Taxonium.stories.jsx | 19 +++++++ taxonium_component/src/utils/nexusToNewick.js | 26 +++++++-- taxonium_component/src/utils/processNewick.js | 55 ++++++++++++++++--- yarn.lock | 2 + 6 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 node_modules/.yarn-integrity diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity new file mode 100644 index 00000000..c4644651 --- /dev/null +++ b/node_modules/.yarn-integrity @@ -0,0 +1,13 @@ +{ + "systemParams": "darwin-arm64-108", + "modulesFolders": [], + "flags": [], + "linkedModules": [ + "my-project", + "taxonium-component" + ], + "topLevelPatterns": [], + "lockfileEntries": {}, + "files": [], + "artifacts": {} +} \ No newline at end of file diff --git a/taxonium_component/src/hooks/useSearch.jsx b/taxonium_component/src/hooks/useSearch.jsx index f5b5aa6e..c00a52d4 100644 --- a/taxonium_component/src/hooks/useSearch.jsx +++ b/taxonium_component/src/hooks/useSearch.jsx @@ -37,7 +37,6 @@ const useSearch = ({ const searchesEnabled = query.enabled ? JSON.parse(query.enabled) : JSON.parse(default_query.enabled); - console.log("searchesEnabled", searchesEnabled); const setEnabled = (key, enabled) => { console.log("setEnabled", key, enabled); diff --git a/taxonium_component/src/stories/Taxonium.stories.jsx b/taxonium_component/src/stories/Taxonium.stories.jsx index 3f422d7f..affc2b61 100644 --- a/taxonium_component/src/stories/Taxonium.stories.jsx +++ b/taxonium_component/src/stories/Taxonium.stories.jsx @@ -113,3 +113,22 @@ export const LocalDataWithMetadataNew = { layout: "padded", }, }; + + + +export const NexusTree = { + args: { + + + sourceData: { + status: "url_supplied", + filename: + "https://cov2tree.nyc3.cdn.digitaloceanspaces.com/nexus.tree", + filetype: "nexus", + }, + }, + parameters: { + // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout + layout: "padded", + }, +}; diff --git a/taxonium_component/src/utils/nexusToNewick.js b/taxonium_component/src/utils/nexusToNewick.js index 74219861..801f2663 100644 --- a/taxonium_component/src/utils/nexusToNewick.js +++ b/taxonium_component/src/utils/nexusToNewick.js @@ -1,4 +1,3 @@ -// get nexusString from tree.nexus function nexusToNewick(nexusString) { // get Translate section if present const translateBlock = nexusString.match(/Translate(.*?);/gims); @@ -27,7 +26,26 @@ function nexusToNewick(nexusString) { // get the Newick string from the tree block const newickString = treeBlock[0].match(/\((.*?)\).+;/gims)[0]; - //remove comments, which are indicated by [...] + let nodeProperties = {}; + + // extract properties, which are indicated by [&key=value] or [&key={value1,value2,...}] + newickString.replace( + /\[&?(.*?)\]/gims, + (match, contents, offset, inputString) => { + let nodeId = inputString.slice(0, offset).match(/[^,\(\):]+$/g)[0]; + // use a regular expression to split on commas not inside curly brackets + let properties = contents.split(/,(?![^{]*})/g); + let propertyDict = {}; + for (let prop of properties) { + let [key, value] = prop.split("="); + propertyDict["meta_"+key] = value; + } + nodeProperties[nodeId] = propertyDict; + + } + ); + + // remove comments, which are indicated by [...] const newick = newickString.replace(/\[(.*?)\]/gims, ""); @@ -35,12 +53,12 @@ function nexusToNewick(nexusString) { const translatedNewickString = newick.replace( /([^:\,\(\)]+)/gims, (match) => { - //console.log(translations[match]) return translations[match] || match; } ); + - return translatedNewickString; + return { newick: translatedNewickString, nodeProperties }; } export default nexusToNewick; diff --git a/taxonium_component/src/utils/processNewick.js b/taxonium_component/src/utils/processNewick.js index 531101ba..a37b9743 100644 --- a/taxonium_component/src/utils/processNewick.js +++ b/taxonium_component/src/utils/processNewick.js @@ -117,12 +117,15 @@ async function cleanup(tree) { export async function processNewick(data, sendStatusMessage) { let the_data; + let extra_metadata; the_data = await fetch_or_extract(data, sendStatusMessage, "tree"); console.log("data.filetype", data.filetype); if (data.filetype == "nexus") { - the_data = nexusToNewick(the_data); + const result = nexusToNewick(the_data); + the_data = result.newick; + extra_metadata = result.nodeProperties; } sendStatusMessage({ @@ -205,6 +208,7 @@ export async function processNewick(data, sendStatusMessage) { rootMutations: [], rootId: 0, overwrite_config: { num_tips: total_tips, from_newick: true }, + extra_metadata, }; return output; @@ -276,29 +280,66 @@ export async function processMetadataFile(data, sendStatusMessage) { export async function processNewickAndMetadata(data, sendStatusMessage) { const treePromise = processNewick(data, sendStatusMessage); - + let tree, metadata_double + let metadata = new Map(); + let headers = []; const metadataInput = data.metadata; if (!metadataInput) { - return await treePromise; + tree = await treePromise; } + else{ // Wait for both promises to resolve - const [tree, metadata_double] = await Promise.all([ + [tree, metadata_double] = await Promise.all([ treePromise, processMetadataFile(metadataInput, sendStatusMessage), ]); - const [metadata, headers] = metadata_double; + [metadata, headers] = metadata_double; + +} + + + const blanks = Object.fromEntries( headers.slice(1).map((x) => ["meta_" + x, ""]) ); + + if (tree.extra_metadata){ + // loop over the extra metadata dict to find all the (sub)keys + const all_extra_keys = new Set(); + Object.values(tree.extra_metadata).forEach((node_extra) => { + Object.keys(node_extra).forEach((key) => { + all_extra_keys.add(key); + }); + } + + + ); + // add any misssing keys to blanks + all_extra_keys.forEach((key) => { + if (!blanks[ key]) { + blanks[key] = ""; + } + }); + + } + + sendStatusMessage({ message: "Assigning metadata to nodes", }); tree.nodes.forEach((node) => { const this_metadata = metadata.get(node.name); + Object.assign(node, blanks); if (this_metadata) { Object.assign(node, this_metadata); - } else { - Object.assign(node, blanks); + } + if (tree.extra_metadata) { + + const node_extra = tree.extra_metadata[node.name]; + if (node_extra) { + Object.assign(node, node_extra); + + } } }); diff --git a/yarn.lock b/yarn.lock index 4a580188..fb57ccd1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,2 +1,4 @@ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 + + From 472aae269ccedd0c42ec6fe64e4f23066c51ded6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 6 Jul 2023 21:46:52 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- node_modules/.yarn-integrity | 2 +- .../src/stories/Taxonium.stories.jsx | 7 +--- taxonium_component/src/utils/nexusToNewick.js | 4 +- taxonium_component/src/utils/processNewick.js | 37 +++++++------------ yarn.lock | 2 - 5 files changed, 16 insertions(+), 36 deletions(-) diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity index c4644651..0c416750 100644 --- a/node_modules/.yarn-integrity +++ b/node_modules/.yarn-integrity @@ -10,4 +10,4 @@ "lockfileEntries": {}, "files": [], "artifacts": {} -} \ No newline at end of file +} diff --git a/taxonium_component/src/stories/Taxonium.stories.jsx b/taxonium_component/src/stories/Taxonium.stories.jsx index affc2b61..79a84311 100644 --- a/taxonium_component/src/stories/Taxonium.stories.jsx +++ b/taxonium_component/src/stories/Taxonium.stories.jsx @@ -114,16 +114,11 @@ export const LocalDataWithMetadataNew = { }, }; - - export const NexusTree = { args: { - - sourceData: { status: "url_supplied", - filename: - "https://cov2tree.nyc3.cdn.digitaloceanspaces.com/nexus.tree", + filename: "https://cov2tree.nyc3.cdn.digitaloceanspaces.com/nexus.tree", filetype: "nexus", }, }, diff --git a/taxonium_component/src/utils/nexusToNewick.js b/taxonium_component/src/utils/nexusToNewick.js index 801f2663..895af99c 100644 --- a/taxonium_component/src/utils/nexusToNewick.js +++ b/taxonium_component/src/utils/nexusToNewick.js @@ -38,10 +38,9 @@ function nexusToNewick(nexusString) { let propertyDict = {}; for (let prop of properties) { let [key, value] = prop.split("="); - propertyDict["meta_"+key] = value; + propertyDict["meta_" + key] = value; } nodeProperties[nodeId] = propertyDict; - } ); @@ -56,7 +55,6 @@ function nexusToNewick(nexusString) { return translations[match] || match; } ); - return { newick: translatedNewickString, nodeProperties }; } diff --git a/taxonium_component/src/utils/processNewick.js b/taxonium_component/src/utils/processNewick.js index a37b9743..614aee80 100644 --- a/taxonium_component/src/utils/processNewick.js +++ b/taxonium_component/src/utils/processNewick.js @@ -280,50 +280,41 @@ export async function processMetadataFile(data, sendStatusMessage) { export async function processNewickAndMetadata(data, sendStatusMessage) { const treePromise = processNewick(data, sendStatusMessage); - let tree, metadata_double + let tree, metadata_double; let metadata = new Map(); let headers = []; const metadataInput = data.metadata; if (!metadataInput) { - tree = await treePromise; + tree = await treePromise; + } else { + // Wait for both promises to resolve + [tree, metadata_double] = await Promise.all([ + treePromise, + processMetadataFile(metadataInput, sendStatusMessage), + ]); + [metadata, headers] = metadata_double; } - else{ - // Wait for both promises to resolve - [tree, metadata_double] = await Promise.all([ - treePromise, - processMetadataFile(metadataInput, sendStatusMessage), - ]); - [metadata, headers] = metadata_double; - -} - - const blanks = Object.fromEntries( headers.slice(1).map((x) => ["meta_" + x, ""]) ); - - if (tree.extra_metadata){ + + if (tree.extra_metadata) { // loop over the extra metadata dict to find all the (sub)keys const all_extra_keys = new Set(); Object.values(tree.extra_metadata).forEach((node_extra) => { Object.keys(node_extra).forEach((key) => { all_extra_keys.add(key); }); - } - - - ); + }); // add any misssing keys to blanks all_extra_keys.forEach((key) => { - if (!blanks[ key]) { + if (!blanks[key]) { blanks[key] = ""; } }); - } - sendStatusMessage({ message: "Assigning metadata to nodes", }); @@ -334,11 +325,9 @@ export async function processNewickAndMetadata(data, sendStatusMessage) { Object.assign(node, this_metadata); } if (tree.extra_metadata) { - const node_extra = tree.extra_metadata[node.name]; if (node_extra) { Object.assign(node, node_extra); - } } }); diff --git a/yarn.lock b/yarn.lock index fb57ccd1..4a580188 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,4 +1,2 @@ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 - -