Skip to content

Commit

Permalink
Merge branch 'master' into nextstrain-export
Browse files Browse the repository at this point in the history
  • Loading branch information
theosanderson authored Jul 28, 2022
2 parents 6e63df1 + bd80976 commit 22d34f3
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 35 deletions.
1 change: 0 additions & 1 deletion .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@
"infra"
]
}

],
"contributorsPerLine": 7,
"projectName": "taxonium",
Expand Down
4 changes: 2 additions & 2 deletions taxonium_web_client/src/Taxonium.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ function Taxonium({
// This can happen with e.g. nextstrain json
if (data.base_data && data.base_data.nodes) {
const n = data.base_data.nodes[0];
if (!n.x_dist) {
if (!n.hasOwnProperty("x_dist")) {
setxType("x_time");
} else if (!n.x_time) {
} else if (!n.hasOwnProperty("x_time")) {
setxType("x_dist");
}
}
Expand Down
2 changes: 1 addition & 1 deletion taxonium_web_client/src/hooks/useInputHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export const useInputHelper = ({
);
const newQuery = {
treeUrl: tree_file.name,
ladderizeTree: tree_file.ladderize === "true",
ladderizeTree: tree_file.ladderize === true,
treeType: tree_file.filetype,
};
if (meta_file) {
Expand Down
11 changes: 2 additions & 9 deletions taxonium_web_client/src/utils/processNewick.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,13 @@ async function cleanup(tree) {

const scale_x = 450 / ref_x;

console.log(scale_y, "scale_y");
tree.node.forEach((node) => {
node.x_dist = node.x_dist * scale_x;
node.y = node.y * scale_y;
});
}

export async function processNewick(data, sendStatusMessage) {
console.log("got data", data);
let the_data;

the_data = await fetch_or_extract(data, sendStatusMessage, "tree");
Expand Down Expand Up @@ -139,11 +137,8 @@ export async function processNewick(data, sendStatusMessage) {
}
assignNumTips(tree.root);
const total_tips = tree.root.num_tips;
console.log("tree.root.num_tips", tree.root.num_tips);

if (data.ladderize) {
console.log("ladderizing");

sortWithNumTips(tree.root);
tree.node = kn_expand_node(tree.root);
}
Expand Down Expand Up @@ -186,7 +181,7 @@ export async function processNewick(data, sendStatusMessage) {
rootId: 0,
overwrite_config: { num_tips: total_tips },
};
console.log(JSON.stringify(output));

return output;
}

Expand All @@ -198,8 +193,6 @@ export async function processMetadataFile(data, sendStatusMessage) {

the_data = await fetch_or_extract(data, logStatusToConsole, "metadata");

console.log("Got metadata file");

const lines = the_data.split("\n");
const output = {};
let separator;
Expand Down Expand Up @@ -275,6 +268,6 @@ export async function processNewickAndMetadata(data, sendStatusMessage) {
Object.assign(node, blanks);
}
});
console.log(tree);

return tree;
}
153 changes: 134 additions & 19 deletions taxonium_web_client/src/utils/processNextstrain.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,85 @@ import { kn_expand_node, kn_calxy } from "./jstree";

const emptyList = [];

const nodeMutationsFromNextStrainToTaxonium = (
mutations,
unique_mutations,
mutation_lookup
) => {
//console.log("mutations", mutations);
const keys = Object.keys(mutations);
const nuc_muts = mutations["nuc"] ? mutations["nuc"] : [];

const genes = keys.filter((key) => key !== "nuc");
const taxonium_muts = [];
nuc_muts.forEach((nuc_mut) => {
// input format is like "C123T", we want to break this into old_residue, position and new_residue
// use regex to match the position
const position = nuc_mut.match(/\d+/g);
const index_of_position = nuc_mut.indexOf(position[0]);
const previous_residue = nuc_mut.substring(0, index_of_position);
const new_residue = nuc_mut.substring(
index_of_position + position[0].length
);
const tax_format = {
type: "nt",
gene: "nt",
previous_residue,
new_residue,
residue_pos: parseInt(position[0]),
};
const jsonned = JSON.stringify(tax_format);
//console.log("jsonned", jsonned);
if (mutation_lookup[jsonned]) {
taxonium_muts.push(mutation_lookup[jsonned]);
} else {
unique_mutations.push({
...tax_format,
mutation_id: unique_mutations.length,
});
const this_index = unique_mutations.length - 1;
mutation_lookup[jsonned] = this_index;
taxonium_muts.push(this_index);
}
});

genes.forEach((gene) => {
const gene_muts = mutations[gene];
gene_muts.forEach((gene_mut) => {
// input format is like "Q123F", we want to break this into old_residue, position and new_residue
// use regex to match the position
const position = gene_mut.match(/\d+/g);
const index_of_position = gene_mut.indexOf(position[0]);
const previous_residue = gene_mut.substring(0, index_of_position);
const new_residue = gene_mut.substring(
index_of_position + position[0].length
);
const tax_format = {
type: "aa",
gene,
previous_residue,
new_residue,
residue_pos: parseInt(position[0]),
};
const jsonned = JSON.stringify(tax_format);
//console.log("jsonned", jsonned);
if (mutation_lookup[jsonned]) {
taxonium_muts.push(mutation_lookup[jsonned]);
} else {
unique_mutations.push({
...tax_format,
mutation_id: unique_mutations.length,
});
const this_index = unique_mutations.length - 1;
mutation_lookup[jsonned] = this_index;
taxonium_muts.push(this_index);
}
});
});

return taxonium_muts;
};

async function do_fetch(url, sendStatusMessage, whatIsBeingDownloaded) {
if (!sendStatusMessage) {
sendStatusMessage = () => {};
Expand Down Expand Up @@ -81,7 +160,7 @@ async function cleanup(tree) {
const cleaned = {
name: node.name.replace(/'/g, ""),
parent_id: node.parent ? node.parent.node_id : node.node_id,
mutations: emptyList,
mutations: node.mutations ? node.mutations : emptyList,
y: node.y,
num_tips: node.num_tips,
is_tip: node.child.length === 0,
Expand Down Expand Up @@ -129,7 +208,7 @@ async function cleanup(tree) {
});
}

async function processJsTree(tree, data, sendStatusMessage) {
async function processJsTree(tree, data, config, sendStatusMessage) {
function assignNumTips(node) {
if (node.child.length === 0) {
node.num_tips = 1;
Expand Down Expand Up @@ -169,6 +248,7 @@ async function processJsTree(tree, data, sendStatusMessage) {
tree.node.forEach((node) => {
node.d = node.pre_x_dist;
});

kn_calxy(tree, true);
// kn_calxy sets x -> move x to x_dist
tree.node.forEach((node) => {
Expand Down Expand Up @@ -218,16 +298,22 @@ async function processJsTree(tree, data, sendStatusMessage) {
node_to_mut: {},
rootMutations: [],
rootId: 0,
overwrite_config: { num_tips: total_tips },
overwrite_config: { ...config, num_tips: total_tips },
};

return output;
}

function json_preorder(root) {
let n_tips = 0;
const parents = {};
parents[root.name] = null;
const path = [];
const stack = [root];

const unique_mutations = [];

const mutation_lookup = {};
while (stack.length > 0) {
const nodeJson = stack.pop();
let div = null;
Expand All @@ -238,14 +324,22 @@ function json_preorder(root) {
if (nodeJson.node_attrs.num_date) {
time = nodeJson.node_attrs.num_date.value;
}

//console.log(nodeJson);
// this is the node format for downstream processing
const parsedNode = {
name: nodeJson.name,
child: [],
meta: "",
hl: false,
hidden: false,
mutations:
nodeJson.branch_attrs && nodeJson.branch_attrs.mutations
? nodeMutationsFromNextStrainToTaxonium(
nodeJson.branch_attrs.mutations,
unique_mutations,
mutation_lookup
)
: [],
};

// assign distance
Expand Down Expand Up @@ -273,15 +367,22 @@ function json_preorder(root) {
parents[childJson.name] = parsedNode;
stack.push(childJson);
}
} else {
n_tips += 1;
}
}
return [path, parents];
return { path, parents, n_tips, unique_mutations };
}

async function json_to_tree(json) {
const rootJson = json.tree;
const [preorder, parents] = json_preorder(rootJson);
let n_tips = 0;
const {
path: preorder,
parents,
n_tips,
unique_mutations,
} = json_preorder(rootJson);

const nodes = [];
let root;
for (const node of preorder) {
Expand All @@ -303,32 +404,46 @@ async function json_to_tree(json) {
nodes.push(node);
}

return {
const jsTree = {
// tree in jstree.js format
node: nodes,
error: 0,
n_tips: n_tips,
root: root,
};
}

export async function processNextstrain(data, sendStatusMessage) {
console.log("got data", data);
let the_data;
const config = {};

console.log("META", json.meta);
config.title = json.meta.title;
console.log("META PROV", json.meta.data_provenance);
config.source =
json.meta.data_provenance.map((source) => source.name).join(" & ") +
" on " +
json.meta.updated +
" in a build maintained by " +
json.meta.maintainers.map((source) => source.name).join(" & ");
config.overlay = `<p>This is a tree generated from a <a href='//nextstrain.org'>NextStrain</a> JSON file, being visualised in Taxonium.</p>.`;
if (json.meta.build_url) {
config.overlay += `<p>The NextStrain build is available <a class='underline' href='${json.meta.build_url}'>here</a>.</p>`;
}

the_data = await fetch_or_extract(data, sendStatusMessage, "tree");
return { jsTree, config, unique_mutations };
}

console.log("the_data", the_data);
export async function processNextstrain(data, sendStatusMessage) {
const the_data = await fetch_or_extract(data, sendStatusMessage, "tree");

sendStatusMessage({
message: "Parsing NS file",
});

const input_string = the_data;

const jsTree = await json_to_tree(JSON.parse(input_string));
const { jsTree, config, unique_mutations } = await json_to_tree(
JSON.parse(the_data)
);

const output = await processJsTree(jsTree, data, sendStatusMessage);
const output = await processJsTree(jsTree, data, config, sendStatusMessage);
const node_to_mut = output.nodes.map((x) => x.mutations);

return output;
return { ...output, mutations: unique_mutations, node_to_mut: node_to_mut };
}
3 changes: 0 additions & 3 deletions taxonium_web_client/src/webworkers/localBackendWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,6 @@ onmessage = async (event) => {
//Process uploaded data:
console.log("Worker onmessage");
const { data } = event;
console.log(data, "data");
if (
data.type === "upload" &&
data.data &&
Expand All @@ -360,8 +359,6 @@ onmessage = async (event) => {
data.data.filename &&
data.data.filetype === "nextstrain"
) {
console.log("got nextstrain file", data.data);

processedUploadedData = await processNextstrain(
data.data,
sendStatusMessage
Expand Down

0 comments on commit 22d34f3

Please sign in to comment.