Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimenting with refactoring tb #345

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion taxonium_web_client/src/components/SearchTopLayerItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Modal from "react-modal";

function SearchTopLayerItem({ singleSearchSpec, myKey, search, config }) {
const myLoadingStatus = search.searchLoadingStatus[myKey];
console.log("myLoadingStatus", myLoadingStatus);

const [permaLinkModalOpen, setPermaLinkModalOpen] = useState(false);
const this_result = search.searchResults[myKey];

Expand Down
354 changes: 193 additions & 161 deletions taxonium_web_client/src/hooks/useTreenomeLayerData.js
Original file line number Diff line number Diff line change
@@ -1,181 +1,213 @@
import { useCallback, useMemo, useEffect, useState } from "react";

const useTreenomeLayerData = (
data,
treenomeState,
settings,
selectedDetails
) => {
const [varDataAa, setVarDataAa] = useState([]);
const [varDataNt, setVarDataNt] = useState([]);
const [numNodes, setNumNodes] = useState(0);
const [cachedVarDataAa, setCachedVarDataAa] = useState([]);
const [cachedVarDataNt, setCachedVarDataNt] = useState([]);
const [reference, setReference] = useState(null);
const [didFirstAa, setDidFirstAa] = useState(false);
const [didFirstNt, setDidFirstNt] = useState(false);

const [currentJobId, setCurrentJobId] = useState(null);
const worker = useMemo(
() =>
new Worker(new URL("../webworkers/treenomeWorker.js", import.meta.url)),
[]
);

worker.onmessage = useCallback(
(e) => {
if (!reference && e.data.reference) {
setReference(e.data.reference);
}
const getNtPos =
(mut) => {
if (mut.gene === "nt") {
return mut.residue_pos - 1;
}
if (mut.nuc_for_codon !== undefined) {
return mut.nuc_for_codon - 1;
}

};

if (e.data.type === "variation_data_return_cache_aa") {
setCachedVarDataAa(e.data.filteredVarData);
setVarDataAa(e.data.filteredVarData);
} else if (e.data.type === "variation_data_return_aa") {
setVarDataAa(e.data.filteredVarData);
} else if (e.data.type === "variation_data_return_cache_nt") {
setCachedVarDataNt(e.data.filteredVarData);
setVarDataNt(e.data.filteredVarData);
} else if (e.data.type === "variation_data_return_nt") {
setVarDataNt(e.data.filteredVarData);
}
},
[
reference,
setReference,
setVarDataAa,
setVarDataNt,
setCachedVarDataAa,
setCachedVarDataNt,
]
);

useEffect(() => {
if (!(data.data && data.data.nodes)) {
return;
const make_to_children = (data) => {
if (!data || !data.nodes) {
return [{}, null];
}

const nodes = data.nodes;
let to_children = {};
let root_id = null;
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].parent_id === nodes[i].node_id) {
root_id = nodes[i].node_id;
continue;
}
if (to_children[nodes[i].parent_id] !== undefined) {
to_children[nodes[i].parent_id].push(nodes[i].node_id);
} else {
to_children[nodes[i].parent_id] = [nodes[i].node_id];
}
}

return [to_children, root_id];
};

if (
!didFirstAa &&
data.data &&
data.data.nodes &&
treenomeState.genomeSize > 0 &&
treenomeState.ntBounds[0] === 0 &&
treenomeState.ntBounds[1] === treenomeState.genomeSize
) {
if (settings.mutationTypesEnabled.aa) {
const jobId = data.data.nodes.length;
worker.postMessage({
type: "variation_data_aa",
data: data,
jobId: jobId,
ntBounds: treenomeState.ntBounds,
});
const postorder_traversal = ( root_id,to_children, data) => {
if (!data || !data.nodes) {
return [];
}

let result = [];
let stack = [root_id];
while (stack.length > 0) {
let node_id = stack.pop();
if (to_children[node_id] !== undefined) {
for (let i = to_children[node_id].length - 1; i >= 0; i--) {
stack.push(to_children[node_id][i]);
}
setDidFirstAa(true);
}
if (
!didFirstNt &&
data.data &&
data.data.nodes &&
treenomeState.genomeSize > 0 &&
treenomeState.ntBounds[0] === 0 &&
treenomeState.ntBounds[1] === treenomeState.genomeSize
) {
if (settings.mutationTypesEnabled.nt) {
const jobId = data.data.nodes.length;
worker.postMessage({
type: "variation_data_nt",
data: data,
jobId: jobId,
ntBounds: treenomeState.ntBounds,
});
result.push(node_id);
}
console.log("result", result, "root_id", root_id);
result.reverse();
return result;
}

const min_and_max_y = (postorder, to_children, data) =>{
const min_y = {}
const max_y = {}
postorder.forEach(node_id => {

const node = data.nodeLookup[node_id];
min_y[node_id] = node.y;
max_y[node_id] = node.y;
const children = to_children[node_id];
if (children) {
children.forEach(child_id => {
min_y[node_id] = Math.min(min_y[node_id], min_y[child_id]);
max_y[node_id] = Math.max(max_y[node_id], max_y[child_id]);
}
setDidFirstNt(true);
}
if (!settings.treenomeEnabled) {
return;
)
}
}
)
return [min_y, max_y];
}

if (data.data.nodes.length >= 90000) {
if (cachedVarDataAa.length > 0 && cachedVarDataAa !== varDataAa) {
setVarDataAa(cachedVarDataAa);
}
if (cachedVarDataNt.length > 0) {
setVarDataNt(cachedVarDataNt);
}
if (settings.mutationTypesEnabled.aa && cachedVarDataAa.length > 0) {
if (cachedVarDataNt.length > 0 || !settings.mutationTypesEnabled.nt) {
setNumNodes(data.data.nodes.length);
return;
}
}
if (settings.mutationTypesEnabled.nt && cachedVarDataNt.length > 0) {
if (cachedVarDataAa.length > 0 || !settings.mutationTypesEnabled.aa) {
setNumNodes(data.data.nodes.length);
return;



const nodes_satisfying_function = (starting_node_id, data, to_children, matching_function, minY, maxY, minYdiff) => {

let satisfying_nodes = [];
let next_nodes = []
const my_children = to_children[starting_node_id];
if(my_children){
next_nodes = [...my_children];
}
while (next_nodes.length > 0) {
let next_node_id = next_nodes.pop();
if (matching_function(data.nodeLookup[next_node_id])) {
satisfying_nodes.push(next_node_id);
}
else{ // N.B. the else, we do not descend into children of nodes that do satisfy.
const children = to_children[next_node_id];
if(children){
children.forEach(child => {
if (maxY[child] - minY[child] > minYdiff) {
// console.log("maxY[child]", maxY[child], "minY[child]", minY[child], minYdiff);
next_nodes.push(child);
}
}
);
}
let skipAa = false;
let skipNt = false;
if (numNodes === data.data.nodes.length) {
// only ntBounds changed, need to recompute only if < 1000 nts are visible
if (!data.data || !data.data.nodes) {
return;
}
if (settings.mutationTypesEnabled.aa && varDataAa.length > 0) {
setVarDataAa(varDataAa);
skipAa = true;
}
if (settings.mutationTypesEnabled.nt && varDataNt.length > 0) {
setVarDataNt(varDataNt);
skipNt = true;
}
}
}
return satisfying_nodes;

}

const node_mutation_to_lines = (node,mutation, to_children, data,minY,maxY, minYdiff) => {

const has_mutation_in_same_place = (node) =>
node.mutations.some( m=> m.residue_pos === mutation.residue_pos && m.gene === mutation.gene);
const nodes_with_mutations_away = nodes_satisfying_function(node.node_id, data, to_children, has_mutation_in_same_place, minY, maxY, minYdiff);
//console.log("nodes_with_mutations_away", nodes_with_mutations_away);
const ranges_negative = nodes_with_mutations_away.map(node_id => [minY[node_id], maxY[node_id]]);
ranges_negative.sort((a,b) => a[0] - b[0]);
const total_range = [minY[node.node_id], maxY[node.node_id]]
let output_ranges
if (ranges_negative.length === 0) {
output_ranges = [total_range];
}
else {
// create a series of ranges which are the inverse of the ranges_negative
output_ranges = [total_range];
for (let i = 0; i < ranges_negative.length; i++) {
const range = ranges_negative[i];
const new_range = [range[1], output_ranges[output_ranges.length - 1][0]];
output_ranges.push(new_range);
output_ranges[output_ranges.length - 2][1] = range[0];
}
// full computation
setNumNodes(data.data.nodes.length);
let jobId = data.data.nodes.length;
if (!skipAa) {
if (settings.mutationTypesEnabled.aa) {
worker.postMessage({
type: "variation_data_aa",
data: data,
jobId: jobId,
ntBounds: treenomeState.ntBounds,
});
}
const to_output = output_ranges.map(range => ({x: getNtPos(mutation), y:range, m:mutation}))
//console.log("to_output", to_output);
return to_output;



}
;

const getAllLines = (data, to_children, root_id, minY, maxY, mutation_checker, minYDiff) => {
console.log("getAllLines start");
if(!data || !data.nodes){
return [];
}
let lines = [];
let next_nodes = to_children[root_id];
while (next_nodes.length > 0) {
let next_node_id = next_nodes.pop();
//console.log("next_node_id", next_node_id);
//console.log("data",data)
let node = data.nodeLookup[next_node_id];

node.mutations.forEach(mutation => {
//console.log("mutation", mutation);
if (mutation_checker(mutation)) {
const result = node_mutation_to_lines(node,mutation, to_children, data,minY, maxY, minYDiff);
result.forEach(line => {
lines.push(line);
}
);
}
if (!skipNt) {
if (settings.mutationTypesEnabled.nt) {
worker.postMessage({
type: "variation_data_nt",
data: data,
jobId: jobId,
ntBounds: treenomeState.ntBounds,
});
}
);

const children = to_children[next_node_id]
if(children){
children.forEach(child => {
if (maxY[child] - minY[child] > minYDiff) {
next_nodes.push(child);
}
}
);
}
}, [
data.data,
numNodes,
settings.treenomeEnabled,
varDataAa,
varDataNt,
worker,
settings.mutationTypesEnabled,
treenomeState.ntBounds,
currentJobId,
setCurrentJobId,
cachedVarDataAa,
cachedVarDataNt,
data,
didFirstAa,
treenomeState.genomeSize,
didFirstNt,
]);

return [varDataAa, varDataNt, reference, cachedVarDataAa, cachedVarDataNt];
};

}
console.log("getAllLines end");
console.log("lines", lines);

return lines
}




const useTreenomeLayerData = (
my_data,
treenomeState,
settings,
selectedDetails,
mutationChecker,
minYDiff

) => {




const [to_children, root_id] = useMemo(() => make_to_children(my_data), [my_data]);
const postorder = useMemo(() => postorder_traversal(root_id, to_children, my_data), [root_id, to_children, my_data]);
const [minY, maxY] = useMemo(() => min_and_max_y(postorder, to_children, my_data), [postorder, to_children, my_data]);

const allLines = useMemo(() => getAllLines(my_data, to_children, root_id, minY, maxY,mutationChecker, minYDiff), [my_data, to_children, root_id,minY,maxY,mutationChecker, minYDiff]);

return {allLines}
}



export default useTreenomeLayerData;
Loading