-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathexporting.js
128 lines (120 loc) · 3.77 KB
/
exporting.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// can be called by local or server backend
export const getNextstrainSubtreeJson = async (
subtree_root_id,
nodes,
config
) => {
const subtree_root = nodes.find((node) => node.node_id === subtree_root_id);
const childMap = {};
const lookup = {};
// get child pointers
for (const node of nodes) {
lookup[node.node_id] = node;
if (node.parent_id !== null) {
if (childMap[node.parent_id] === undefined) {
childMap[node.parent_id] = [];
}
childMap[node.parent_id].push(node.node_id);
}
}
let muts = subtree_root.mutations.map((m) => config.mutations[m]);
const nucMuts = muts.filter((m) => m.type === "nt");
const aaMuts = muts.filter((m) => m.type === "aa");
const treeJson = {
name: subtree_root.name,
node_id: subtree_root.node_id,
node_attrs: { div: 0 },
branch_attrs: {
mutations: {
nuc: nucMuts.map(
(m) => `${m.previous_residue}${m.residue_pos}${m.new_residue}`
),
},
},
};
const alreadyIn = [];
aaMuts.forEach((m) => {
if (alreadyIn.includes(m.gene)) {
treeJson.branch_attrs.mutations[m.gene].push(
`${m.previous_residue}${m.residue_pos}${m.new_residue}`
);
} else {
treeJson.branch_attrs.mutations[m.gene] = [
`${m.previous_residue}${m.residue_pos}${m.new_residue}`,
];
alreadyIn.push(m.gene);
}
});
Object.keys(subtree_root)
.filter((v) => v.startsWith("meta_"))
.map((v) => ({ [v.slice(5)]: { value: subtree_root[v] } }))
.forEach((v) => {
Object.assign(treeJson.node_attrs, v);
});
const stack = [treeJson];
const metadataSet = new Set();
while (stack.length > 0) {
const currNodeJson = stack.pop();
const currNodeDiv = currNodeJson.node_attrs.div;
const children = childMap[currNodeJson.node_id];
const childrenJson = [];
if (children !== undefined) {
for (const child_id of children) {
const child_node = lookup[child_id];
let muts = child_node.mutations.map((m) => config.mutations[m]);
const nucMuts = muts.filter((m) => m.type === "nt");
const aaMuts = muts.filter((m) => m.type === "aa");
console.log(nucMuts, aaMuts);
const nucMutsNoAmb = nucMuts.filter(
(m) => m.new_residue != "-" && m.previous_residue != "-"
);
// TODO: Above discards ambiguities from distance calculation.
// Do we want to do this? In mpx e.g. there are nodes with
// many thousands of ambiguous mutations which throws off display
const childJson = {
name: child_node.name,
node_id: child_node.node_id,
node_attrs: {
div: currNodeDiv + nucMutsNoAmb.length,
},
branch_attrs: {},
};
// TODO add div key for genetic distance
Object.keys(child_node)
.filter((v) => v.startsWith("meta_"))
.map((v) => ({ [v.slice(5)]: { value: child_node[v] } }))
.forEach((v) => {
metadataSet.add(Object.keys(v)[0]);
Object.assign(childJson.node_attrs, v);
});
childrenJson.push(childJson);
stack.push(childJson);
}
}
if (childrenJson.length > 0) {
currNodeJson.children = childrenJson;
}
}
let json = {
meta: {
description: "JSON exported from Taxonium.",
panels: ["tree"],
title: config.title,
description: "Source: " + config.source,
display_defaults: {
distance_measure: "div",
color_by: metadataSet[0],
},
colorings: Array.from(metadataSet).map((v) => ({
key: v,
title: v,
type: "categorical",
})),
},
tree: treeJson,
version: "v2",
};
console.log("Nextstrain json: ", json);
return json;
};
export default { getNextstrainSubtreeJson };