Source code for pyham.iham

from string import Template
import logging
import json
import datetime
import lxml.etree as etree

logger = logging.getLogger(__name__)

[docs] class IHAM(object): def __init__(self, newick_str, hog): self.hog = hog self.newick_str = newick_str self.html_template = self._get_html_template() self.famdata = json.dumps(self._get_famdata()) self.orthoxml = OrthoXML_manager(self.hog) try: numeric_hogid = int(self.hog.hog_id) hogid = "HOG:{:07d}".format(numeric_hogid) except ValueError: hogid = self.hog.hog_id self.HTML = self.html_template.safe_substitute({'name': hog.hog_id, 'hog_id': hogid, "tree": self.newick_str, "orthoxml": self.orthoxml.get_orthoxml_str(), "fam_data": self.famdata }) def _get_html_template(self): x =Template('''<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>$hog_id</title> <script src="https://d3js.org/d3.v3.js"></script> <!-- Bootstrap CDN --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous"> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js" integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js" integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm" crossorigin="anonymous"></script> <!-- Roboto font --> <link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet"> <script src="https://cdn.jsdelivr.net/gh/DessimozLab/iHam@master/build/iHam.js"></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/DessimozLab/iHam@master/build/iHam.css" type="text/css"/> <style> body { font-family: 'Roboto', sans-serif; } #updating { position: absolute; left: 20px; display: none; } .alert_remove { margin-bottom: 0px; padding: 4px; display: none; } .alert-link { cursor: pointer; } #header { margin-left: 20px; margin-bottom: 20px; } #header > h3 { margin-top: 20px; margin-bottom: 20px; } #menu-bar > div { display: inline-block; } .dropdown-toggle { padding-top: 7px; padding-bottom: 7px; } </style> </head> <body> <div id="header"> <h3>Hierarchical group $hog_id open at level of <span id="current-node"></span></h3> <div id="menu-bar"> <div id="gene-tooltips-dropdown" class="dropdown"> <button class="btn btn-sm btn-outline-dark dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> Show gene tooltips on </button> <div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> <a class="dropdown-item active" href="#">Click</a> <a class="dropdown-item" href="#">Mouseover</a> </div> </div> <div id="percentage-coverage-selector"> <button class="btn btn-sm btn-outline-dark" type="button" aria-haspopup="true" aria-expanded="false">Remove columns under <input id="set_min_coverage" type="number" step="10" value="0" min="0" max="100">% of species coverage </button> </div> </div> </div> <div id="updating"> Updating... </div> <div class="alert alert-info text-center alert_remove" role="alert" > Lowly supported hogs have been removed as per settings. <a class="alert-link" id="reset_column_filter">Click here to reset.</a> </div> <div style="width: 1500px; min-width: 500px;" id="iham"></div> <script> (function (div) { const data = { "tree": '$tree', "orthoxml": `$orthoxml`, "fam_data": $fam_data } var theme = iHam() .on('node_selected', function (node) { d3.select('#current-node') .text(node.node_name()); }) .orthoxml(data.orthoxml) .show_oma_link(false) .newick(data.tree) .fam_data(data.fam_data) .tree_width(330) .board_width(530) .query_gene({id: 3965}) .on("updating", function () { d3.select("#updating") .style("display", 'block'); }) .on("updated", function () { d3.select("#updating") .style("display", "none"); }) .on("hogs_removed", function (what) { if (what.length) { d3.select(".alert_remove") .style("display", "block") } else { d3.select(".alert_remove") .style("display", "none"); } }); theme(div); // Update the color schemas // Update event for gene tooltips d3.select("#gene-tooltips-dropdown") .selectAll("a") .on("click", function () { // Manage state of menu itself d3.select(this.parentNode).selectAll("a").classed("active", false); d3.select(this) .classed("active", true); if (d3.select(this).text() === "Click") { theme.gene_tooltips_on("click"); } if (d3.select(this).text() === "Mouseover") { theme.gene_tooltips_on("mouseover"); } }); // Set minimum species coverage d3.select("#percentage-coverage-selector").select("input") .on("input", function () { theme.coverage_threshold(d3.select(this).property("value")); }); // Reset the coverage d3.select("#reset_column_filter") .on("click", function () { d3.select("#percentage-coverage-selector").select("input").property("value", 0); theme.coverage_threshold(0); }) })(document.getElementById('iham')); </script> </body> </html>''') return x def _get_famdata(self): fd = [] for gene in self.hog.get_all_descendant_genes(): data = {} data["taxon"] = {"species": gene.genome.name} data["xrefid"] = gene.prot_id data["sequence_length"] = 0 data["gc_content"] = 0 data["protid"] = gene.prot_id data["id"] = gene.unique_id fd.append(data) return fd
class OrthoXML_manager(object): def __init__(self, hog): self.hog = hog origin = "OMA" vers = datetime.date.today().strftime("%b %Y") origin += " standalone (bottom-up)" self.xml = self._create_xml(origin, vers) self._add_species_data() self._add_groups() def _create_xml(self, origin, originVersion): xml_core = etree.Element("orthoXML") xml_core.set("origin", origin) xml_core.set("originVersion", originVersion) xml_core.set("version", "0.3") xml_core.set("xmlns", "http://orthoXML.org/2011/") return xml_core def _add_species_data(self): sp_data = self.hog.get_all_descendant_genes_clustered_by_species() for species, childs in sp_data.items(): species_xml = etree.Element("species") species_xml.set("name", species.name) species_xml.set("NCBITaxId", str(-1)) self.xml.insert(0, species_xml) # Add <database> into <species> database_xml = etree.SubElement(species_xml, "database") database_xml.set("name", "None") database_xml.set("version", "None") # Add <genes> TAG into <database> genes_xml = etree.SubElement(database_xml, "genes") # Fill <genes> with <gene> for gene_obj in childs: gene_xml = etree.SubElement(genes_xml, "gene") gene_xml.set("id", str(gene_obj.unique_id)) gene_xml.set("protId", str(gene_obj.prot_id)) def _add_groups(self): from . import abstractgene as absGene def _process_child(child, current_xml): if isinstance(child, absGene.Gene): generef_xml = etree.SubElement(current_xml, "geneRef") generef_xml.set('id', str(child.unique_id)) else: _visit(child, current_xml) def _visit(hog, parent): if len(hog.children) == 1: current_hog_xml = parent elif len(hog.duplications) >= 1: dup_child = [] for duplicationNode in hog.duplications: dup_child += duplicationNode.children remaining_hog = list(set(hog.children) - set(dup_child)) if len(remaining_hog) == 0 and len(hog.duplications) == 1 : current_hog_xml = parent else: current_hog_xml = etree.SubElement(parent, "orthologGroup") taxon = etree.SubElement(current_hog_xml, "property") taxon.set("name", 'TaxRange') taxon.set("value", hog.genome.name) current_hog_xml.set('id', str(hog.hog_id)) else: current_hog_xml = etree.SubElement(parent, "orthologGroup") taxon = etree.SubElement(current_hog_xml, "property") taxon.set("name", 'TaxRange') taxon.set("value", hog.genome.name) current_hog_xml.set('id', str(hog.hog_id)) processed_child = [] if len(hog.duplications) > 0: for duplicationNode in hog.duplications: paralogGroup = etree.SubElement(current_hog_xml, "paralogGroup") for child in duplicationNode.children: _process_child(child, paralogGroup) processed_child.append(child) remaining_hog = list(set(hog.children) - set(processed_child)) for child in remaining_hog: _process_child(child, current_hog_xml) self.groupsxml = etree.SubElement(self.xml, "groups") _visit(self.hog, self.groupsxml) def get_orthoxml_str(self): return etree.tostring(self.xml, encoding='utf8', method='xml', pretty_print=False).decode('utf-8')