Skip to content

Commit

Permalink
Merge pull request #497 from theosanderson/nexus_props
Browse files Browse the repository at this point in the history
Add support for nexus props
  • Loading branch information
theosanderson authored Jul 6, 2023
2 parents f971e47 + 472aae2 commit 9ef8cf1
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 16 deletions.
13 changes: 13 additions & 0 deletions node_modules/.yarn-integrity

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

1 change: 0 additions & 1 deletion taxonium_component/src/hooks/useSearch.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
14 changes: 14 additions & 0 deletions taxonium_component/src/stories/Taxonium.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,17 @@ 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",
},
};
24 changes: 20 additions & 4 deletions taxonium_component/src/utils/nexusToNewick.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// get nexusString from tree.nexus
function nexusToNewick(nexusString) {
// get Translate section if present
const translateBlock = nexusString.match(/Translate(.*?);/gims);
Expand Down Expand Up @@ -27,20 +26,37 @@ 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, "");

// translate the taxon labels in the Newick string
const translatedNewickString = newick.replace(
/([^:\,\(\)]+)/gims,
(match) => {
//console.log(translations[match])
return translations[match] || match;
}
);

return translatedNewickString;
return { newick: translatedNewickString, nodeProperties };
}

export default nexusToNewick;
52 changes: 41 additions & 11 deletions taxonium_component/src/utils/processNewick.js
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -276,29 +280,55 @@ 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
[tree, metadata_double] = await Promise.all([
treePromise,
processMetadataFile(metadataInput, sendStatusMessage),
]);
[metadata, headers] = metadata_double;
}
// Wait for both promises to resolve
const [tree, metadata_double] = await Promise.all([
treePromise,
processMetadataFile(metadataInput, sendStatusMessage),
]);
const [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);
}
}
});

Expand Down

1 comment on commit 9ef8cf1

@vercel
Copy link

@vercel vercel bot commented on 9ef8cf1 Jul 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.