UNPKG

cttv.api

Version:

Javascript interface for the Open Targets rest api

283 lines (251 loc) 7.68 kB
// Given a flat structure of associations // convert and return that structure to a tree var rootId = "cttv_root"; var childrenProperty = "children"; var flat2tree = function (config, taslist) { // cProp -- children property var data; if (Array.isArray(config)) { data = config; } else { data = config; var cProp = config.childrenProperty; if (cProp) { childrenProperty = cProp; } } var associations = data.data; var therapeuticAreas = data.therapeutic_areas; // Unfold the associations with more than 1 path var unfoldedAssociations = unfoldAssoc(associations); // Sort them by min path length var sortedAssociations = unfoldedAssociations.sort(sortByPathLength); var tree = { "__id": rootId, "__key": rootId, "name" : "cttv_disease", "disease": { "id": rootId, "efo_info": { "label": "" } } }; var tas = getTAs(therapeuticAreas, taslist); for (var i=0; i<sortedAssociations.length; i++) { addNode(sortedAssociations[i], tree, tas); } return tree; }; function unfoldAssoc (arr) { var unfold = []; for (var i=0; i<arr.length; i++) { var node = arr[i]; if (node.disease.efo_info.path.length > 1) { for (var j=1; j<node.disease.efo_info.path.length; j++) { var clone = cloneNode(node); clone.currPath = node.disease.efo_info.path[j]; unfold.push(clone); } } node.currPath = node.disease.efo_info.path[0]; unfold.push(node); } return unfold; } function addNode (node, tree, tas) { // nodes don't have a name, we put one there node.__id = node.disease.id; node.__name = node.disease.efo_info.label; node.name = node.disease.efo_info.label; node.label = node.disease.efo_info.label; node.__association_score = node.association_score.overall; node.__evidence_count = node.evidence_count.total; var path = node.currPath; var parent = findParent(path, tree, node); delete(node.currPath); // If the parent is cttv_root and the node is not a therapeutic area (path.length > 2) we search for the TA in the set of TAs if ((parent.__id === rootId) && (path.length > 1)) { // var taId = node.target.id + "-" + path[0]; var taId = 'TA-' + path[0]; var ta = tas[taId]; if (!ta) { console.error("We do not have TA: " + taId); } ta.__id = path[0]; ta.__name = ta.disease.efo_info.label; ta.name = ta.disease.efo_info.label; ta.label = ta.disease.efo_info.label; ta.__association_score = ta.association_score.overall; ta.__evidence_count = ta.evidence_count.total; if (!tree[childrenProperty]) { tree[childrenProperty] = []; } tree[childrenProperty].push(ta); parent = ta; } if (!parent[childrenProperty]) { parent[childrenProperty] = []; } // Only push the child if it is not there if (!hasTwin(parent[childrenProperty], node)) { parent[childrenProperty].push(node); } } function hasTwin (siblings, himself) { for (var i=0; i<siblings.length; i++) { if (siblings[i].__id === himself.__id) { return true; } } return false; } /** * Merge list of therapeutic areas from the associations and the full static list of TAs into a map * @param {*} arr array of therapeutic areas for these associations * @param {*} therapeuticareas the static full list of therapeautic areas as returned by the API */ function getTAs (arr, therapeuticareas) { arr = arr || []; // avoid 'undefined'-related errors as per some associations var tas = {}; for (var i=0; i<therapeuticareas.length; i++) { var ta = therapeuticareas[i]; // since this is the static list of therapeutic areas, it has only label and code // so we initialize other params as needed // NOTE: the 'TA-' is used to avoid possible errors/conflicts caused by tas names // e.g. on therapeutic area is called 'Function' (to be precise object.Function works // but object.function doesn't as it's a javascript reserved word) tas['TA-'+ta.code] = { id: ta.code, disease: { efo_info: { label: ta.label }, id: ta.code, }, association_score: { overall: 0, datatypes: {} }, target: { gene_info: {}, id: undefined }, evidence_count : {} }; } // update and override with association-specific TAs for (var i=0; i<arr.length; i++) { var ta = arr[i]; tas['TA-'+ta.disease.id] = ta; } return tas; } function findParent (path, tree, myself) { var children = tree[childrenProperty] || []; for (var i=0; i<path.length; i++) { var found = false; FINDINCHILDREN: for (var j=0; j<children.length; j++) { var child = children[j]; if ((child.__id === path[i]) && (child.__id !== myself.__id)) { tree = child; children = child[childrenProperty] || []; found = true; break FINDINCHILDREN; } } if (!found) { return tree; } } return tree; } function sortByPathLength (a, b) { return a.currPath.length - b.currPath.length; } // Fast clone methods function cloneNode (n) { var c = { __association_score: n.__association_score, __evidence_count: n.__evidence_count, id: n.id, __id: n.__id, __name: n.__name, target: { id: n.target.id, name: n.target.gene_info.name, symbol: n.target.gene_info.symbol }, disease: cloneDisease(n.disease), association_score: cloneAssociationScore(n.association_score), evidence_count: cloneEvidenceCount(n.evidence_count) }; if (n.is_direct) { c.is_direct = n.is_direct; } return c; } function cloneDisease (disease) { var d = { id: disease.id, efo_info: cloneEfoInfo(disease.efo_info) }; return d; } function cloneEfoInfo (efo) { var e = { label: efo.label, path: clonePath(efo.path), therapeutic_area: cloneTherapeuticArea(efo.therapeutic_area) }; return e; } function cloneTherapeuticArea (tas) { var ta = { codes: cloneArr(tas.codes), labels: cloneArr(tas.labels) }; return ta; } function clonePath (p) { var c = []; for (var i=0; i<p.length; i++) { c.push(p[i]); } return c; } function cloneEvidenceCount (ect) { var ec = { total: ect.total, datatype: cloneObj(ect.datatype), datasource: cloneObj(ect.datasource) }; return ec; } function cloneAssociationScore (asr) { var as = { overall: asr.overall, datatypes: cloneObj(asr.datatypes), datasources: cloneObj(asr.datasources) }; return as; } function cloneArr (arr) { var a = []; for (var i=0; i<arr.length; i++) { a.push(arr[i]); } return a; } function cloneObj (obj) { var o = {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { o[key] = obj[key]; } } return o; } module.exports = exports = { flat2tree: flat2tree };