aboutsummaryrefslogtreecommitdiff
path: root/gn2/wqflask/marker_regression
diff options
context:
space:
mode:
authorArun Isaac2023-12-29 18:55:37 +0000
committerArun Isaac2023-12-29 19:01:46 +0000
commit204a308be0f741726b9a620d88fbc22b22124c81 (patch)
treeb3cf66906674020b530c844c2bb4982c8a0e2d39 /gn2/wqflask/marker_regression
parent83062c75442160427b50420161bfcae2c5c34c84 (diff)
downloadgenenetwork2-204a308be0f741726b9a620d88fbc22b22124c81.tar.gz
Namespace all modules under gn2.
We move all modules under a gn2 directory. This is important for "correct" packaging and deployment as a Guix service.
Diffstat (limited to 'gn2/wqflask/marker_regression')
-rw-r--r--gn2/wqflask/marker_regression/__init__.py0
-rw-r--r--gn2/wqflask/marker_regression/display_mapping_results.py3336
-rw-r--r--gn2/wqflask/marker_regression/exceptions.py13
-rw-r--r--gn2/wqflask/marker_regression/gemma_mapping.py245
-rw-r--r--gn2/wqflask/marker_regression/plink_mapping.py167
-rw-r--r--gn2/wqflask/marker_regression/qtlreaper_mapping.py193
-rw-r--r--gn2/wqflask/marker_regression/rqtl_mapping.py167
-rw-r--r--gn2/wqflask/marker_regression/run_mapping.py743
8 files changed, 4864 insertions, 0 deletions
diff --git a/gn2/wqflask/marker_regression/__init__.py b/gn2/wqflask/marker_regression/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/gn2/wqflask/marker_regression/__init__.py
diff --git a/gn2/wqflask/marker_regression/display_mapping_results.py b/gn2/wqflask/marker_regression/display_mapping_results.py
new file mode 100644
index 00000000..b3bf3bc3
--- /dev/null
+++ b/gn2/wqflask/marker_regression/display_mapping_results.py
@@ -0,0 +1,3336 @@
+# Copyright (C) University of Tennessee Health Science Center, Memphis, TN.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Affero General Public License for more details.
+#
+# This program is available from Source Forge: at GeneNetwork Project
+# (sourceforge.net/projects/genenetwork/).
+#
+# Contact Drs. Robert W. Williams and Xiaodong Zhou (2010)
+# at rwilliams@uthsc.edu and xzhou15@uthsc.edu
+#
+#
+#
+# This module is used by GeneNetwork project (www.genenetwork.org)
+#
+# Created by GeneNetwork Core Team 2010/08/10
+#
+# Last updated by Zach 12/14/2010
+
+import datetime
+import string
+from math import *
+from PIL import Image
+from PIL import ImageDraw
+from PIL import ImageFont
+from PIL import ImageColor
+import os
+import json
+
+import htmlgen as HT
+
+from gn2.base import webqtlConfig
+from gn2.base.GeneralObject import GeneralObject
+from gn2.utility import webqtlUtil
+from gn2.utility import Plot
+from gn2.utility.tools import get_setting
+from gn2.wqflask.interval_analyst import GeneUtil
+from gn2.base.webqtlConfig import GENERATED_IMAGE_DIR
+from gn2.utility.pillow_utils import draw_rotated_text, draw_open_polygon
+from gn2.wqflask.database import database_connection
+
+try: # Only import this for Python3
+ from functools import reduce
+except:
+ pass
+
+RED = ImageColor.getrgb("red")
+BLUE = ImageColor.getrgb("blue")
+GRAY = ImageColor.getrgb("gray")
+GOLD = ImageColor.getrgb("gold")
+BLACK = ImageColor.getrgb("black")
+GREEN = ImageColor.getrgb("green")
+PURPLE = ImageColor.getrgb("purple")
+ORANGE = ImageColor.getrgb("orange")
+YELLOW = ImageColor.getrgb("yellow")
+DARKRED = ImageColor.getrgb("darkred")
+DARKBLUE = ImageColor.getrgb("darkblue")
+DARKGRAY = ImageColor.getrgb("darkgray")
+DEEPPINK = ImageColor.getrgb("deeppink")
+DARKGREEN = ImageColor.getrgb("darkgreen")
+GAINSBORO = ImageColor.getrgb("gainsboro")
+LIGHTBLUE = ImageColor.getrgb("lightblue")
+DARKORANGE = ImageColor.getrgb("darkorange")
+DARKVIOLET = ImageColor.getrgb("darkviolet")
+MEDIUMPURPLE = ImageColor.getrgb("mediumpurple")
+# ---- END: Define common colours ---- #
+
+# ZS: List of distinct colors for manhattan plot if user selects "varied"
+COLOR_CODES = ["#FF0000", "#00FF00", "#0000FF", "#FFFF00", "#FF00FF", "#00FFFF",
+ "#000000", "#800000", "#008000", "#000080", "#808000", "#800080",
+ "#008080", "#808080", "#C00000", "#00C000", "#0000C0", "#C0C000",
+ "#C000C0", "#00C0C0", "#C0C0C0", "#400000", "#004000", "#000040"]
+
+DISTINCT_COLOR_LIST = [ImageColor.getrgb(color) for color in COLOR_CODES]
+
+# ---- FONT FILES ---- #
+VERDANA_FILE = "./gn2/wqflask/static/fonts/verdana.ttf"
+VERDANA_BOLD_FILE = "./gn2/wqflask/static/fonts/verdanab.ttf"
+TREBUC_FILE = "./gn2/wqflask/static/fonts/trebucbd.ttf"
+FNT_BS_FILE = "./gn2/wqflask/static/fonts/fnt_bs.ttf"
+ARIAL_FILE = "./gn2/wqflask/static/fonts/arial.ttf"
+
+assert(os.path.isfile(VERDANA_FILE))
+
+class HtmlGenWrapper:
+ """Wrapper Methods for HTML gen"""
+ @staticmethod
+ def create_image_tag(**kwargs):
+ image = HT.Image("", "")
+ for key, value in list(kwargs.items()):
+ image.set_attribute(key, value)
+ return image
+
+ @staticmethod
+ def create_form_tag(**kwargs):
+ form = HT.Form("POST", "") # Default method is POST
+
+ for key, value in list(kwargs.items()):
+ if key == "submit":
+ form.append(value)
+ continue
+ form.set_attribute(key.replace("cgi", "action"), str(value))
+ return form
+
+ @staticmethod
+ def create_p_tag(**kwargs):
+ paragraph = HT.Paragraph()
+ for key, value in list(kwargs.items()):
+ paragraph.set_attribute(key, value)
+ return paragraph
+
+ @staticmethod
+ def create_br_tag():
+ return HT.VoidElement("br")
+
+ @staticmethod
+ def create_input_tag(**kwargs):
+ input_ = HT.Input()
+ for key, value in list(kwargs.items()):
+ input_.set_attribute(key.lower().replace("_", ""), value)
+ return input_
+
+ @staticmethod
+ def create_area_tag(**kwargs):
+ area = HT.VoidElement("area")
+ for key, value in list(kwargs.items()):
+ area.set_attribute(key, value)
+ return area
+
+ @staticmethod
+ def create_link_tag(href, content, **kwargs):
+ link = HT.Link(href, content)
+ for key, value in list(kwargs.items()):
+ link.set_attribute(key, value)
+ return link
+
+ @staticmethod
+ def create_map_tag(**kwargs):
+ map_ = HT.Element("map")
+ for key, value in list(kwargs.items()):
+ map_.set_attribute(key, value)
+ return map_
+
+
+class DisplayMappingResults:
+ """Inteval Mapping Plot Page"""
+ cMGraphInterval = 5
+ GRAPH_MIN_WIDTH = 900
+ GRAPH_MAX_WIDTH = 10000 # Don't set this too high
+ GRAPH_DEFAULT_WIDTH = 1280
+ MULT_GRAPH_DEFAULT_WIDTH = 2000
+ MULT_GRAPH_MIN_WIDTH = 1400
+ MULT_GRAPH_DEFAULT_WIDTH = 1600
+ GRAPH_DEFAULT_HEIGHT = 600
+
+ # Display order:
+ # UCSC BAND =========
+ # ENSEMBL BAND -=-=-=
+ # ** GENES **********
+ BAND_SPACING = 4
+
+ BAND_HEIGHT = 10
+ BAND_HEIGHT = 10
+ BAND_HEIGHT = 10
+
+ NUM_GENE_ROWS = 10
+ EACH_GENE_HEIGHT = 6 # number of pixels tall, for each gene to display
+ EACH_GENE_ARROW_WIDTH = 5
+ EACH_GENE_ARROW_SPACING = 14
+ DRAW_DETAIL_MB = 4
+ DRAW_UTR_LABELS_MB = 4
+
+ qmarkImg = HtmlGenWrapper.create_image_tag(
+ src='/images/qmarkBoxBlue.gif',
+ width="10", height="13", border="0", alt='Glossary'
+ )
+
+ # Note that "qmark.gif" is a similar, smaller, rounded-edges
+ # question mark. It doesn't look like the ones on the image,
+ # though, which is why we don't use it here.
+
+ HELP_WINDOW_NAME = 'helpWind'
+
+ # BEGIN HaplotypeAnalyst
+ NR_INDIVIDUALS = 0
+ # END HaplotypeAnalyst
+
+ ALEX_DEBUG_BOOL_PRINT_GENE_LIST = 1
+
+ kONE_MILLION = 1000000
+
+ LODFACTOR = 4.61
+
+ SNP_COLOR = ORANGE # Color for the SNP "seismograph"
+ TRANSCRIPT_LOCATION_COLOR = MEDIUMPURPLE
+
+ BOOTSTRAP_BOX_COLOR = YELLOW
+ LRS_COLOR = ImageColor.getrgb("#0000FF")
+ SIGNIFICANT_COLOR = ImageColor.getrgb("#EBC7C7")
+ SUGGESTIVE_COLOR = GAINSBORO
+ SIGNIFICANT_WIDTH = 5
+ SUGGESTIVE_WIDTH = 5
+ ADDITIVE_COLOR_POSITIVE = GREEN
+ ADDITIVE_COLOR_NEGATIVE = ORANGE
+ DOMINANCE_COLOR_POSITIVE = DARKVIOLET
+ DOMINANCE_COLOR_NEGATIVE = RED
+
+ # BEGIN HaplotypeAnalyst
+ HAPLOTYPE_POSITIVE = GREEN
+ HAPLOTYPE_NEGATIVE = RED
+ HAPLOTYPE_HETEROZYGOUS = BLUE
+ HAPLOTYPE_RECOMBINATION = DARKGRAY
+ # END HaplotypeAnalyst
+
+ TOP_RIGHT_INFO_COLOR = BLACK
+
+ CLICKABLE_WEBQTL_REGION_COLOR = ImageColor.getrgb("#F5D3D3")
+ CLICKABLE_WEBQTL_REGION_OUTLINE_COLOR = ImageColor.getrgb("#FCE9E9")
+ CLICKABLE_WEBQTL_TEXT_COLOR = ImageColor.getrgb("#912828")
+
+ CLICKABLE_PHENOGEN_REGION_COLOR = ImageColor.getrgb("#A2FB94")
+ CLICKABLE_PHENOGEN_REGION_OUTLINE_COLOR = ImageColor.getrgb("#CEFEC7")
+ CLICKABLE_PHENOGEN_TEXT_COLOR = ImageColor.getrgb("#1FD504")
+
+ CLICKABLE_UCSC_REGION_COLOR = ImageColor.getrgb("#DDDDEE")
+ CLICKABLE_UCSC_REGION_OUTLINE_COLOR = ImageColor.getrgb("#EDEDFF")
+ CLICKABLE_UCSC_TEXT_COLOR = ImageColor.getrgb("#333366")
+
+ CLICKABLE_ENSEMBL_REGION_COLOR = ImageColor.getrgb("#EEEEDD")
+ CLICKABLE_ENSEMBL_REGION_OUTLINE_COLOR = ImageColor.getrgb("#FEFEEE")
+ CLICKABLE_ENSEMBL_TEXT_COLOR = ImageColor.getrgb("#555500")
+
+ GRAPH_BACK_LIGHT_COLOR = ImageColor.getrgb("#FBFBFF")
+ GRAPH_BACK_DARK_COLOR = ImageColor.getrgb("#F1F1F9")
+
+ HELP_PAGE_REF = '/glossary.html'
+
+ def __init__(self, start_vars):
+ self.temp_uuid = start_vars['temp_uuid']
+ self.hash_of_inputs = start_vars['hash_of_inputs']
+ self.dataid = start_vars['dataid']
+
+ self.dataset = start_vars['dataset']
+ self.this_trait = start_vars['this_trait']
+ self.n_samples = start_vars['n_samples']
+ self.species = start_vars['species']
+ self.genofile_string = ""
+ if 'genofile_string' in start_vars:
+ self.genofile_string = start_vars['genofile_string']
+
+ self.geno_db_exists = start_vars['geno_db_exists']
+
+ self.first_run = True
+ if 'first_run' in start_vars:
+ self.first_run = start_vars['first_run']
+
+ if 'temp_trait' in start_vars and start_vars['temp_trait'] != "False":
+ self.temp_trait = "True"
+ self.group = start_vars['group']
+
+ # Needing for form submission when doing single chr
+ # mapping or remapping after changing options
+ self.sample_vals = start_vars['sample_vals']
+ self.vals_hash= start_vars['vals_hash']
+ self.sample_vals_dict = json.loads(self.sample_vals)
+
+ self.transform = start_vars['transform']
+ self.mapping_method = start_vars['mapping_method']
+ self.mapping_results_path = start_vars['mapping_results_path']
+ if self.mapping_method == "rqtl_geno":
+ self.mapmethod_rqtl_geno = start_vars['method']
+ self.mapmodel_rqtl_geno = start_vars['model']
+ self.pair_scan = start_vars['pair_scan']
+
+ self.js_data = start_vars['js_data']
+ # Top markers to display in table
+ self.trimmed_markers = start_vars['trimmed_markers']
+
+ if self.dataset.group.species == "rat":
+ self._ucscDb = "rn6"
+ elif self.dataset.group.species == "mouse":
+ self._ucscDb = "mm10"
+ else:
+ self._ucscDb = ""
+
+ #####################################
+ # Options
+ #####################################
+ # Mapping options
+ if start_vars['mapping_scale'] != "":
+ self.plotScale = start_vars['mapping_scale']
+ else:
+ self.plotScale = "physic"
+
+ self.manhattan_plot = start_vars['manhattan_plot']
+ if self.manhattan_plot:
+ self.color_scheme = "alternating"
+ if 'color_scheme' in start_vars:
+ self.color_scheme = start_vars['color_scheme']
+ if self.color_scheme == "single":
+ self.manhattan_single_color = ImageColor.getrgb(
+ "#" + start_vars['manhattan_single_color'])
+
+ if 'permCheck' in list(start_vars.keys()):
+ self.permChecked = start_vars['permCheck']
+ else:
+ self.permChecked = False
+ if start_vars['num_perm'] > 0:
+ self.nperm = int(start_vars['num_perm'])
+ if self.permChecked:
+ self.perm_output = start_vars['perm_output']
+ self.suggestive = start_vars['suggestive']
+ self.significant = start_vars['significant']
+ else:
+ self.nperm = 0
+
+ if 'bootCheck' in list(start_vars.keys()):
+ self.bootChecked = start_vars['bootCheck']
+ else:
+ self.bootChecked = False
+ if 'num_bootstrap' in list(start_vars.keys()):
+ self.nboot = int(start_vars['num_bootstrap'])
+ else:
+ self.nboot = 0
+ if 'bootstrap_results' in list(start_vars.keys()):
+ self.bootResult = start_vars['bootstrap_results']
+ else:
+ self.bootResult = []
+
+ if 'do_control' in list(start_vars.keys()):
+ self.doControl = start_vars['do_control']
+ else:
+ self.doControl = "false"
+ if 'control_marker' in list(start_vars.keys()):
+ self.controlLocus = start_vars['control_marker']
+ else:
+ self.controlLocus = ""
+ if 'covariates' in list(start_vars.keys()):
+ self.covariates = start_vars['covariates']
+ if 'maf' in list(start_vars.keys()):
+ self.maf = start_vars['maf']
+ else:
+ self.maf = ""
+ if 'output_files' in list(start_vars.keys()):
+ self.output_files = start_vars['output_files']
+ if 'use_loco' in list(start_vars.keys()) and self.mapping_method == "gemma":
+ self.use_loco = start_vars['use_loco']
+
+ if self.mapping_method == "reaper":
+ if 'output_files' in start_vars:
+ self.output_files = ",".join(
+ [(the_file if the_file is not None else "") for the_file in start_vars['output_files']])
+
+ self.categorical_vars = ""
+ self.perm_strata = ""
+ if 'perm_strata' in list(start_vars.keys()) and 'categorical_vars' in list(start_vars.keys()):
+ self.categorical_vars = start_vars['categorical_vars']
+ self.perm_strata = start_vars['perm_strata']
+
+ self.selectedChr = int(start_vars['selected_chr'])
+
+ self.strainlist = start_vars['samples']
+
+ self.traitList = []
+ thisTrait = start_vars['this_trait']
+ self.traitList.append(thisTrait)
+
+ ################################################################
+ # Calculations QTL goes here
+ ################################################################
+ self.multipleInterval = len(self.traitList) > 1
+ self.qtlresults = start_vars['qtl_results']
+
+ if self.multipleInterval:
+ self.colorCollection = Plot.colorSpectrum(len(self.qtlresults))
+ else:
+ self.colorCollection = [self.LRS_COLOR]
+
+ self.dataset.group.genofile = self.genofile_string.split(":")[0]
+ if self.mapping_method == "reaper" and self.manhattan_plot != True:
+ self.genotype = self.dataset.group.read_genotype_file(
+ use_reaper=True)
+ else:
+ self.genotype = self.dataset.group.read_genotype_file()
+
+ # Darwing Options
+ try:
+ if self.selectedChr > -1:
+ self.graphWidth = min(self.GRAPH_MAX_WIDTH, max(
+ self.GRAPH_MIN_WIDTH, int(start_vars['graphWidth'])))
+ else:
+ self.graphWidth = min(self.GRAPH_MAX_WIDTH, max(
+ self.MULT_GRAPH_MIN_WIDTH, int(start_vars['graphWidth'])))
+ except:
+ if self.selectedChr > -1:
+ self.graphWidth = self.GRAPH_DEFAULT_WIDTH
+ else:
+ self.graphWidth = self.MULT_GRAPH_DEFAULT_WIDTH
+
+# BEGIN HaplotypeAnalyst
+ if 'haplotypeAnalystCheck' in list(start_vars.keys()):
+ self.haplotypeAnalystChecked = start_vars['haplotypeAnalystCheck']
+ else:
+ self.haplotypeAnalystChecked = False
+# END HaplotypeAnalyst
+
+ self.graphHeight = self.GRAPH_DEFAULT_HEIGHT
+ self.dominanceChecked = False
+ if 'LRSCheck' in list(start_vars.keys()):
+ self.LRS_LOD = start_vars['LRSCheck']
+ else:
+ self.LRS_LOD = start_vars['score_type']
+ self.intervalAnalystChecked = True
+ self.draw2X = False
+ if 'additiveCheck' in list(start_vars.keys()):
+ self.additiveChecked = start_vars['additiveCheck']
+ else:
+ self.additiveChecked = False
+ if 'viewLegend' in list(start_vars.keys()):
+ self.legendChecked = start_vars['viewLegend']
+ else:
+ self.legendChecked = False
+ if 'showSNP' in list(start_vars.keys()):
+ self.SNPChecked = start_vars['showSNP']
+ else:
+ self.SNPChecked = False
+ if 'showHomology' in list(start_vars.keys()):
+ self.homologyChecked = start_vars['showHomology']
+ else:
+ self.homologyChecked = "ON"
+ if 'showGenes' in list(start_vars.keys()):
+ self.geneChecked = start_vars['showGenes']
+ else:
+ self.geneChecked = False
+ try:
+ self.startMb = float(start_vars['startMb'])
+ except:
+ self.startMb = -1
+ try:
+ self.endMb = float(start_vars['endMb'])
+ except:
+ self.endMb = -1
+ try:
+ self.lrsMax = float(start_vars['lrsMax'])
+ except:
+ self.lrsMax = 0
+
+ # Trait Infos
+ self.identification = ""
+
+ ################################################################
+ # Generate Chr list and Retrieve Length Information
+ ################################################################
+ self.ChrList = [("All", -1)]
+ for i, indChr in enumerate(self.genotype):
+ if self.dataset.group.species == "mouse" and indChr.name == "20":
+ self.ChrList.append(("X", i))
+ elif self.dataset.group.species == "rat" and indChr.name == "21":
+ self.ChrList.append(("X", i))
+ self.ChrList.append((indChr.name, i))
+ with database_connection(get_setting("SQL_URI")) as conn, conn.cursor() as cursor:
+ cursor.execute("SELECT Length FROM Chr_Length, InbredSet "
+ "WHERE Chr_Length.SpeciesId = InbredSet.SpeciesId "
+ "AND InbredSet.Name = %s AND Chr_Length.Name IN "
+ f"({', '.join(['%s' for x in self.ChrList[1:]])}) "
+ "ORDER BY Chr_Length.OrderId",
+ (self.dataset.group.name,
+ *[x[0] for x in self.ChrList[1:]],))
+ self.ChrLengthMbList = cursor.fetchall()
+
+ self.ChrLengthMbList = [x[0] / 1000000.0 for x in self.ChrLengthMbList]
+ self.ChrLengthMbSum = reduce(
+ lambda x, y: x + y, self.ChrLengthMbList, 0.0)
+ if self.ChrLengthMbList:
+ self.MbGraphInterval = self.ChrLengthMbSum / \
+ (len(self.ChrLengthMbList) * 12) # Empirical Mb interval
+ else:
+ self.MbGraphInterval = 1
+
+ self.ChrLengthCMList = []
+ for i, _chr in enumerate(self.genotype):
+ self.ChrLengthCMList.append(_chr[-1].cM - _chr[0].cM)
+
+ self.ChrLengthCMSum = reduce(
+ lambda x, y: x + y, self.ChrLengthCMList, 0.0)
+
+ if self.plotScale == 'physic':
+ self.GraphInterval = self.MbGraphInterval # Mb
+ else:
+ self.GraphInterval = self.cMGraphInterval # cM
+
+ #########################
+ # Get the sorting column
+ #########################
+ RISet = self.dataset.group.name
+ if RISet in ('AXB', 'BXA', 'AXBXA'):
+ self.diffCol = ['B6J', 'A/J']
+ elif RISet in ('BXD', 'BXD300', 'B6D2F2', 'BDF2-2005', 'BDF2-1999', 'BHHBF2', 'BXD-Harvested', 'BXD-Longevity', 'BXD-Micturition', 'BXD-AE', 'BXD-NIA-AD', 'B6D2RI', 'BXD-Bone', 'DOD-BXD-GWI', 'BXD-Heart-Metals', 'UTHSC-Cannabinoid'):
+ self.diffCol = ['B6J', 'D2J']
+ elif RISet in ('CXB'):
+ self.diffCol = ['CBY', 'B6J']
+ elif RISet in ('BXH', 'BHF2'):
+ self.diffCol = ['B6J', 'C3H']
+ elif RISet in ('B6BTBRF2'):
+ self.diffCol = ['B6J', 'BTB']
+ elif RISet in ('LXS'):
+ self.diffCol = ['ILS', 'ISS']
+ else:
+ self.diffCol = []
+
+ for i, strain in enumerate(self.diffCol):
+ with database_connection(get_setting("SQL_URI")) as conn, conn.cursor() as cursor:
+ cursor.execute("SELECT Id FROM Strain WHERE Symbol = %s",
+ (strain,))
+ if result := cursor.fetchone():
+ self.diffCol[i] = result[0]
+
+ ################################################################
+ # GeneCollection goes here
+ ################################################################
+ if self.plotScale == 'physic' and self.selectedChr != -1:
+ #StartMb or EndMb
+ if self.startMb < 0 or self.endMb < 0:
+ self.startMb = 0
+ self.endMb = self.ChrLengthMbList[self.selectedChr - 1]
+
+ geneTable = ""
+
+ self.geneCol = None
+ self.homology = None
+ if self.plotScale == 'physic' and self.selectedChr > -1 and (self.intervalAnalystChecked or self.geneChecked or self.homologyChecked):
+ # Draw the genes for this chromosome / region of this chromosome
+ webqtldatabase = self.dataset.name
+
+ if self.dataset.group.species == "mouse":
+ if self.selectedChr == 20:
+ chrName = "X"
+ else:
+ chrName = self.selectedChr
+ self.geneCol = GeneUtil.loadGenes(
+ str(chrName), self.diffCol, self.startMb, self.endMb, "mouse")
+ self.homology = GeneUtil.load_homology(str(chrName), self.startMb, self.endMb, "mouse_to_human.csv")
+
+ elif self.dataset.group.species == "rat":
+ if self.selectedChr == 21:
+ chrName = "X"
+ else:
+ chrName = self.selectedChr
+ self.geneCol = GeneUtil.loadGenes(
+ str(chrName), self.diffCol, self.startMb, self.endMb, "rat")
+
+ if self.geneCol and self.intervalAnalystChecked:
+ #######################################################################
+ #Nick use GENEID as RefGene to get Literature Correlation Informations#
+ #For Interval Mapping, Literature Correlation isn't useful, so skip it#
+ #through set GENEID is None #
+ #######################################################################
+
+ GENEID = None
+
+ self.geneTable(self.geneCol, GENEID)
+
+# BEGIN HaplotypeAnalyst
+# count the amount of individuals to be plotted, and increase self.graphHeight
+ if self.haplotypeAnalystChecked and self.selectedChr > -1:
+ thisTrait = self.this_trait
+ smd = []
+ for sample in self.sample_vals_dict.keys():
+ if self.sample_vals_dict[sample] != "x":
+ temp = GeneralObject(name=sample, value=float(
+ self.sample_vals_dict[sample]))
+ smd.append(temp)
+ else:
+ continue
+ samplelist = list(self.genotype.prgy)
+ for j, _geno in enumerate(self.genotype[0][1].genotype):
+ for item in smd:
+ if item.name == samplelist[j]:
+ self.NR_INDIVIDUALS = self.NR_INDIVIDUALS + 1
+# default:
+ self.graphHeight = self.graphHeight + 2 * \
+ (self.NR_INDIVIDUALS + 10) * self.EACH_GENE_HEIGHT
+# END HaplotypeAnalyst
+
+ if self.homologyChecked and self.homology and self.geneChecked and self.geneCol:
+ self.graphHeight = self.graphHeight + (self.NUM_GENE_ROWS) * self.EACH_GENE_HEIGHT
+ if self.geneChecked and self.geneCol:
+ self.graphHeight = self.graphHeight + (self.NUM_GENE_ROWS) * self.EACH_GENE_HEIGHT
+
+
+ ################################################################
+ # Plots goes here
+ ################################################################
+ showLocusForm = ""
+ intCanvas = Image.new("RGBA", size=(self.graphWidth, self.graphHeight))
+ gifmap = self.plotIntMapping(
+ intCanvas, startMb=self.startMb, endMb=self.endMb, showLocusForm=showLocusForm)
+
+ self.gifmap = gifmap.__str__()
+
+ self.filename = webqtlUtil.genRandStr("Itvl_")
+ intCanvas.save(
+ "{}.png".format(
+ os.path.join(webqtlConfig.GENERATED_IMAGE_DIR, self.filename)),
+ format='png')
+ intImg = HtmlGenWrapper.create_image_tag(
+ src="/image/{}.png".format(self.filename),
+ border="0", usemap='#WebQTLImageMap'
+ )
+
+ # Scales plot differently for high resolution
+ if self.draw2X:
+ intCanvasX2 = Image.new("RGBA", size=(
+ self.graphWidth * 2, self.graphHeight * 2))
+ gifmapX2 = self.plotIntMapping(
+ intCanvasX2, startMb=self.startMb, endMb=self.endMb, showLocusForm=showLocusForm, zoom=2)
+ intCanvasX2.save(
+ "{}.png".format(
+ os.path.join(webqtlConfig.GENERATED_IMAGE_DIR,
+ self.filename + "X2")),
+ format='png')
+
+ ################################################################
+ # Outputs goes here
+ ################################################################
+ # this form is used for opening Locus page or trait page, only available for genetic mapping
+ if showLocusForm:
+ showLocusForm = HtmlGenWrapper.create_form_tag(
+ cgi=os.path.join(webqtlConfig.CGIDIR, webqtlConfig.SCRIPTFILE),
+ enctype='multipart/form-data',
+ name=showLocusForm,
+ submit=HtmlGenWrapper.create_input_tag(type_='hidden'))
+
+ hddn = {'FormID': 'showDatabase', 'ProbeSetID': '_', 'database': fd.RISet + \
+ "Geno", 'CellID': '_', 'RISet': fd.RISet, 'incparentsf1': 'ON'}
+ for key in hddn.keys():
+ showLocusForm.append(HtmlGenWrapper.create_input_tag(
+ name=key, value=hddn[key], type_='hidden'))
+ showLocusForm.append(intImg)
+ else:
+ showLocusForm = intImg
+
+ if (self.permChecked and self.nperm > 0) and not (self.multipleInterval and 0 < self.nperm):
+ self.perm_filename = self.drawPermutationHistogram()
+
+ ################################################################
+ # footnote goes here
+ ################################################################
+ # Small('More information about this graph is available here.')
+ btminfo = HtmlGenWrapper.create_p_tag(id="smallsize")
+
+ if self.traitList and self.traitList[0].dataset and self.traitList[0].dataset.type == 'Geno':
+ btminfo.append(HtmlGenWrapper.create_br_tag())
+ btminfo.append(
+ 'Mapping using genotype data as a trait will result in infinity LRS at one locus. In order to display the result properly, all LRSs higher than 100 are capped at 100.')
+
+ def plotIntMapping(self, canvas, offset=(80, 120, 110, 100), zoom=1, startMb=None, endMb=None, showLocusForm=""):
+ im_drawer = ImageDraw.Draw(canvas)
+ # calculating margins
+ xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
+ if self.multipleInterval:
+ yTopOffset = max(90, yTopOffset)
+ else:
+ if self.legendChecked:
+ yTopOffset += 10
+ if self.covariates != "" and self.controlLocus and self.doControl != "false":
+ yTopOffset += 25
+ if len(self.transform) > 0:
+ yTopOffset += 5
+ else:
+ pass
+
+ if self.plotScale != 'physic':
+ yBottomOffset = max(120, yBottomOffset)
+ fontZoom = zoom
+ if zoom == 2:
+ xLeftOffset += 20
+ fontZoom = 1.5
+
+ xLeftOffset = int(xLeftOffset * fontZoom)
+ xRightOffset = int(xRightOffset * fontZoom)
+ yBottomOffset = int(yBottomOffset * fontZoom)
+
+ cWidth = canvas.size[0]
+ cHeight = canvas.size[1]
+ plotWidth = cWidth - xLeftOffset - xRightOffset
+ plotHeight = cHeight - yTopOffset - yBottomOffset
+
+ # Drawing Area Height
+ drawAreaHeight = plotHeight
+ if self.plotScale == 'physic' and self.selectedChr > -1:
+ if self.dataset.group.species == "mouse" or self.dataset.group.species == "rat":
+ drawAreaHeight -= 4 * self.BAND_HEIGHT + 4 * self.BAND_SPACING + 10 * zoom
+ else:
+ drawAreaHeight -= 3 * self.BAND_HEIGHT + 3 * self.BAND_SPACING + 10 * zoom
+ if self.homologyChecked:
+ drawAreaHeight -= self.NUM_GENE_ROWS * \
+ self.EACH_GENE_HEIGHT + 3 * self.BAND_SPACING
+ if self.geneChecked:
+ drawAreaHeight -= self.NUM_GENE_ROWS * \
+ self.EACH_GENE_HEIGHT + 3 * self.BAND_SPACING
+ else:
+ if self.selectedChr > -1:
+ drawAreaHeight -= 20
+ else:
+ drawAreaHeight -= 30
+
+# BEGIN HaplotypeAnalyst
+ if self.haplotypeAnalystChecked and self.selectedChr > -1:
+ drawAreaHeight -= self.EACH_GENE_HEIGHT * \
+ (self.NR_INDIVIDUALS + 10) * 2 * zoom
+# END HaplotypeAnalyst
+
+ if zoom == 2:
+ drawAreaHeight -= 60
+
+ # Image map
+ gifmap = HtmlGenWrapper.create_map_tag(name="WebQTLImageMap")
+
+ newoffset = (xLeftOffset, xRightOffset, yTopOffset, yBottomOffset)
+ # Draw the alternating-color background first and get plotXScale
+ plotXScale = self.drawGraphBackground(
+ canvas, gifmap, offset=newoffset, zoom=zoom, startMb=startMb, endMb=endMb)
+
+ # draw bootstap
+ if self.bootChecked and not self.multipleInterval:
+ self.drawBootStrapResult(canvas, self.nboot, drawAreaHeight, plotXScale,
+ offset=newoffset, zoom=zoom, startMb=startMb, endMb=endMb)
+
+ # Draw clickable region and gene band if selected
+ if self.plotScale == 'physic' and self.selectedChr > -1:
+ self.drawClickBand(canvas, gifmap, plotXScale, offset=newoffset,
+ zoom=zoom, startMb=startMb, endMb=endMb)
+
+ if self.geneChecked and self.geneCol:
+ self.drawGeneBand(canvas, gifmap, plotXScale, offset=newoffset,
+ zoom=zoom, startMb=startMb, endMb=endMb)
+ if self.homologyChecked and self.homology:
+ if self.geneChecked and self.geneCol:
+ yTopOffset = newoffset[3] + self.NUM_GENE_ROWS * \
+ self.EACH_GENE_HEIGHT + 3 * self.BAND_SPACING + 10
+ self.drawHomologyBand(canvas, gifmap, plotXScale, offset=(xLeftOffset, xRightOffset, yTopOffset, yBottomOffset),
+ zoom=zoom, startMb=startMb, endMb=endMb)
+ if self.SNPChecked:
+ self.drawSNPTrackNew(
+ canvas, offset=newoffset, zoom=2 * zoom, startMb=startMb, endMb=endMb)
+# BEGIN HaplotypeAnalyst
+ if self.haplotypeAnalystChecked:
+ self.drawHaplotypeBand(
+ canvas, gifmap, plotXScale, offset=newoffset, zoom=zoom, startMb=startMb, endMb=endMb)
+# END HaplotypeAnalyst
+ # Draw X axis
+ self.drawXAxis(canvas, drawAreaHeight, gifmap, plotXScale, showLocusForm,
+ offset=newoffset, zoom=zoom, startMb=startMb, endMb=endMb)
+ # Draw QTL curve
+ self.drawQTL(canvas, drawAreaHeight, gifmap, plotXScale,
+ offset=newoffset, zoom=zoom, startMb=startMb, endMb=endMb)
+
+ # draw legend
+ if self.multipleInterval:
+ self.drawMultiTraitName(
+ fd, canvas, gifmap, showLocusForm, offset=newoffset)
+ elif self.legendChecked:
+ self.drawLegendPanel(canvas, offset=newoffset, zoom=zoom)
+ else:
+ pass
+
+ # draw position, no need to use a separate function
+ self.drawProbeSetPosition(
+ canvas, plotXScale, offset=newoffset, zoom=zoom)
+
+ return gifmap
+
+ def drawBootStrapResult(self, canvas, nboot, drawAreaHeight, plotXScale, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
+ im_drawer = ImageDraw.Draw(canvas)
+ xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
+ plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
+ plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
+ yZero = canvas.size[1] - yBottomOffset
+ fontZoom = zoom
+ if zoom == 2:
+ fontZoom = 1.5
+
+ bootHeightThresh = drawAreaHeight * 3 / 4
+
+ # break bootstrap result into groups
+ BootCoord = []
+ i = 0
+ previous_chr = None
+ previous_chr_as_int = 0
+ startX = xLeftOffset
+
+ BootChrCoord = []
+ if self.selectedChr == -1: # ZS: If viewing full genome/all chromosomes
+ for i, result in enumerate(self.qtlresults):
+ if result['chr'] != previous_chr:
+ previous_chr = result['chr']
+ previous_chr_as_int += 1
+ if previous_chr_as_int != 1:
+ BootCoord.append(BootChrCoord)
+ BootChrCoord = []
+ startX += (
+ self.ChrLengthDistList[previous_chr_as_int - 2] + self.GraphInterval) * plotXScale
+ if self.plotScale == 'physic':
+ Xc = startX + (result['Mb'] - self.startMb) * plotXScale
+ else:
+ Xc = startX + \
+ (result['cM'] - self.qtlresults[0]['cM']) * plotXScale
+ BootChrCoord.append([Xc, self.bootResult[i]])
+ else:
+ for i, result in enumerate(self.qtlresults):
+ if str(result['chr']) == str(self.ChrList[self.selectedChr][0]):
+ if self.plotScale == 'physic':
+ Xc = startX + (result['Mb'] - \
+ self.startMb) * plotXScale
+ else:
+ Xc = startX + \
+ (result['cM'] - self.qtlresults[0]
+ ['cM']) * plotXScale
+ BootChrCoord.append([Xc, self.bootResult[i]])
+ BootCoord = [BootChrCoord]
+
+ # reduce bootResult
+ if self.selectedChr > -1:
+ maxBootBar = 80.0
+ else:
+ maxBootBar = 200.0
+ stepBootStrap = plotWidth / maxBootBar
+ reducedBootCoord = []
+ maxBootCount = 0
+
+ for BootChrCoord in BootCoord:
+ nBoot = len(BootChrCoord)
+ bootStartPixX = BootChrCoord[0][0]
+ bootCount = BootChrCoord[0][1]
+ for i in range(1, nBoot):
+ if BootChrCoord[i][0] - bootStartPixX < stepBootStrap:
+ bootCount += BootChrCoord[i][1]
+ continue
+ else:
+ if maxBootCount < bootCount:
+ maxBootCount = bootCount
+ # end if
+ reducedBootCoord.append(
+ [bootStartPixX, BootChrCoord[i][0], bootCount])
+ bootStartPixX = BootChrCoord[i][0]
+ bootCount = BootChrCoord[i][1]
+ # end else
+ # end for
+ # add last piece
+ if BootChrCoord[-1][0] - bootStartPixX > stepBootStrap / 2.0:
+ reducedBootCoord.append(
+ [bootStartPixX, BootChrCoord[-1][0], bootCount])
+ else:
+ reducedBootCoord[-1][2] += bootCount
+ reducedBootCoord[-1][1] = BootChrCoord[-1][0]
+ # end else
+ if maxBootCount < reducedBootCoord[-1][2]:
+ maxBootCount = reducedBootCoord[-1][2]
+ # end if
+ for item in reducedBootCoord:
+ if item[2] > 0:
+ if item[0] < xLeftOffset:
+ item[0] = xLeftOffset
+ if item[0] > xLeftOffset + plotWidth:
+ item[0] = xLeftOffset + plotWidth
+ if item[1] < xLeftOffset:
+ item[1] = xLeftOffset
+ if item[1] > xLeftOffset + plotWidth:
+ item[1] = xLeftOffset + plotWidth
+ if item[0] != item[1]:
+ im_drawer.rectangle(
+ xy=((item[0], yZero),
+ (item[1], yZero - item[2] * bootHeightThresh / maxBootCount)),
+ fill=self.BOOTSTRAP_BOX_COLOR, outline=BLACK)
+
+ if maxBootCount == 0:
+ return
+
+ # draw boot scale
+ highestPercent = (maxBootCount * 100.0) / nboot
+ bootScale = Plot.detScale(0, highestPercent)
+ bootScale = Plot.frange(
+ bootScale[0], bootScale[1], bootScale[1] / bootScale[2])
+ bootScale = bootScale[:-1] + [highestPercent]
+
+ bootOffset = 50 * fontZoom
+ bootScaleFont = ImageFont.truetype(
+ font=VERDANA_FILE, size=13 * fontZoom)
+ im_drawer.rectangle(
+ xy=((canvas.size[0] - bootOffset, yZero - bootHeightThresh),
+ (canvas.size[0] - bootOffset - 15 * zoom, yZero)),
+ fill=YELLOW, outline=BLACK)
+ im_drawer.line(
+ xy=((canvas.size[0] - bootOffset + 4, yZero),
+ (canvas.size[0] - bootOffset, yZero)),
+ fill=BLACK)
+ TEXT_Y_DISPLACEMENT = -8
+ im_drawer.text(xy=(canvas.size[0] - bootOffset + 10, yZero + TEXT_Y_DISPLACEMENT), text='0%',
+ font=bootScaleFont, fill=BLACK)
+
+ for item in bootScale:
+ if item == 0:
+ continue
+ bootY = yZero - bootHeightThresh * item / highestPercent
+ im_drawer.line(
+ xy=((canvas.size[0] - bootOffset + 4, bootY),
+ (canvas.size[0] - bootOffset, bootY)),
+ fill=BLACK)
+ im_drawer.text(xy=(canvas.size[0] - bootOffset + 10, bootY + TEXT_Y_DISPLACEMENT),
+ text='%2.1f' % item, font=bootScaleFont, fill=BLACK)
+
+ if self.legendChecked:
+ if hasattr(self.traitList[0], 'chr') and hasattr(self.traitList[0], 'mb'):
+ startPosY = 30
+ else:
+ startPosY = 15
+ smallLabelFont = ImageFont.truetype(
+ font=TREBUC_FILE, size=12 * fontZoom)
+ leftOffset = canvas.size[0] - xRightOffset - 190
+ im_drawer.rectangle(
+ xy=((leftOffset, startPosY - 6),
+ (leftOffset + 12, startPosY + 6)),
+ fill=YELLOW, outline=BLACK)
+ im_drawer.text(xy=(canvas.size[0] - xRightOffset - 170, startPosY + TEXT_Y_DISPLACEMENT),
+ text='Frequency of the Peak LRS',
+ font=smallLabelFont, fill=BLACK)
+
+ def drawProbeSetPosition(self, canvas, plotXScale, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
+ im_drawer = ImageDraw.Draw(canvas)
+ if len(self.traitList) != 1:
+ return
+
+ xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
+ plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
+ plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
+ yZero = canvas.size[1] - yBottomOffset
+ fontZoom = zoom
+ if zoom == 2:
+ fontZoom = 1.5
+
+ try:
+ Chr = self.traitList[0].chr
+ Mb = self.traitList[0].mb
+ except:
+ return
+
+ previous_chr = 1
+ previous_chr_as_int = 0
+ if self.plotScale == "physic":
+ this_chr = str(self.ChrList[self.selectedChr][0])
+ else:
+ this_chr = str(self.ChrList[self.selectedChr][1] + 1)
+
+ if self.plotScale == 'physic':
+ if self.selectedChr > -1:
+ if this_chr != Chr or Mb < self.startMb or Mb > self.endMb:
+ return
+ else:
+ locPixel = xLeftOffset + (Mb - self.startMb) * plotXScale
+ else:
+ locPixel = xLeftOffset
+ for i, _chr in enumerate(self.ChrList[1:]):
+ if _chr[0] != Chr:
+ locPixel += (self.ChrLengthDistList[i] + \
+ self.GraphInterval) * plotXScale
+ else:
+ locPixel += Mb * plotXScale
+ break
+ else:
+ if self.selectedChr > -1:
+ for i, qtlresult in enumerate(self.qtlresults):
+ if qtlresult['chr'] != self.selectedChr:
+ continue
+
+ if i == 0 and qtlresult['Mb'] >= Mb:
+ locPixel = -1
+ break
+
+ # the trait's position is between two traits
+ if i > 0 and self.qtlresults[i - 1]['Mb'] < Mb and qtlresult['Mb'] >= Mb:
+ locPixel = xLeftOffset + plotXScale * (self.qtlresults[i - 1]['Mb'] + (qtlresult['Mb'] - self.qtlresults[i - 1]['Mb']) * (
+ Mb - self.qtlresults[i - 1]['Mb']) / (qtlresult['Mb'] - self.qtlresults[i - 1]['Mb']))
+ break
+
+ # the trait's position is on the right of the last genotype
+ if i == len(self.qtlresults) and Mb >= qtlresult['Mb']:
+ locPixel = -1
+ else:
+ locPixel = xLeftOffset
+ for i, _chr in enumerate(self.ChrList):
+ if i < (len(self.ChrList) - 1):
+ if _chr != Chr:
+ locPixel += (self.ChrLengthDistList[i] + \
+ self.GraphInterval) * plotXScale
+ else:
+ locPixel += (Mb * (_chr[-1].cM - _chr[0].cM) / \
+ self.ChrLengthCMList[i]) * plotXScale
+ break
+ if locPixel >= 0 and self.plotScale == 'physic':
+ traitPixel = ((locPixel, yZero), (locPixel - 7,
+ yZero + 14), (locPixel + 7, yZero + 14))
+ draw_open_polygon(canvas, xy=traitPixel, outline=BLACK,
+ fill=self.TRANSCRIPT_LOCATION_COLOR)
+
+ def drawSNPTrackNew(self, canvas, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
+ im_drawer = ImageDraw.Draw(canvas)
+ if self.plotScale != 'physic' or self.selectedChr == -1 or not self.diffCol:
+ return
+
+ SNP_HEIGHT_MODIFIER = 18.0
+
+ xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
+ plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
+ plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
+ yZero = canvas.size[1] - yBottomOffset
+ fontZoom = zoom
+ if zoom == 2:
+ fontZoom = 1.5
+
+ drawSNPLocationY = yTopOffset + plotHeight
+ #chrName = self.genotype[0].name
+ chrName = self.ChrList[self.selectedChr][0]
+
+ stepMb = (endMb - startMb) / plotWidth
+ strainId1, strainId2 = self.diffCol
+ SNPCounts = []
+
+ while startMb < endMb:
+ with database_connection(get_setting("SQL_URI")) as conn, conn.cursor() as cursor:
+ # snp count
+ cursor.execute("SELECT COUNT(*) FROM BXDSnpPosition "
+ "WHERE Chr = %s AND Mb >= %s AND Mb < %s AND "
+ "StrainId1 = %s AND StrainId2 = %s",
+ (chrName, f"{startMb:2.6f}",
+ f"{startMb + stepMb:2.6f}", strainId1, strainId2,))
+ SNPCounts.append(cursor.fetchone()[0])
+ startMb += stepMb
+
+ if (len(SNPCounts) > 0):
+ maxCount = max(SNPCounts)
+ if maxCount > 0:
+ for i in range(xLeftOffset, xLeftOffset + plotWidth):
+ snpDensity = float(
+ SNPCounts[i - xLeftOffset] * SNP_HEIGHT_MODIFIER / maxCount)
+ im_drawer.line(
+ xy=((i, drawSNPLocationY + (snpDensity) * zoom),
+ (i, drawSNPLocationY - (snpDensity) * zoom)),
+ fill=self.SNP_COLOR, width=1)
+
+ def drawMultiTraitName(self, fd, canvas, gifmap, showLocusForm, offset=(40, 120, 80, 10), zoom=1):
+ nameWidths = []
+ yPaddingTop = 10
+ colorFont = ImageFont.truetype(font=TREBUC_FILE, size=12)
+ if len(self.qtlresults) > 20 and self.selectedChr > -1:
+ rightShift = 20
+ rightShiftStep = 60
+ rectWidth = 10
+ else:
+ rightShift = 40
+ rightShiftStep = 80
+ rectWidth = 15
+
+ for k, thisTrait in enumerate(self.traitList):
+ thisLRSColor = self.colorCollection[k]
+ kstep = k % 4
+ if k != 0 and kstep == 0:
+ if nameWidths:
+ rightShiftStep = max(nameWidths[-4:]) + rectWidth + 20
+ rightShift += rightShiftStep
+
+ name = thisTrait.displayName()
+ nameWidth, nameHeight = im_drawer.textsize(name, font=colorFont)
+ nameWidths.append(nameWidth)
+
+ im_drawer.rectangle(
+ xy=((rightShift, yPaddingTop + kstep * 15),
+ (rectWidth + rightShift, yPaddingTop + 10 + kstep * 15)),
+ fill=thisLRSColor, outline=BLACK)
+ im_drawer.text(
+ text=name, xy=(rectWidth + 2 + rightShift,
+ yPaddingTop + 10 + kstep * 15),
+ font=colorFont, fill=BLACK)
+ if thisTrait.db:
+ COORDS = "%d,%d,%d,%d" % (rectWidth + 2 + rightShift, yPaddingTop + kstep * \
+ 15, rectWidth + 2 + rightShift + nameWidth, yPaddingTop + 10 + kstep * 15,)
+ HREF = "javascript:showDatabase3('%s','%s','%s','');" % (
+ showLocusForm, thisTrait.db.name, thisTrait.name)
+ Areas = HtmlGenWrapper.create_area_tag(
+ shape='rect', coords=COORDS, href=HREF)
+ gifmap.append(Areas) # TODO
+
+ def drawLegendPanel(self, canvas, offset=(40, 120, 80, 10), zoom=1):
+ im_drawer = ImageDraw.Draw(canvas)
+ xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
+ plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
+ plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
+ yZero = canvas.size[1] - yBottomOffset
+ TEXT_Y_DISPLACEMENT = -8
+ fontZoom = zoom
+ if zoom == 2:
+ fontZoom = 1.5
+
+ labelFont = ImageFont.truetype(font=TREBUC_FILE, size=12 * fontZoom)
+ startPosY = 15
+ stepPosY = 12 * fontZoom
+
+ startPosX = canvas.size[0] - xRightOffset - 415
+ if hasattr(self.traitList[0], 'chr') and hasattr(self.traitList[0], 'mb'):
+ startPosY = 15
+ nCol = 2
+ smallLabelFont = ImageFont.truetype(
+ font=TREBUC_FILE, size=12 * fontZoom)
+
+ leftOffset = canvas.size[0] - xRightOffset - 190
+ draw_open_polygon(
+ canvas,
+ xy=(
+ (leftOffset + 6, startPosY - 7),
+ (leftOffset - 1, startPosY + 7),
+ (leftOffset + 13, startPosY + 7)),
+ outline=BLACK, fill=self.TRANSCRIPT_LOCATION_COLOR
+ )
+ TEXT_Y_DISPLACEMENT = -8
+ im_drawer.text(
+ text="Sequence Site",
+ xy=(leftOffset + 20, startPosY + TEXT_Y_DISPLACEMENT), font=smallLabelFont,
+ fill=self.TOP_RIGHT_INFO_COLOR)
+
+ if self.manhattan_plot != True:
+ im_drawer.line(
+ xy=((startPosX, startPosY), (startPosX + 32, startPosY)),
+ fill=self.LRS_COLOR, width=2)
+ im_drawer.text(
+ text=self.LRS_LOD, xy=(
+ startPosX + 40, startPosY + TEXT_Y_DISPLACEMENT),
+ font=labelFont, fill=BLACK)
+ startPosY += stepPosY
+
+ if self.additiveChecked:
+ im_drawer.line(
+ xy=((startPosX, startPosY), (startPosX + 17, startPosY)),
+ fill=self.ADDITIVE_COLOR_POSITIVE, width=2)
+ im_drawer.line(
+ xy=((startPosX + 18, startPosY), (startPosX + 32, startPosY)),
+ fill=self.ADDITIVE_COLOR_NEGATIVE, width=2)
+ im_drawer.text(
+ text='Additive Effect', xy=(startPosX + 40, startPosY + TEXT_Y_DISPLACEMENT),
+ font=labelFont, fill=BLACK)
+ startPosY += stepPosY
+
+ if self.genotype.type == 'intercross' and self.dominanceChecked:
+ im_drawer.line(
+ xy=((startPosX, startPosY), (startPosX + 17, startPosY)),
+ fill=self.DOMINANCE_COLOR_POSITIVE, width=4)
+ im_drawer.line(
+ xy=((startPosX + 18, startPosY), (startPosX + 35, startPosY)),
+ fill=self.DOMINANCE_COLOR_NEGATIVE, width=4)
+ im_drawer.text(
+ text='Dominance Effect', xy=(startPosX + 42, startPosY + 5),
+ font=labelFont, fill=BLACK)
+ startPosY += stepPosY
+
+ if self.haplotypeAnalystChecked:
+ im_drawer.line(
+ xy=((startPosX - 34, startPosY), (startPosX - 17, startPosY)),
+ fill=self.HAPLOTYPE_POSITIVE, width=4)
+ im_drawer.line(
+ xy=((startPosX - 17, startPosY), (startPosX, startPosY)),
+ fill=self.HAPLOTYPE_NEGATIVE, width=4)
+ im_drawer.line(
+ xy=((startPosX, startPosY), (startPosX + 17, startPosY)),
+ fill=self.HAPLOTYPE_HETEROZYGOUS, width=4)
+ im_drawer.line(
+ xy=((startPosX + 17, startPosY), (startPosX + 34, startPosY)),
+ fill=self.HAPLOTYPE_RECOMBINATION, width=4)
+ im_drawer.text(
+ text='Haplotypes (Pat, Mat, Het, Unk)',
+ xy=(startPosX + 41, startPosY + TEXT_Y_DISPLACEMENT), font=labelFont, fill=BLACK)
+ startPosY += stepPosY
+
+ if self.permChecked and self.nperm > 0:
+ thisStartX = startPosX
+ if self.multipleInterval and not self.bootChecked:
+ thisStartX = canvas.size[0] - xRightOffset - 205
+ im_drawer.line(
+ xy=((thisStartX, startPosY), (startPosX + 32, startPosY)),
+ fill=self.SIGNIFICANT_COLOR, width=self.SIGNIFICANT_WIDTH)
+ im_drawer.line(
+ xy=((thisStartX, startPosY + stepPosY),
+ (startPosX + 32, startPosY + stepPosY)),
+ fill=self.SUGGESTIVE_COLOR, width=self.SUGGESTIVE_WIDTH)
+ im_drawer.text(
+ text='Significant %s = %2.2f' % (
+ self.LRS_LOD, self.significant),
+ xy=(thisStartX + 40, startPosY + TEXT_Y_DISPLACEMENT), font=labelFont, fill=BLACK)
+ im_drawer.text(
+ text='Suggestive %s = %2.2f' % (self.LRS_LOD, self.suggestive),
+ xy=(thisStartX + 40, startPosY + TEXT_Y_DISPLACEMENT + stepPosY), font=labelFont,
+ fill=BLACK)
+
+ labelFont = ImageFont.truetype(font=VERDANA_FILE, size=12 * fontZoom)
+ labelColor = BLACK
+
+ if self.dataset.type == "Publish" or self.dataset.type == "Geno":
+ dataset_label = self.dataset.fullname
+ else:
+ dataset_label = "%s - %s" % (self.dataset.group.name,
+ self.dataset.fullname)
+
+
+ self.current_datetime = datetime.datetime.now().strftime("%b %d %Y %H:%M:%S")
+ string1 = 'UTC Timestamp: %s' % (self.current_datetime)
+ string2 = 'Dataset: %s' % (dataset_label)
+ string3 = 'Trait Hash: %s' % (self.vals_hash)
+
+ if self.genofile_string == "":
+ string4 = 'Genotype File: %s.geno' % self.dataset.group.name
+ else:
+ string4 = 'Genotype File: %s' % self.genofile_string.split(":")[1]
+
+ string6 = ''
+ if self.mapping_method == "gemma" or self.mapping_method == "gemma_bimbam":
+ if self.use_loco == "True":
+ string5 = 'Using GEMMA mapping method with LOCO and '
+ else:
+ string5 = 'Using GEMMA mapping method with '
+ if self.covariates != "":
+ string5 += 'the cofactors below:'
+ cofactor_names = ", ".join(
+ [covar.split(":")[0] for covar in self.covariates.split(",")])
+ string6 = cofactor_names
+ else:
+ string5 += 'no cofactors'
+ elif self.mapping_method == "rqtl_plink" or self.mapping_method == "rqtl_geno":
+ string5 = 'Using R/qtl mapping method with '
+ if self.covariates != "":
+ string5 += 'the cofactors below:'
+ cofactor_names = ", ".join(
+ [covar.split(":")[0] for covar in self.covariates.split(",")])
+ string6 = cofactor_names
+ elif self.controlLocus and self.doControl != "false":
+ string5 += '%s as control' % self.controlLocus
+ else:
+ string5 += 'no cofactors'
+ else:
+ string5 = 'Using Haldane mapping function with '
+ if self.controlLocus and self.doControl != "false":
+ string5 += '%s as control' % self.controlLocus
+ else:
+ string5 += 'no control for other QTLs'
+
+ y_constant = 10
+ if self.this_trait.name:
+ if self.selectedChr == -1:
+ identification = "Mapping on All Chromosomes for "
+ else:
+ identification = "Mapping on Chromosome %s for " % (
+ self.ChrList[self.selectedChr][0])
+
+ if self.this_trait.symbol:
+ identification += "Trait: %s - %s" % (
+ self.this_trait.display_name, self.this_trait.symbol)
+ elif self.dataset.type == "Publish":
+ if self.this_trait.post_publication_abbreviation:
+ identification += "Trait: %s - %s" % (
+ self.this_trait.display_name, self.this_trait.post_publication_abbreviation)
+ elif self.this_trait.pre_publication_abbreviation:
+ identification += "Trait: %s - %s" % (
+ self.this_trait.display_name, self.this_trait.pre_publication_abbreviation)
+ else:
+ identification += "Trait: %s" % (self.this_trait.display_name)
+ else:
+ identification += "Trait: %s" % (self.this_trait.display_name)
+ identification += " with %s samples" % (self.n_samples)
+
+ d = 4 + max(
+ im_drawer.textsize(identification, font=labelFont)[0],
+ im_drawer.textsize(string1, font=labelFont)[0],
+ im_drawer.textsize(string2, font=labelFont)[0],
+ im_drawer.textsize(string3, font=labelFont)[0],
+ im_drawer.textsize(string4, font=labelFont)[0])
+ im_drawer.text(
+ text=identification,
+ xy=(xLeftOffset, y_constant * fontZoom), font=labelFont,
+ fill=labelColor)
+ y_constant += 15
+ else:
+ d = 4 + max(
+ im_drawer.textsize(string1, font=labelFont)[0],
+ im_drawer.textsize(string2, font=labelFont)[0],
+ im_drawer.textsize(string3, font=labelFont)[0],
+ im_drawer.textsize(string4, font=labelFont)[0])
+
+ if len(self.transform) > 0:
+ transform_text = "Transform - "
+ if self.transform == "qnorm":
+ transform_text += "Quantile Normalized"
+ elif self.transform == "log2" or self.transform == "log10":
+ transform_text += self.transform.capitalize()
+ elif self.transform == "sqrt":
+ transform_text += "Square Root"
+ elif self.transform == "zscore":
+ transform_text += "Z-Score"
+ elif self.transform == "invert":
+ transform_text += "Invert +/-"
+
+ im_drawer.text(
+ text=transform_text, xy=(xLeftOffset, y_constant * fontZoom),
+ font=labelFont, fill=labelColor)
+ y_constant += 15
+ im_drawer.text(
+ text=string1, xy=(xLeftOffset, y_constant * fontZoom),
+ font=labelFont, fill=labelColor)
+ y_constant += 15
+ im_drawer.text(
+ text=string2, xy=(xLeftOffset, y_constant * fontZoom),
+ font=labelFont, fill=labelColor)
+ y_constant += 15
+ im_drawer.text(
+ text=string3, xy=(xLeftOffset, y_constant * fontZoom),
+ font=labelFont, fill=labelColor)
+ y_constant += 15
+ im_drawer.text(
+ text=string4, xy=(xLeftOffset, y_constant * fontZoom),
+ font=labelFont, fill=labelColor)
+ y_constant += 15
+ if string4 != '':
+ im_drawer.text(
+ text=string5, xy=(xLeftOffset, y_constant * fontZoom),
+ font=labelFont, fill=labelColor)
+ y_constant += 15
+ if string5 != '':
+ im_drawer.text(
+ text=string6, xy=(xLeftOffset, y_constant * fontZoom),
+ font=labelFont, fill=labelColor)
+
+ def drawHomologyBand(self, canvas, gifmap, plotXScale, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
+ im_drawer = ImageDraw.Draw(canvas)
+ if self.plotScale != 'physic' or self.selectedChr == -1 or not self.homology:
+ return
+
+ xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
+ plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
+ plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
+ yZero = canvas.size[1] - yBottomOffset
+ fontZoom = zoom
+ if zoom == 2:
+ fontZoom = 1.5
+
+ yPaddingTop = yTopOffset
+
+ for index, homology_dict in enumerate(self.homology):
+ ref_strand = homology_dict["ref_strand"]
+ ref_start = homology_dict["ref_start"]
+ ref_end = homology_dict["ref_end"]
+ query_chr = homology_dict["query_chr"]
+ query_strand = homology_dict["query_strand"]
+ query_start = homology_dict["query_start"]
+ query_end = homology_dict["query_end"]
+
+ geneLength = (ref_end - ref_start) * 1000.0
+ tenPercentLength = geneLength * 0.0001
+
+ geneStartPix = xLeftOffset + \
+ plotXScale * (float(ref_start) - startMb)
+ geneEndPix = xLeftOffset + plotXScale * \
+ (float(ref_end) - startMb) # at least one pixel
+
+ if (geneEndPix < xLeftOffset):
+ return # this gene is not on the screen
+ if (geneEndPix > xLeftOffset + plotWidth):
+ geneEndPix = xLeftOffset + plotWidth # clip the last in-range gene
+ if (geneStartPix > xLeftOffset + plotWidth):
+ return # we are outside the valid on-screen range, so stop drawing genes
+ elif (geneStartPix < xLeftOffset):
+ geneStartPix = xLeftOffset # clip the first in-range gene
+
+ myColor = BLACK
+
+ outlineColor = myColor
+ fillColor = myColor
+
+ TITLE = f"hg38: Chr {query_chr} from {query_start:.3f} to {query_end:.3f} Mb"
+ HREF = f"http://genome.ucsc.edu/cgi-bin/hgTracks?db=hg38&position=chr{query_chr}:{int(query_start * 1000000)}-{int(query_end * 1000000)}"
+
+ # Draw Genes
+ geneYLocation = yPaddingTop + \
+ (index % self.NUM_GENE_ROWS) * self.EACH_GENE_HEIGHT * zoom
+ if self.dataset.group.species == "mouse" or self.dataset.group.species == "rat":
+ geneYLocation += 4 * self.BAND_HEIGHT + 4 * self.BAND_SPACING
+ else:
+ geneYLocation += 3 * self.BAND_HEIGHT + 3 * self.BAND_SPACING
+
+ # draw the detail view
+ utrColor = ImageColor.getrgb("rgb(66%, 66%, 66%)")
+ arrowColor = ImageColor.getrgb("rgb(70%, 70%, 70%)")
+
+ # draw the line that runs the entire length of the gene
+ im_drawer.line(
+ xy=(
+ (geneStartPix, geneYLocation + \
+ self.EACH_GENE_HEIGHT / 2 * zoom),
+ (geneEndPix, geneYLocation + self.EACH_GENE_HEIGHT / 2 * zoom)),
+ fill=outlineColor, width=1)
+
+ # draw the arrows
+ if geneEndPix - geneStartPix < 1:
+ genePixRange = 1
+ else:
+ genePixRange = int(geneEndPix - geneStartPix)
+ for xCoord in range(0, genePixRange):
+
+ if (xCoord % self.EACH_GENE_ARROW_SPACING == 0 and xCoord + self.EACH_GENE_ARROW_SPACING < geneEndPix - geneStartPix) or xCoord == 0:
+ if query_strand == "+":
+ im_drawer.line(
+ xy=((geneStartPix + xCoord, geneYLocation),
+ (geneStartPix + xCoord + self.EACH_GENE_ARROW_WIDTH,
+ geneYLocation + (self.EACH_GENE_HEIGHT / 2) * zoom)),
+ fill=arrowColor, width=1)
+ im_drawer.line(
+ xy=((geneStartPix + xCoord,
+ geneYLocation + self.EACH_GENE_HEIGHT * zoom),
+ (geneStartPix + xCoord + self.EACH_GENE_ARROW_WIDTH,
+ geneYLocation + (self.EACH_GENE_HEIGHT / 2) * zoom)),
+ fill=arrowColor, width=1)
+ else:
+ im_drawer.line(
+ xy=((geneStartPix + xCoord + self.EACH_GENE_ARROW_WIDTH,
+ geneYLocation),
+ (geneStartPix + xCoord,
+ geneYLocation + (self.EACH_GENE_HEIGHT / 2) * zoom)),
+ fill=arrowColor, width=1)
+ im_drawer.line(
+ xy=((geneStartPix + xCoord + self.EACH_GENE_ARROW_WIDTH,
+ geneYLocation + self.EACH_GENE_HEIGHT * zoom),
+ (geneStartPix + xCoord,
+ geneYLocation + (self.EACH_GENE_HEIGHT / 2) * zoom)),
+ fill=arrowColor, width=1)
+
+ COORDS = "%d, %d, %d, %d" % (
+ geneStartPix, geneYLocation, geneEndPix, (geneYLocation + self.EACH_GENE_HEIGHT))
+
+ gifmap.append(
+ HtmlGenWrapper.create_area_tag(
+ shape='rect',
+ coords=COORDS,
+ href=HREF,
+ title=TITLE,
+ target="_blank"))
+
+ def drawGeneBand(self, canvas, gifmap, plotXScale, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
+ im_drawer = ImageDraw.Draw(canvas)
+ if self.plotScale != 'physic' or self.selectedChr == -1 or not self.geneCol:
+ return
+
+ xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
+ plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
+ plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
+ yZero = canvas.size[1] - yBottomOffset
+ fontZoom = zoom
+ if zoom == 2:
+ fontZoom = 1.5
+
+ yPaddingTop = yTopOffset
+
+ for gIndex, theGO in enumerate(self.geneCol):
+ geneNCBILink = 'http://www.ncbi.nlm.nih.gov/gene?term=%s'
+ if self.dataset.group.species == "mouse":
+ exonStarts = []
+ exonEnds = []
+ txStart = theGO["TxStart"]
+ txEnd = theGO["TxEnd"]
+ geneLength = (txEnd - txStart) * 1000.0
+ tenPercentLength = geneLength * 0.0001
+ SNPdensity = theGO["snpCount"] / geneLength
+
+ if theGO['exonStarts']:
+ exonStarts = list(
+ map(float, theGO['exonStarts'].split(",")[:-1]))
+ exonEnds = list(map(float, theGO['exonEnds'].split(",")[:-1]))
+ cdsStart = theGO['cdsStart']
+ cdsEnd = theGO['cdsEnd']
+ accession = theGO['NM_ID']
+ geneSymbol = theGO["GeneSymbol"]
+ strand = theGO["Strand"]
+ exonCount = theGO["exonCount"]
+
+ geneStartPix = xLeftOffset + \
+ plotXScale * (float(txStart) - startMb)
+ geneEndPix = xLeftOffset + plotXScale * \
+ (float(txEnd) - startMb) # at least one pixel
+
+ if (geneEndPix < xLeftOffset):
+ return # this gene is not on the screen
+ elif (geneEndPix > xLeftOffset + plotWidth):
+ geneEndPix = xLeftOffset + plotWidth # clip the last in-range gene
+ if (geneStartPix > xLeftOffset + plotWidth):
+ return # we are outside the valid on-screen range, so stop drawing genes
+ elif (geneStartPix < xLeftOffset):
+ geneStartPix = xLeftOffset # clip the first in-range gene
+
+ # color the gene based on SNP density
+ # found earlier, needs to be recomputed as snps are added
+ # always apply colors now, even if SNP Track not checked - Zach 11/24/2010
+
+ densities = [1.0000000000000001e-05, 0.094094033555233408,
+ 0.3306166377816987, 0.88246026851027781, 2.6690084029581951, 4.1, 61.0]
+ if SNPdensity < densities[0]:
+ myColor = BLACK
+ elif SNPdensity < densities[1]:
+ myColor = PURPLE
+ elif SNPdensity < densities[2]:
+ myColor = DARKBLUE
+ elif SNPdensity < densities[3]:
+ myColor = DARKGREEN
+ elif SNPdensity < densities[4]:
+ myColor = GOLD
+ elif SNPdensity < densities[5]:
+ myColor = DARKORANGE
+ else:
+ myColor = DARKRED
+
+ outlineColor = myColor
+ fillColor = myColor
+
+ TITLE = "Gene: %s (%s)\nFrom %2.3f to %2.3f Mb (%s)\nNum. exons: %d." % (
+ geneSymbol, accession, float(txStart), float(txEnd), strand, exonCount)
+ # NL: 06-02-2011 Rob required to change this link for gene related
+ HREF = geneNCBILink % geneSymbol
+
+ elif self.dataset.group.species == "rat":
+ exonStarts = []
+ exonEnds = []
+ txStart = theGO["TxStart"]
+ txEnd = theGO["TxEnd"]
+ cdsStart = theGO["TxStart"]
+ cdsEnd = theGO["TxEnd"]
+ geneSymbol = theGO["GeneSymbol"]
+ strand = theGO["Strand"]
+ exonCount = 0
+
+ geneStartPix = xLeftOffset + \
+ plotXScale * (float(txStart) - startMb)
+ geneEndPix = xLeftOffset + plotXScale * \
+ (float(txEnd) - startMb) # at least one pixel
+
+ if (geneEndPix < xLeftOffset):
+ return # this gene is not on the screen
+ elif (geneEndPix > xLeftOffset + plotWidth):
+ geneEndPix = xLeftOffset + plotWidth # clip the last in-range gene
+ if (geneStartPix > xLeftOffset + plotWidth):
+ return # we are outside the valid on-screen range, so stop drawing genes
+ elif (geneStartPix < xLeftOffset):
+ geneStartPix = xLeftOffset # clip the first in-range gene
+
+ outlineColor = DARKBLUE
+ fillColor = DARKBLUE
+ TITLE = "Gene: %s\nFrom %2.3f to %2.3f Mb (%s)" % (
+ geneSymbol, float(txStart), float(txEnd), strand)
+ # NL: 06-02-2011 Rob required to change this link for gene related
+ HREF = geneNCBILink % geneSymbol
+ else:
+ outlineColor = ORANGE
+ fillColor = ORANGE
+ TITLE = "Gene: %s" % geneSymbol
+
+ # Draw Genes
+ geneYLocation = yPaddingTop + \
+ (gIndex % self.NUM_GENE_ROWS) * self.EACH_GENE_HEIGHT * zoom
+ if self.dataset.group.species == "mouse" or self.dataset.group.species == "rat":
+ geneYLocation += 4 * self.BAND_HEIGHT + 4 * self.BAND_SPACING
+ else:
+ geneYLocation += 3 * self.BAND_HEIGHT + 3 * self.BAND_SPACING
+
+ # draw the detail view
+ if self.endMb - self.startMb <= self.DRAW_DETAIL_MB and geneEndPix - geneStartPix > self.EACH_GENE_ARROW_SPACING * 3:
+ utrColor = ImageColor.getrgb("rgb(66%, 66%, 66%)")
+ arrowColor = ImageColor.getrgb("rgb(70%, 70%, 70%)")
+
+ # draw the line that runs the entire length of the gene
+ im_drawer.line(
+ xy=(
+ (geneStartPix, geneYLocation + \
+ self.EACH_GENE_HEIGHT / 2 * zoom),
+ (geneEndPix, geneYLocation + self.EACH_GENE_HEIGHT / 2 * zoom)),
+ fill=outlineColor, width=1)
+
+ # draw the arrows
+ if geneEndPix - geneStartPix < 1:
+ genePixRange = 1
+ else:
+ genePixRange = int(geneEndPix - geneStartPix)
+ for xCoord in range(0, genePixRange):
+
+ if (xCoord % self.EACH_GENE_ARROW_SPACING == 0 and xCoord + self.EACH_GENE_ARROW_SPACING < geneEndPix - geneStartPix) or xCoord == 0:
+ if strand == "+":
+ im_drawer.line(
+ xy=((geneStartPix + xCoord, geneYLocation),
+ (geneStartPix + xCoord + self.EACH_GENE_ARROW_WIDTH,
+ geneYLocation + (self.EACH_GENE_HEIGHT / 2) * zoom)),
+ fill=arrowColor, width=1)
+ im_drawer.line(
+ xy=((geneStartPix + xCoord,
+ geneYLocation + self.EACH_GENE_HEIGHT * zoom),
+ (geneStartPix + xCoord + self.EACH_GENE_ARROW_WIDTH,
+ geneYLocation + (self.EACH_GENE_HEIGHT / 2) * zoom)),
+ fill=arrowColor, width=1)
+ else:
+ im_drawer.line(
+ xy=((geneStartPix + xCoord + self.EACH_GENE_ARROW_WIDTH,
+ geneYLocation),
+ (geneStartPix + xCoord,
+ geneYLocation + (self.EACH_GENE_HEIGHT / 2) * zoom)),
+ fill=arrowColor, width=1)
+ im_drawer.line(
+ xy=((geneStartPix + xCoord + self.EACH_GENE_ARROW_WIDTH,
+ geneYLocation + self.EACH_GENE_HEIGHT * zoom),
+ (geneStartPix + xCoord,
+ geneYLocation + (self.EACH_GENE_HEIGHT / 2) * zoom)),
+ fill=arrowColor, width=1)
+
+ # draw the blocks for the exon regions
+ for i in range(0, len(exonStarts)):
+ exonStartPix = (
+ exonStarts[i] - startMb) * plotXScale + xLeftOffset
+ exonEndPix = (exonEnds[i] - startMb) * \
+ plotXScale + xLeftOffset
+ if (exonStartPix < xLeftOffset):
+ exonStartPix = xLeftOffset
+ if (exonEndPix < xLeftOffset):
+ exonEndPix = xLeftOffset
+ if (exonEndPix > xLeftOffset + plotWidth):
+ exonEndPix = xLeftOffset + plotWidth
+ if (exonStartPix > xLeftOffset + plotWidth):
+ exonStartPix = xLeftOffset + plotWidth
+ im_drawer.rectangle(
+ xy=((exonStartPix, geneYLocation),
+ (exonEndPix, (geneYLocation + self.EACH_GENE_HEIGHT * zoom))),
+ outline=outlineColor, fill=fillColor)
+
+ # draw gray blocks for 3' and 5' UTR blocks
+ if cdsStart and cdsEnd:
+ utrStartPix = (txStart - startMb) * \
+ plotXScale + xLeftOffset
+ utrEndPix = (cdsStart - startMb) * plotXScale + xLeftOffset
+ if (utrStartPix < xLeftOffset):
+ utrStartPix = xLeftOffset
+ if (utrEndPix < xLeftOffset):
+ utrEndPix = xLeftOffset
+ if (utrEndPix > xLeftOffset + plotWidth):
+ utrEndPix = xLeftOffset + plotWidth
+ if (utrStartPix > xLeftOffset + plotWidth):
+ utrStartPix = xLeftOffset + plotWidth
+
+ if self.endMb - self.startMb <= self.DRAW_UTR_LABELS_MB:
+ if strand == "-":
+ labelText = "3'"
+ else:
+ labelText = "5'"
+ im_drawer.text(
+ text=labelText,
+ xy=(utrStartPix - 9, geneYLocation + \
+ self.EACH_GENE_HEIGHT),
+ font=ImageFont.truetype(font=ARIAL_FILE, size=2))
+
+ # the second UTR region
+
+ utrStartPix = (cdsEnd - startMb) * plotXScale + xLeftOffset
+ utrEndPix = (txEnd - startMb) * plotXScale + xLeftOffset
+ if (utrStartPix < xLeftOffset):
+ utrStartPix = xLeftOffset
+ if (utrEndPix < xLeftOffset):
+ utrEndPix = xLeftOffset
+ if (utrEndPix > xLeftOffset + plotWidth):
+ utrEndPix = xLeftOffset + plotWidth
+ if (utrStartPix > xLeftOffset + plotWidth):
+ utrStartPix = xLeftOffset + plotWidth
+
+ if self.endMb - self.startMb <= self.DRAW_UTR_LABELS_MB:
+ if strand == "-":
+ labelText = "5'"
+ else:
+ labelText = "3'"
+ im_drawer.text(
+ text=labelText,
+ xy=(utrEndPix + 2, geneYLocation + \
+ self.EACH_GENE_HEIGHT),
+ font=ImageFont.truetype(font=ARIAL_FILE, size=2))
+
+ # draw the genes as rectangles
+ else:
+ im_drawer.rectangle(
+ xy=((geneStartPix, geneYLocation),
+ (geneEndPix, (geneYLocation + self.EACH_GENE_HEIGHT * zoom))),
+ outline=outlineColor, fill=fillColor)
+
+ COORDS = "%d, %d, %d, %d" % (
+ geneStartPix, geneYLocation, geneEndPix, (geneYLocation + self.EACH_GENE_HEIGHT))
+ # NL: 06-02-2011 Rob required to display NCBI info in a new window
+ gifmap.append(
+ HtmlGenWrapper.create_area_tag(
+ shape='rect',
+ coords=COORDS,
+ href=HREF,
+ title=TITLE,
+ target="_blank"))
+
+# BEGIN HaplotypeAnalyst
+ def drawHaplotypeBand(self, canvas, gifmap, plotXScale, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
+ if self.plotScale != 'physic' or self.selectedChr == -1 or not self.geneCol:
+ return
+
+ xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
+ plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
+
+ yPaddingTop = yTopOffset
+
+ thisTrait = self.this_trait
+
+ samplelist = list(self.genotype.prgy)
+
+ smd = []
+ for sample in self.sample_vals_dict.keys():
+ if self.sample_vals_dict[sample] != "x" and sample in samplelist:
+ temp = GeneralObject(name=sample, value=float(
+ self.sample_vals_dict[sample]))
+ smd.append(temp)
+ else:
+ continue
+
+ smd.sort(key=lambda A: A.value)
+ smd.reverse()
+
+ oldgeneEndPix = -1
+ # Initializing plotRight, error before
+ plotRight = xRightOffset
+
+ im_drawer = ImageDraw.Draw(canvas)
+
+# find out PlotRight
+ for _chr in self.genotype:
+ if _chr.name == self.ChrList[self.selectedChr][0]:
+ for i, _locus in enumerate(_chr):
+ txStart = _chr[i].Mb
+ txEnd = _chr[i].Mb
+
+ geneStartPix = xLeftOffset + plotXScale * \
+ (float(txStart) - startMb) - 0
+ geneEndPix = xLeftOffset + plotXScale * \
+ (float(txEnd) - startMb) - 0
+
+ drawit = 1
+ if (geneStartPix < xLeftOffset):
+ drawit = 0
+ if (geneStartPix > xLeftOffset + plotWidth):
+ drawit = 0
+
+ if drawit == 1:
+ if _chr[i].name != " - ":
+ plotRight = geneEndPix + 4
+
+# end find out PlotRight
+
+ firstGene = 1
+ lastGene = 0
+
+ # Sets the length to the length of the strain list. Beforehand, "oldgeno = self.genotype[0][i].genotype"
+ # was the only place it was initialized, which worked as long as the very start (startMb = None/0) wasn't being mapped.
+ # Now there should always be some value set for "oldgeno" - Zach 12/14/2010
+ oldgeno = [None] * len(self.strainlist)
+
+ for i, _chr in enumerate(self.genotype):
+ if _chr.name == self.ChrList[self.selectedChr][0]:
+ for j, _locus in enumerate(_chr):
+ txStart = _chr[j].Mb
+ txEnd = _chr[j].Mb
+
+ geneStartPix = xLeftOffset + plotXScale * \
+ (float(txStart) - startMb) - 0
+ geneEndPix = xLeftOffset + plotXScale * \
+ (float(txEnd) - startMb) + 0
+
+ if oldgeneEndPix >= xLeftOffset:
+ drawStart = oldgeneEndPix + 4
+ else:
+ drawStart = xLeftOffset + 3
+
+ drawEnd = plotRight - 9
+
+ drawit = 1
+
+ if (geneStartPix < xLeftOffset):
+ if firstGene == 1:
+ drawit = 1
+ else:
+ drawit = 0
+
+ elif (geneStartPix > (xLeftOffset + plotWidth - 3)):
+ if lastGene == 0:
+ drawit = 1
+ drawEnd = xLeftOffset + plotWidth - 6
+ lastGene = 1
+ else:
+ break
+
+ else:
+ firstGene = 0
+ drawit = 1
+
+ if drawit == 1:
+ myColor = DARKBLUE
+ outlineColor = myColor
+ fillColor = myColor
+
+ maxind = 0
+
+ # Draw Genes
+
+ geneYLocation = yPaddingTop + self.NUM_GENE_ROWS * \
+ (self.EACH_GENE_HEIGHT) * zoom
+ if self.dataset.group.species == "mouse" or self.dataset.group.species == "rat":
+ geneYLocation += 4 * self.BAND_HEIGHT + 4 * self.BAND_SPACING
+ else:
+ geneYLocation += 3 * self.BAND_HEIGHT + 3 * self.BAND_SPACING
+
+ if _chr[j].name != " - ":
+
+ if (firstGene == 1) and (lastGene != 1):
+ oldgeneEndPix = drawStart = xLeftOffset
+ oldgeno = _chr[j].genotype
+ continue
+
+ for k, _geno in enumerate(_chr[j].genotype):
+ plotbxd = 0
+ if samplelist[k] in [item.name for item in smd]:
+ plotbxd = 1
+
+ if (plotbxd == 1):
+ ind = 0
+ if samplelist[k] in [item.name for item in smd]:
+ ind = [item.name for item in smd].index(
+ samplelist[k])
+
+ maxind = max(ind, maxind)
+
+ # lines
+ if (oldgeno[k] == -1 and _geno == -1):
+ mylineColor = self.HAPLOTYPE_NEGATIVE
+ elif (oldgeno[k] == 1 and _geno == 1):
+ mylineColor = self.HAPLOTYPE_POSITIVE
+ elif (oldgeno[k] == 0 and _geno == 0):
+ mylineColor = self.HAPLOTYPE_HETEROZYGOUS
+ else:
+ mylineColor = self.HAPLOTYPE_RECOMBINATION # XZ: Unknown
+
+ im_drawer.line(
+ xy=((drawStart,
+ geneYLocation + 7 + 2 * ind * self.EACH_GENE_HEIGHT * zoom),
+ (drawEnd,
+ geneYLocation + 7 + 2 * ind * self.EACH_GENE_HEIGHT * zoom)),
+ fill=mylineColor, width=zoom * (self.EACH_GENE_HEIGHT + 2))
+
+ fillColor = BLACK
+ outlineColor = BLACK
+ if lastGene == 0:
+ im_drawer.rectangle(
+ xy=((geneStartPix,
+ geneYLocation + 2 * ind * self.EACH_GENE_HEIGHT * zoom),
+ (geneEndPix,
+ geneYLocation + 2 * ind * self.EACH_GENE_HEIGHT + 2 * self.EACH_GENE_HEIGHT * zoom)),
+ outline=outlineColor, fill=fillColor)
+
+ COORDS = "%d, %d, %d, %d" % (
+ geneStartPix, geneYLocation + ind * self.EACH_GENE_HEIGHT, geneEndPix + 1, (geneYLocation + ind * self.EACH_GENE_HEIGHT))
+ TITLE = "Strain: %s, marker (%s) \n Position %2.3f Mb." % (
+ samplelist[k], _chr[j].name, float(txStart))
+ HREF = ''
+ gifmap.append(
+ HtmlGenWrapper.create_area_tag(
+ shape='rect',
+ coords=COORDS,
+ href=HREF,
+ title=TITLE))
+
+ # if there are no more markers in a chromosome, the plotRight value calculated above will be before the plotWidth
+ # resulting in some empty space on the right side of the plot area. This draws an "unknown" bar from plotRight to the edge.
+ if (plotRight < (xLeftOffset + plotWidth - 3)) and (lastGene == 0):
+ drawEnd = xLeftOffset + plotWidth - 6
+ mylineColor = self.HAPLOTYPE_RECOMBINATION
+ im_drawer.line(
+ xy=((plotRight,
+ geneYLocation + 7 + 2 * ind * self.EACH_GENE_HEIGHT * zoom),
+ (drawEnd,
+ geneYLocation + 7 + 2 * ind * self.EACH_GENE_HEIGHT * zoom)),
+ fill=mylineColor, width=zoom * (self.EACH_GENE_HEIGHT + 2))
+
+ if lastGene == 0:
+ draw_rotated_text(
+ canvas, text="%s" % (_chr[j].name),
+ font=ImageFont.truetype(font=VERDANA_FILE,
+ size=12),
+ xy=(geneStartPix,
+ geneYLocation + 17 + 2 * maxind * self.EACH_GENE_HEIGHT * zoom),
+ fill=BLACK, angle=-90)
+
+ oldgeneEndPix = geneEndPix
+ oldgeno = _chr[j].genotype
+ firstGene = 0
+ else:
+ lastGene = 0
+
+ for i, _chr in enumerate(self.genotype):
+ if _chr.name == self.ChrList[self.selectedChr][0]:
+ for j, _geno in enumerate(_chr[1].genotype):
+
+ plotbxd = 0
+ if samplelist[j] in [item.name for item in smd]:
+ plotbxd = 1
+
+ if (plotbxd == 1):
+ ind = [item.name for item in smd].index(
+ samplelist[j]) - 1
+ expr = smd[ind+1].value
+
+ # Place where font is hardcoded
+ im_drawer.text(
+ text="%s" % (samplelist[j]),
+ xy=((xLeftOffset + plotWidth + 10),
+ geneYLocation + 11 + 2 * ind * self.EACH_GENE_HEIGHT * zoom),
+ font=ImageFont.truetype(
+ font=VERDANA_FILE, size=12),
+ fill=BLACK)
+ im_drawer.text(
+ text="%2.2f" % (expr),
+ xy=((xLeftOffset + plotWidth + 60),
+ geneYLocation + 11 + 2 * ind * self.EACH_GENE_HEIGHT * zoom),
+ font=ImageFont.truetype(
+ font=VERDANA_FILE, size=12),
+ fill=BLACK)
+
+# END HaplotypeAnalyst
+
+ def drawClickBand(self, canvas, gifmap, plotXScale, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
+ im_drawer = ImageDraw.Draw(canvas)
+ if self.plotScale != 'physic' or self.selectedChr == -1:
+ return
+
+ xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
+ plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
+ plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
+ yZero = canvas.size[1] - yBottomOffset
+ fontZoom = zoom
+ if zoom == 2:
+ fontZoom = 1.5
+
+ # only draw this many clickable regions (if you set it higher, you get more precision in clicking,
+ # but it makes the HTML huge, and takes forever to render the page in the first place)
+ # Draw the bands that you can click on to go to UCSC / Ensembl
+ MAX_CLICKABLE_REGION_DIVISIONS = 100
+ clickableRegionLabelFont = ImageFont.truetype(
+ font=VERDANA_FILE, size=9)
+ pixelStep = max(
+ 5, int(float(plotWidth) / MAX_CLICKABLE_REGION_DIVISIONS))
+ # pixelStep: every N pixels, we make a new clickable area for the user to go to that area of the genome.
+
+ numBasesCurrentlyOnScreen = self.kONE_MILLION * \
+ abs(startMb - endMb) # Number of bases on screen now
+ flankingWidthInBases = int(
+ min((float(numBasesCurrentlyOnScreen) / 2.0), (5 * self.kONE_MILLION)))
+ webqtlZoomWidth = numBasesCurrentlyOnScreen / 16.0
+ # Flanking width should be such that we either zoom in to a 10 million base region, or we show the clicked region at the same scale as we are currently seeing.
+
+ currentChromosome = self.genotype[0].name
+ i = 0
+
+ paddingTop = yTopOffset
+ phenogenPaddingTop = paddingTop + \
+ (self.BAND_HEIGHT + self.BAND_SPACING)
+ if self.dataset.group.species == "mouse" or self.dataset.group.species == "rat":
+ ucscPaddingTop = paddingTop + 2 * \
+ (self.BAND_HEIGHT + self.BAND_SPACING)
+ ensemblPaddingTop = paddingTop + 3 * \
+ (self.BAND_HEIGHT + self.BAND_SPACING)
+ else:
+ ucscPaddingTop = paddingTop + \
+ (self.BAND_HEIGHT + self.BAND_SPACING)
+ ensemblPaddingTop = paddingTop + 2 * \
+ (self.BAND_HEIGHT + self.BAND_SPACING)
+
+ if zoom == 1:
+ for pixel in range(xLeftOffset, xLeftOffset + plotWidth, pixelStep):
+
+ calBase = self.kONE_MILLION * \
+ (startMb + (endMb - startMb) * \
+ (pixel - xLeftOffset - 0.0) / plotWidth)
+
+ xBrowse1 = pixel
+ xBrowse2 = min(xLeftOffset + plotWidth,
+ (pixel + pixelStep - 1))
+
+ WEBQTL_COORDS = "%d, %d, %d, %d" % (
+ xBrowse1, paddingTop, xBrowse2, (paddingTop + self.BAND_HEIGHT))
+ WEBQTL_HREF = "javascript:rangeView('%s', %f, %f)" % (self.selectedChr - 1, max(
+ 0, (calBase - webqtlZoomWidth)) / 1000000.0, (calBase + webqtlZoomWidth) / 1000000.0)
+
+ WEBQTL_TITLE = "Click to view this section of the genome in WebQTL"
+ gifmap.append(
+ HtmlGenWrapper.create_area_tag(
+ shape='rect',
+ coords=WEBQTL_COORDS,
+ href=WEBQTL_HREF,
+ title=WEBQTL_TITLE))
+ im_drawer.rectangle(
+ xy=((xBrowse1, paddingTop),
+ (xBrowse2, (paddingTop + self.BAND_HEIGHT))),
+ outline=self.CLICKABLE_WEBQTL_REGION_COLOR,
+ fill=self.CLICKABLE_WEBQTL_REGION_COLOR)
+ im_drawer.line(
+ xy=((xBrowse1, paddingTop), (xBrowse1,
+ (paddingTop + self.BAND_HEIGHT))),
+ fill=self.CLICKABLE_WEBQTL_REGION_OUTLINE_COLOR)
+
+ if self.dataset.group.species == "mouse" or self.dataset.group.species == "rat":
+ PHENOGEN_COORDS = "%d, %d, %d, %d" % (
+ xBrowse1, phenogenPaddingTop, xBrowse2, (phenogenPaddingTop + self.BAND_HEIGHT))
+ if self.dataset.group.species == "mouse":
+ PHENOGEN_HREF = "https://phenogen.org/gene.jsp?speciesCB=Mm&auto=Y&geneTxt=chr%s:%d-%d&genomeVer=mm10" % (
+ self.selectedChr, max(0, calBase - flankingWidthInBases), calBase + flankingWidthInBases)
+ else:
+ PHENOGEN_HREF = "https://phenogen.org/gene.jsp?speciesCB=Mm&auto=Y&geneTxt=chr%s:%d-%d&genomeVer=mm10" % (
+ self.selectedChr, max(0, calBase - flankingWidthInBases), calBase + flankingWidthInBases)
+ PHENOGEN_TITLE = "Click to view this section of the genome in PhenoGen"
+ gifmap.append(
+ HtmlGenWrapper.create_area_tag(
+ shape='rect',
+ coords=PHENOGEN_COORDS,
+ href=PHENOGEN_HREF,
+ title=PHENOGEN_TITLE))
+ im_drawer.rectangle(
+ xy=((xBrowse1, phenogenPaddingTop),
+ (xBrowse2, (phenogenPaddingTop + self.BAND_HEIGHT))),
+ outline=self.CLICKABLE_PHENOGEN_REGION_COLOR,
+ fill=self.CLICKABLE_PHENOGEN_REGION_COLOR)
+ im_drawer.line(
+ xy=((xBrowse1, phenogenPaddingTop), (xBrowse1,
+ (phenogenPaddingTop + self.BAND_HEIGHT))),
+ fill=self.CLICKABLE_PHENOGEN_REGION_OUTLINE_COLOR)
+
+ UCSC_COORDS = "%d, %d, %d, %d" % (
+ xBrowse1, ucscPaddingTop, xBrowse2, (ucscPaddingTop + self.BAND_HEIGHT))
+ if self.dataset.group.species == "mouse":
+ UCSC_HREF = "http://genome.ucsc.edu/cgi-bin/hgTracks?db=%s&position=chr%s:%d-%d&hgt.customText=%s/snp/chr%s" % (
+ self._ucscDb, self.selectedChr, max(0, calBase - flankingWidthInBases), calBase + flankingWidthInBases, webqtlConfig.PORTADDR, self.selectedChr)
+ else:
+ UCSC_HREF = "http://genome.ucsc.edu/cgi-bin/hgTracks?db=%s&position=chr%s:%d-%d" % (
+ self._ucscDb, self.selectedChr, max(0, calBase - flankingWidthInBases), calBase + flankingWidthInBases)
+ UCSC_TITLE = "Click to view this section of the genome in the UCSC Genome Browser"
+ gifmap.append(
+ HtmlGenWrapper.create_area_tag(
+ shape='rect',
+ coords=UCSC_COORDS,
+ href=UCSC_HREF,
+ title=UCSC_TITLE))
+ im_drawer.rectangle(
+ xy=((xBrowse1, ucscPaddingTop),
+ (xBrowse2, (ucscPaddingTop + self.BAND_HEIGHT))),
+ outline=self.CLICKABLE_UCSC_REGION_COLOR,
+ fill=self.CLICKABLE_UCSC_REGION_COLOR)
+ im_drawer.line(
+ xy=((xBrowse1, ucscPaddingTop),
+ (xBrowse1, (ucscPaddingTop + self.BAND_HEIGHT))),
+ fill=self.CLICKABLE_UCSC_REGION_OUTLINE_COLOR)
+
+ ENSEMBL_COORDS = "%d, %d, %d, %d" % (
+ xBrowse1, ensemblPaddingTop, xBrowse2, (ensemblPaddingTop + self.BAND_HEIGHT))
+ if self.dataset.group.species == "mouse":
+ ENSEMBL_HREF = "http://www.ensembl.org/Mus_musculus/contigview?highlight=&chr=%s&vc_start=%d&vc_end=%d&x=35&y=12" % (
+ self.selectedChr, max(0, calBase - flankingWidthInBases), calBase + flankingWidthInBases)
+ else:
+ ENSEMBL_HREF = "http://www.ensembl.org/Rattus_norvegicus/contigview?chr=%s&start=%d&end=%d" % (
+ self.selectedChr, max(0, calBase - flankingWidthInBases), calBase + flankingWidthInBases)
+ ENSEMBL_TITLE = "Click to view this section of the genome in the Ensembl Genome Browser"
+ gifmap.append(HtmlGenWrapper.create_area_tag(
+ shape='rect',
+ coords=ENSEMBL_COORDS,
+ href=ENSEMBL_HREF,
+ title=ENSEMBL_TITLE))
+ im_drawer.rectangle(
+ xy=((xBrowse1, ensemblPaddingTop),
+ (xBrowse2, (ensemblPaddingTop + self.BAND_HEIGHT))),
+ outline=self.CLICKABLE_ENSEMBL_REGION_COLOR,
+ fill=self.CLICKABLE_ENSEMBL_REGION_COLOR)
+ im_drawer.line(
+ xy=((xBrowse1, ensemblPaddingTop),
+ (xBrowse1, (ensemblPaddingTop + self.BAND_HEIGHT))),
+ fill=self.CLICKABLE_ENSEMBL_REGION_OUTLINE_COLOR)
+ # end for
+
+ im_drawer.text(
+ text="Click to view the corresponding section of the genome in an 8x expanded WebQTL map",
+ xy=((xLeftOffset + 10), paddingTop), # + self.BAND_HEIGHT/2),
+ font=clickableRegionLabelFont,
+ fill=self.CLICKABLE_WEBQTL_TEXT_COLOR)
+ if self.dataset.group.species == "mouse" or self.dataset.group.species == "rat":
+ im_drawer.text(
+ text="Click to view the corresponding section of the genome in PhenoGen",
+ # + self.BAND_HEIGHT/2),
+ xy=((xLeftOffset + 10), phenogenPaddingTop),
+ font=clickableRegionLabelFont, fill=self.CLICKABLE_PHENOGEN_TEXT_COLOR)
+ im_drawer.text(
+ text="Click to view the corresponding section of the genome in the UCSC Genome Browser",
+ # + self.BAND_HEIGHT/2),
+ xy=((xLeftOffset + 10), ucscPaddingTop),
+ font=clickableRegionLabelFont, fill=self.CLICKABLE_UCSC_TEXT_COLOR)
+ im_drawer.text(
+ text="Click to view the corresponding section of the genome in the Ensembl Genome Browser",
+ # + self.BAND_HEIGHT/2),
+ xy=((xLeftOffset + 10), ensemblPaddingTop),
+ font=clickableRegionLabelFont, fill=self.CLICKABLE_ENSEMBL_TEXT_COLOR)
+
+ # draw the gray text
+ chrFont = ImageFont.truetype(
+ font=VERDANA_BOLD_FILE, size=26 * zoom)
+ chrX = xLeftOffset + plotWidth - 2 - im_drawer.textsize(
+ "Chr %s" % self.ChrList[self.selectedChr][0], font=chrFont)[0]
+ im_drawer.text(
+ text="Chr %s" % self.ChrList[self.selectedChr][0],
+ xy=(chrX, paddingTop), font=chrFont, fill=GRAY)
+ # end of drawBrowserClickableRegions
+ else:
+ # draw the gray text
+ chrFont = ImageFont.truetype(font=VERDANA_FILE, size=26 * zoom)
+ chrX = xLeftOffset + (plotWidth - im_drawer.textsize(
+ "Chr %s" % currentChromosome, font=chrFont)[0]) / 2
+ im_drawer.text(
+ text="Chr %s" % currentChromosome, xy=(chrX, 32), font=chrFont,
+ fill=GRAY)
+ # end of drawBrowserClickableRegions
+ pass
+
+ def drawXAxis(self, canvas, drawAreaHeight, gifmap, plotXScale, showLocusForm, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
+ im_drawer = ImageDraw.Draw(canvas)
+ xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
+ plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
+ plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
+ yZero = canvas.size[1] - yBottomOffset
+ fontZoom = zoom
+ if zoom == 2:
+ fontZoom = 1.5
+
+ # Parameters
+ NUM_MINOR_TICKS = 5 # Number of minor ticks between major ticks
+ X_MAJOR_TICK_THICKNESS = 3
+ X_MINOR_TICK_THICKNESS = 1
+ X_AXIS_THICKNESS = 1 * zoom
+
+ # ======= Alex: Draw the X-axis labels (megabase location)
+ MBLabelFont = ImageFont.truetype(font=VERDANA_FILE, size=15 * zoom)
+ xMajorTickHeight = 10 * zoom # How high the tick extends below the axis
+ xMinorTickHeight = 5 * zoom
+ xAxisTickMarkColor = BLACK
+ xAxisLabelColor = BLACK
+ fontHeight = 12 * fontZoom # How tall the font that we're using is
+ spacingFromLabelToAxis = 10
+
+ if self.plotScale == 'physic':
+ strYLoc = yZero + MBLabelFont.font.height / 2
+ # Physical single chromosome view
+ if self.selectedChr > -1:
+ XScale = Plot.detScale(startMb, endMb)
+ XStart, XEnd, XStep = XScale
+ if XStep < 8:
+ XStep *= 2
+ spacingAmtX = spacingAmt = (XEnd - XStart) / XStep
+
+ j = 0
+ while abs(spacingAmtX - int(spacingAmtX)) >= spacingAmtX / 100.0 and j < 6:
+ j += 1
+ spacingAmtX *= 10
+
+ formatStr = '%%2.%df' % j
+
+ for counter, _Mb in enumerate(Plot.frange(XStart, XEnd, spacingAmt / NUM_MINOR_TICKS)):
+ if _Mb < startMb or _Mb > endMb:
+ continue
+ Xc = xLeftOffset + plotXScale * (_Mb - startMb)
+ if counter % NUM_MINOR_TICKS == 0: # Draw a MAJOR mark, not just a minor tick mark
+ im_drawer.line(xy=((Xc, yZero),
+ (Xc, yZero + xMajorTickHeight)),
+ fill=xAxisTickMarkColor,
+ width=X_MAJOR_TICK_THICKNESS) # Draw the MAJOR tick mark
+ # What Mbase location to put on the label
+ labelStr = str(formatStr % _Mb)
+ strWidth, strHeight = im_drawer.textsize(
+ labelStr, font=MBLabelFont)
+ drawStringXc = (Xc - (strWidth / 2.0))
+ im_drawer.text(xy=(drawStringXc, strYLoc),
+ text=labelStr, font=MBLabelFont,
+ fill=xAxisLabelColor)
+ else:
+ im_drawer.line(xy=((Xc, yZero),
+ (Xc, yZero + xMinorTickHeight)),
+ fill=xAxisTickMarkColor,
+ width=X_MINOR_TICK_THICKNESS) # Draw the MINOR tick mark
+
+ # Physical genome wide view
+ else:
+ distScale = 0
+ startPosX = xLeftOffset
+ for i, distLen in enumerate(self.ChrLengthDistList):
+ if distScale == 0: # universal scale in whole genome mapping
+ if distLen > 75:
+ distScale = 25
+ elif distLen > 30:
+ distScale = 10
+ else:
+ distScale = 5
+ for j, tickdists in enumerate(range(distScale, int(ceil(distLen)), distScale)):
+ im_drawer.line(
+ xy=((startPosX + tickdists * plotXScale, yZero),
+ (startPosX + tickdists * plotXScale, yZero + 7)),
+ fill=BLACK, width=1 * zoom)
+ if j % 2 == 0:
+ draw_rotated_text(
+ canvas, text=str(tickdists), font=MBLabelFont,
+ xy=(startPosX + tickdists * plotXScale,
+ yZero + 10 * zoom), fill=BLACK, angle=270)
+ startPosX += (self.ChrLengthDistList[i] + \
+ self.GraphInterval) * plotXScale
+
+ megabaseLabelFont = ImageFont.truetype(
+ font=VERDANA_FILE, size=int(18 * zoom * 1.5))
+ im_drawer.text(
+ text="Megabases",
+ xy=(
+ xLeftOffset + (plotWidth - im_drawer.textsize(
+ "Megabases", font=megabaseLabelFont)[0]) / 2,
+ strYLoc + MBLabelFont.font.height + 10 * (zoom % 2)),
+ font=megabaseLabelFont, fill=BLACK)
+ pass
+ else:
+ strYLoc = yZero + spacingFromLabelToAxis + MBLabelFont.font.height / 2
+ ChrAInfo = []
+ preLpos = -1
+ distinctCount = 0.0
+
+ if self.selectedChr == -1: # ZS: If viewing full genome/all chromosomes
+ for i, _chr in enumerate(self.genotype):
+ thisChr = []
+ Locus0CM = _chr[0].cM
+ nLoci = len(_chr)
+ if nLoci <= 8:
+ for _locus in _chr:
+ if _locus.name != ' - ':
+ if _locus.cM != preLpos:
+ distinctCount += 1
+ preLpos = _locus.cM
+ thisChr.append(
+ [_locus.name, _locus.cM - Locus0CM])
+ else:
+ for j in (0, round(nLoci / 4), round(nLoci / 2), round(nLoci * 3 / 4), -1):
+ while _chr[j].name == ' - ':
+ j += 1
+ if _chr[j].cM != preLpos:
+ distinctCount += 1
+ preLpos = _chr[j].cM
+ thisChr.append(
+ [_chr[j].name, _chr[j].cM - Locus0CM])
+ ChrAInfo.append(thisChr)
+ else:
+ for i, _chr in enumerate(self.genotype):
+ if _chr.name == self.ChrList[self.selectedChr][0]:
+ thisChr = []
+ Locus0CM = _chr[0].cM
+ for _locus in _chr:
+ if _locus.name != ' - ':
+ if _locus.cM != preLpos:
+ distinctCount += 1
+ preLpos = _locus.cM
+ thisChr.append(
+ [_locus.name, _locus.cM - Locus0CM])
+ ChrAInfo.append(thisChr)
+
+ stepA = (plotWidth + 0.0) / distinctCount
+
+ LRectWidth = 10
+ LRectHeight = 3
+ offsetA = -stepA
+ lineColor = LIGHTBLUE
+ startPosX = xLeftOffset
+
+ for j, ChrInfo in enumerate(ChrAInfo):
+ preLpos = -1
+ for i, item in enumerate(ChrInfo):
+ Lname, Lpos = item
+ if Lpos != preLpos:
+ offsetA += stepA
+ differ = 1
+ else:
+ differ = 0
+ preLpos = Lpos
+ Lpos *= plotXScale
+ if self.selectedChr > -1:
+ Zorder = i % 5
+ else:
+ Zorder = 0
+ if differ:
+ im_drawer.line(
+ xy=((startPosX + Lpos, yZero), (xLeftOffset + offsetA,\
+ yZero + 25)),
+ fill=lineColor)
+ im_drawer.line(
+ xy=((xLeftOffset + offsetA, yZero + 25), (xLeftOffset + offsetA,\
+ yZero + 40 + Zorder * (LRectWidth + 3))),
+ fill=lineColor)
+ rectColor = ORANGE
+ else:
+ im_drawer.line(
+ xy=((xLeftOffset + offsetA, yZero + 40 + Zorder * (LRectWidth + 3) - 3), (\
+ xLeftOffset + offsetA, yZero + 40 + Zorder * (LRectWidth + 3))),
+ fill=lineColor)
+ rectColor = DEEPPINK
+ im_drawer.rectangle(
+ xy=((xLeftOffset + offsetA, yZero + 40 + Zorder * (LRectWidth + 3)),
+ (xLeftOffset + offsetA - LRectHeight,
+ yZero + 40 + Zorder * (LRectWidth + 3) + LRectWidth)),
+ outline=rectColor, fill=rectColor, width=0)
+ COORDS = "%d,%d,%d,%d" % (xLeftOffset + offsetA - LRectHeight, yZero + 40 + Zorder * (LRectWidth + 3),\
+ xLeftOffset + offsetA, yZero + 40 + Zorder * (LRectWidth + 3) + LRectWidth)
+ HREF = "/show_trait?trait_id=%s&dataset=%s" % (
+ Lname, self.dataset.group.name + "Geno")
+ #HREF="javascript:showDatabase3('%s','%s','%s','');" % (showLocusForm,fd.RISet+"Geno", Lname)
+ Areas = HtmlGenWrapper.create_area_tag(
+ shape='rect',
+ coords=COORDS,
+ href=HREF,
+ target="_blank",
+ title="Locus : {}".format(Lname))
+ gifmap.append(Areas)
+ # piddle bug
+ if j == 0:
+ im_drawer.line(
+ xy=((startPosX, yZero), (startPosX, yZero + 40)),
+ fill=lineColor)
+ startPosX += (self.ChrLengthDistList[j] + \
+ self.GraphInterval) * plotXScale
+
+ centimorganLabelFont = ImageFont.truetype(
+ font=VERDANA_FILE, size=int(18 * zoom * 1.5))
+ im_drawer.text(
+ text="Centimorgans",
+ xy=(xLeftOffset + (plotWidth - im_drawer.textsize(
+ "Centimorgans", font=centimorganLabelFont)[0]) / 2,
+ strYLoc + MBLabelFont.font.height + 10 * (zoom % 2)),
+ font=centimorganLabelFont, fill=BLACK)
+
+ im_drawer.line(xy=((xLeftOffset, yZero), (xLeftOffset + plotWidth, yZero)),
+ fill=BLACK, width=X_AXIS_THICKNESS) # Draw the X axis itself
+
+ def drawQTL(self, canvas, drawAreaHeight, gifmap, plotXScale, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
+ im_drawer = ImageDraw.Draw(canvas)
+ xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
+ plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
+ plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
+ fontZoom = zoom
+ if zoom == 2:
+ fontZoom = 1.5
+
+ INTERCROSS = (self.genotype.type == "intercross")
+
+ # draw the LRS scale
+ # We first determine whether or not we are using a sliding scale.
+ # If so, we need to compute the maximum LRS value to determine where the max y-value should be, and call this LRS_LOD_Max.
+ # LRSTop is then defined to be above the LRS_LOD_Max by enough to add one additional LRSScale increment.
+ # if we are using a set-scale, then we set LRSTop to be the user's value, and LRS_LOD_Max doesn't matter.
+
+ # ZS: This is a mess, but I don't know a better way to account for different mapping methods returning results in different formats + the option to change between LRS and LOD
+ if self.lrsMax <= 0: # sliding scale
+ if "lrs_value" in self.qtlresults[0]:
+ LRS_LOD_Max = max([result['lrs_value']
+ for result in self.qtlresults])
+ if self.LRS_LOD == "LOD" or self.LRS_LOD == "-logP":
+ LRS_LOD_Max = LRS_LOD_Max / self.LODFACTOR
+ if self.permChecked and self.nperm > 0 and not self.multipleInterval:
+ self.significant = min(
+ self.significant / self.LODFACTOR, webqtlConfig.MAXLRS)
+ self.suggestive = min(
+ self.suggestive / self.LODFACTOR, webqtlConfig.MAXLRS)
+ else:
+ if self.permChecked and self.nperm > 0 and not self.multipleInterval:
+ self.significant = min(
+ self.significant, webqtlConfig.MAXLRS)
+ self.suggestive = min(
+ self.suggestive, webqtlConfig.MAXLRS)
+ else:
+ pass
+ else:
+ LRS_LOD_Max = max([result['lod_score']
+ for result in self.qtlresults]) + 1
+ if self.LRS_LOD == "LRS":
+ LRS_LOD_Max = LRS_LOD_Max * self.LODFACTOR
+ if self.permChecked and self.nperm > 0 and not self.multipleInterval:
+ self.significant = min(
+ self.significant * self.LODFACTOR, webqtlConfig.MAXLRS)
+ self.suggestive = min(
+ self.suggestive * self.LODFACTOR, webqtlConfig.MAXLRS)
+ else:
+ if self.permChecked and self.nperm > 0 and not self.multipleInterval:
+ self.significant = min(
+ self.significant, webqtlConfig.MAXLRS)
+ self.suggestive = min(
+ self.suggestive, webqtlConfig.MAXLRS)
+ else:
+ pass
+
+ if self.permChecked and self.nperm > 0 and not self.multipleInterval:
+ LRS_LOD_Max = max(self.significant, LRS_LOD_Max)
+
+ # genotype trait will give infinite LRS
+ LRS_LOD_Max = min(LRS_LOD_Max, webqtlConfig.MAXLRS)
+ else:
+ LRS_LOD_Max = self.lrsMax
+
+ # ZS: Needed to pass to genome browser
+ js_data = json.loads(self.js_data)
+ if self.LRS_LOD == "LRS":
+ js_data['max_score'] = LRS_LOD_Max / 4.61
+ else:
+ js_data['max_score'] = LRS_LOD_Max
+ self.js_data = json.dumps(js_data)
+
+ LRSScaleFont = ImageFont.truetype(font=VERDANA_FILE, size=16 * zoom)
+ LRSLODFont = ImageFont.truetype(
+ font=VERDANA_FILE, size=int(18 * zoom * 1.5))
+
+ yZero = yTopOffset + plotHeight
+ LRSHeightThresh = drawAreaHeight
+ AdditiveHeightThresh = drawAreaHeight / 2
+ DominanceHeightThresh = drawAreaHeight / 2
+
+ if LRS_LOD_Max > 100:
+ LRSScale = 20.0
+ elif LRS_LOD_Max > 20:
+ LRSScale = 5.0
+ elif LRS_LOD_Max > 7.5:
+ LRSScale = 2.5
+ else:
+ LRSScale = 1.0
+
+ LRSAxisList = Plot.frange(LRSScale, LRS_LOD_Max, LRSScale)
+
+ # ZS: Convert to int if all axis values are whole numbers
+ all_int = True
+ for item in LRSAxisList:
+ if isinstance(item, int):
+ continue
+ else:
+ all_int = False
+ break
+ # TODO(PIL): Convert from PIL
+ # if all_int:
+ # max_lrs_width = canvas.stringWidth("%d" % LRS_LOD_Max, font=LRSScaleFont) + 40
+ # else:
+ # max_lrs_width = canvas.stringWidth("%2.1f" % LRS_LOD_Max, font=LRSScaleFont) + 30
+
+ # draw the "LRS" or "LOD" string to the left of the axis
+ LRSScaleFont = ImageFont.truetype(font=VERDANA_FILE, size=16 * zoom)
+ LRSLODFont = ImageFont.truetype(
+ font=VERDANA_FILE, size=int(18 * zoom * 1.5))
+ yZero = yTopOffset + plotHeight
+
+ # TEXT_X_DISPLACEMENT = -20
+ #TEXT_Y_DISPLACEMENT = -215
+ if all_int:
+ TEXT_X_DISPLACEMENT = -12
+ else:
+ TEXT_X_DISPLACEMENT = -30
+ if self.LRS_LOD == "-logP":
+ TEXT_Y_DISPLACEMENT = -242
+ else:
+ TEXT_Y_DISPLACEMENT = -210
+ draw_rotated_text(
+ canvas, text=self.LRS_LOD, font=LRSLODFont,
+ xy=(xLeftOffset - im_drawer.textsize(
+ "999.99", font=LRSScaleFont)[0] - 15 * (zoom - 1) + TEXT_X_DISPLACEMENT,
+ yZero + TEXT_Y_DISPLACEMENT - 300 * (zoom - 1)),
+ fill=BLACK, angle=90)
+
+ for item in LRSAxisList:
+ if LRS_LOD_Max == 0.0:
+ LRS_LOD_Max = 0.000001
+ yTopOffset + 30 * (zoom - 1)
+ yLRS = yZero - (item / LRS_LOD_Max) * LRSHeightThresh
+ im_drawer.line(xy=((xLeftOffset, yLRS), (xLeftOffset - 4, yLRS)),
+ fill=self.LRS_COLOR, width=1 * zoom)
+ if all_int:
+ scaleStr = "%d" % item
+ else:
+ scaleStr = "%2.1f" % item
+ # Draw the LRS/LOD Y axis label
+ TEXT_Y_DISPLACEMENT = -10
+ im_drawer.text(
+ text=scaleStr,
+ xy=(xLeftOffset - 4 - im_drawer.textsize(scaleStr, font=LRSScaleFont)[0] - 5,
+ yLRS + TEXT_Y_DISPLACEMENT),
+ font=LRSScaleFont, fill=self.LRS_COLOR)
+
+ if self.permChecked and self.nperm > 0 and not self.multipleInterval:
+ significantY = yZero - self.significant * LRSHeightThresh / LRS_LOD_Max
+ suggestiveY = yZero - self.suggestive * LRSHeightThresh / LRS_LOD_Max
+ # significantY = yZero - self.significant*LRSHeightThresh/LRSAxisList[-1]
+ # suggestiveY = yZero - self.suggestive*LRSHeightThresh/LRSAxisList[-1]
+ startPosX = xLeftOffset
+
+ # "Significant" and "Suggestive" Drawing Routine
+ # ======= Draw the thick lines for "Significant" and "Suggestive" ===== (crowell: I tried to make the SNPs draw over these lines, but piddle wouldn't have it...)
+
+ # ZS: I don't know if what I did here with this inner function is clever or overly complicated, but it's the only way I could think of to avoid duplicating the code inside this function
+ def add_suggestive_significant_lines_and_legend(start_pos_x, chr_length_dist):
+ rightEdge = xLeftOffset + plotWidth
+ im_drawer.line(
+ xy=((start_pos_x + self.SUGGESTIVE_WIDTH / 1.5, suggestiveY),
+ (rightEdge, suggestiveY)),
+ fill=self.SUGGESTIVE_COLOR, width=self.SUGGESTIVE_WIDTH * zoom
+ # ,clipX=(xLeftOffset, xLeftOffset + plotWidth-2)
+ )
+ im_drawer.line(
+ xy=((start_pos_x + self.SUGGESTIVE_WIDTH / 1.5, significantY),
+ (rightEdge, significantY)),
+ fill=self.SIGNIFICANT_COLOR,
+ width=self.SIGNIFICANT_WIDTH * zoom
+ # , clipX=(xLeftOffset, xLeftOffset + plotWidth-2)
+ )
+ sugg_coords = "%d, %d, %d, %d" % (
+ start_pos_x, suggestiveY - 2, rightEdge + 2 * zoom, suggestiveY + 2)
+ sig_coords = "%d, %d, %d, %d" % (
+ start_pos_x, significantY - 2, rightEdge + 2 * zoom, significantY + 2)
+
+ if self.LRS_LOD == 'LRS':
+ sugg_title = "Suggestive LRS = %0.2f" % self.suggestive
+ sig_title = "Significant LRS = %0.2f" % self.significant
+ else:
+ sugg_title = "Suggestive LOD = %0.2f" % (
+ self.suggestive / 4.61)
+ sig_title = "Significant LOD = %0.2f" % (
+ self.significant / 4.61)
+ Areas1 = HtmlGenWrapper.create_area_tag(
+ shape='rect',
+ coords=sugg_coords,
+ title=sugg_title)
+ Areas2 = HtmlGenWrapper.create_area_tag(
+ shape='rect',
+ coords=sig_coords,
+ title=sig_title)
+ gifmap.append(Areas1)
+ gifmap.append(Areas2)
+
+ start_pos_x += (chr_length_dist + \
+ self.GraphInterval) * plotXScale
+ return start_pos_x
+
+ for i, _chr in enumerate(self.genotype):
+ if self.selectedChr != -1:
+ if _chr.name == self.ChrList[self.selectedChr][0]:
+ startPosX = add_suggestive_significant_lines_and_legend(
+ startPosX, self.ChrLengthDistList[0])
+ break
+ else:
+ continue
+ else:
+ startPosX = add_suggestive_significant_lines_and_legend(
+ startPosX, self.ChrLengthDistList[i])
+
+ if self.multipleInterval:
+ lrsEdgeWidth = 1
+ else:
+ if self.additiveChecked:
+ additiveMax = max([abs(X['additive'])
+ for X in self.qtlresults])
+ lrsEdgeWidth = 3
+
+ if zoom == 2:
+ lrsEdgeWidth = 2 * lrsEdgeWidth
+
+ LRSCoordXY = []
+ AdditiveCoordXY = []
+ DominanceCoordXY = []
+
+ symbolFont = ImageFont.truetype(
+ font=FNT_BS_FILE, size=5) # ZS: For Manhattan Plot
+
+ previous_chr = 1
+ previous_chr_as_int = 0
+ lineWidth = 1
+ oldStartPosX = 0
+ startPosX = xLeftOffset
+ for i, qtlresult in enumerate(self.qtlresults):
+ m = 0
+ thisLRSColor = self.colorCollection[0]
+ if qtlresult['chr'] != previous_chr and self.selectedChr == -1:
+ if self.manhattan_plot != True and len(LRSCoordXY) > 1:
+ draw_open_polygon(canvas, xy=LRSCoordXY,
+ outline=thisLRSColor, width=lrsEdgeWidth)
+
+ if not self.multipleInterval and self.additiveChecked:
+ plusColor = self.ADDITIVE_COLOR_POSITIVE
+ minusColor = self.ADDITIVE_COLOR_NEGATIVE
+ for k, aPoint in enumerate(AdditiveCoordXY):
+ if k > 0:
+ Xc0, Yc0 = AdditiveCoordXY[k - 1]
+ Xc, Yc = aPoint
+ if (Yc0 - yZero) * (Yc - yZero) < 0:
+ if Xc == Xc0: # genotype , locus distance is 0
+ Xcm = Xc
+ else:
+ Xcm = (yZero - Yc0) / \
+ ((Yc - Yc0) / (Xc - Xc0)) + Xc0
+ if Yc0 < yZero:
+ im_drawer.line(
+ xy=((Xc0, Yc0), (Xcm, yZero)),
+ fill=plusColor, width=lineWidth
+ )
+ im_drawer.line(
+ xy=((Xcm, yZero),
+ (Xc, yZero - (Yc - yZero))),
+ fill=minusColor, width=lineWidth
+ )
+ else:
+ im_drawer.line(
+ xy=((Xc0, yZero - (Yc0 - yZero)),
+ (Xcm, yZero)),
+ fill=minusColor, width=lineWidth
+ )
+ im_drawer.line(
+ xy=((Xcm, yZero), (Xc, Yc)),
+ fill=plusColor, width=lineWidth
+ )
+ elif (Yc0 - yZero) * (Yc - yZero) > 0:
+ if Yc < yZero:
+ im_drawer.line(
+ xy=((Xc0, Yc0), (Xc, Yc)),
+ fill=plusColor,
+ width=lineWidth
+ )
+ else:
+ im_drawer.line(
+ xy=((Xc0, yZero - (Yc0 - yZero)),
+ (Xc, yZero - (Yc - yZero))),
+ fill=minusColor, width=lineWidth
+ )
+ else:
+ minYc = min(Yc - yZero, Yc0 - yZero)
+ if minYc < 0:
+ im_drawer.line(
+ xy=((Xc0, Yc0), (Xc, Yc)),
+ fill=plusColor, width=lineWidth
+ )
+ else:
+ im_drawer.line(
+ xy=((Xc0, yZero - (Yc0 - yZero)),
+ (Xc, yZero - (Yc - yZero))),
+ fill=minusColor, width=lineWidth
+ )
+
+ LRSCoordXY = []
+ AdditiveCoordXY = []
+ previous_chr = qtlresult['chr']
+ previous_chr_as_int += 1
+ newStartPosX = (
+ self.ChrLengthDistList[previous_chr_as_int - 1] + self.GraphInterval) * plotXScale
+ if newStartPosX != oldStartPosX:
+ startPosX += newStartPosX
+ oldStartPosX = newStartPosX
+
+ # This is because the chromosome value stored in qtlresult['chr'] can be (for example) either X or 20 depending upon the mapping method/scale used
+ this_chr = str(self.ChrList[self.selectedChr][0])
+ if self.plotScale != "physic":
+ this_chr = str(self.ChrList[self.selectedChr][1] + 1)
+
+ if self.selectedChr == -1 or str(qtlresult['chr']) == this_chr:
+ if self.plotScale != "physic" and self.mapping_method == "reaper" and not self.manhattan_plot:
+ start_cm = self.genotype[self.selectedChr - 1][0].cM
+ Xc = startPosX + (qtlresult['cM'] - start_cm) * plotXScale
+ if hasattr(self.genotype, "filler"):
+ if self.genotype.filler:
+ if self.selectedChr != -1:
+ Xc = startPosX + \
+ (qtlresult['Mb'] - start_cm) * plotXScale
+ else:
+ Xc = startPosX + ((qtlresult['Mb'] - start_cm - startMb) * plotXScale) * (
+ ((qtlresult['Mb'] - start_cm - startMb) * plotXScale) / ((qtlresult['Mb'] - start_cm - startMb + self.GraphInterval) * plotXScale))
+ else:
+ if self.selectedChr != -1 and qtlresult['Mb'] > endMb:
+ Xc = startPosX + endMb * plotXScale
+ else:
+ if qtlresult['Mb'] - startMb < 0:
+ continue
+ Xc = startPosX + (qtlresult['Mb'] - startMb) * plotXScale
+
+ # updated by NL 06-18-2011:
+ # fix the over limit LRS graph issue since genotype trait may give infinite LRS;
+ # for any lrs is over than 460(LRS max in this system), it will be reset to 460
+
+ yLRS = yZero - (item / LRS_LOD_Max) * LRSHeightThresh
+
+ if 'lrs_value' in qtlresult:
+ if self.LRS_LOD == "LOD" or self.LRS_LOD == "-logP":
+ if qtlresult['lrs_value'] > 460 or qtlresult['lrs_value'] == 'inf':
+ Yc = yZero - webqtlConfig.MAXLRS * \
+ LRSHeightThresh / \
+ (LRS_LOD_Max * self.LODFACTOR)
+ else:
+ Yc = yZero - \
+ qtlresult['lrs_value'] * LRSHeightThresh / \
+ (LRS_LOD_Max * self.LODFACTOR)
+ else:
+ if qtlresult['lrs_value'] > 460 or qtlresult['lrs_value'] == 'inf':
+ Yc = yZero - webqtlConfig.MAXLRS * LRSHeightThresh / LRS_LOD_Max
+ else:
+ Yc = yZero - \
+ qtlresult['lrs_value'] * \
+ LRSHeightThresh / LRS_LOD_Max
+ else:
+ if qtlresult['lod_score'] > 100 or qtlresult['lod_score'] == 'inf':
+ Yc = yZero - webqtlConfig.MAXLRS * LRSHeightThresh / LRS_LOD_Max
+ else:
+ if self.LRS_LOD == "LRS":
+ Yc = yZero - \
+ qtlresult['lod_score'] * self.LODFACTOR * \
+ LRSHeightThresh / LRS_LOD_Max
+ else:
+ Yc = yZero - \
+ qtlresult['lod_score'] * \
+ LRSHeightThresh / LRS_LOD_Max
+
+ if self.manhattan_plot == True:
+ if self.color_scheme == "single":
+ point_color = self.manhattan_single_color
+ elif self.color_scheme == "varied":
+ point_color = DISTINCT_COLOR_LIST[previous_chr_as_int]
+ else:
+ if self.selectedChr == -1 and (previous_chr_as_int % 2 == 1):
+ point_color = RED
+ else:
+ point_color = BLUE
+
+ im_drawer.text(
+ text="5",
+ xy=(
+ Xc - im_drawer.textsize("5",
+ font=symbolFont)[0] / 2 + 1,
+ Yc - 4),
+ fill=point_color, font=symbolFont)
+ else:
+ LRSCoordXY.append((Xc, Yc))
+
+ if not self.multipleInterval and self.additiveChecked:
+ if additiveMax == 0.0:
+ additiveMax = 0.000001
+ Yc = yZero - qtlresult['additive'] * \
+ AdditiveHeightThresh / additiveMax
+ AdditiveCoordXY.append((Xc, Yc))
+
+ if self.selectedChr != -1 and qtlresult['Mb'] > endMb and endMb != -1:
+ break
+ m += 1
+
+ if self.manhattan_plot != True and len(LRSCoordXY) > 1:
+ draw_open_polygon(canvas, xy=LRSCoordXY, outline=thisLRSColor,
+ width=lrsEdgeWidth)
+
+ if not self.multipleInterval and self.additiveChecked:
+ plusColor = self.ADDITIVE_COLOR_POSITIVE
+ minusColor = self.ADDITIVE_COLOR_NEGATIVE
+ for k, aPoint in enumerate(AdditiveCoordXY):
+ if k > 0:
+ Xc0, Yc0 = AdditiveCoordXY[k - 1]
+ Xc, Yc = aPoint
+ if (Yc0 - yZero) * (Yc - yZero) < 0:
+ if Xc == Xc0: # genotype , locus distance is 0
+ Xcm = Xc
+ else:
+ Xcm = (yZero - Yc0) / \
+ ((Yc - Yc0) / (Xc - Xc0)) + Xc0
+ if Yc0 < yZero:
+ im_drawer.line(
+ xy=((Xc0, Yc0), (Xcm, yZero)),
+ fill=plusColor, width=lineWidth
+ # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
+ )
+ im_drawer.line(
+ xy=((Xcm, yZero), (Xc, yZero - (Yc - yZero))),
+ fill=minusColor, width=lineWidth
+ # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
+ )
+ else:
+ im_drawer.line(
+ xy=((Xc0, yZero - (Yc0 - yZero)),
+ (Xcm, yZero)),
+ fill=minusColor, width=lineWidth
+ # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
+ )
+ im_drawer.line(
+ xy=((Xcm, yZero), (Xc, Yc)),
+ fill=plusColor, width=lineWidth
+ # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
+ )
+ elif (Yc0 - yZero) * (Yc - yZero) > 0:
+ if Yc < yZero:
+ im_drawer.line(
+ xy=((Xc0, Yc0), (Xc, Yc)), fill=plusColor,
+ width=lineWidth
+ # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
+ )
+ else:
+ im_drawer.line(
+ xy=((Xc0, yZero - (Yc0 - yZero)),
+ (Xc, yZero - (Yc - yZero))),
+ fill=minusColor, width=lineWidth
+ # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
+ )
+ else:
+ minYc = min(Yc - yZero, Yc0 - yZero)
+ if minYc < 0:
+ im_drawer.line(
+ xy=((Xc0, Yc0), (Xc, Yc)),
+ fill=plusColor, width=lineWidth
+ # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
+ )
+ else:
+ im_drawer.line(
+ xy=((Xc0, yZero - (Yc0 - yZero)),
+ (Xc, yZero - (Yc - yZero))),
+ fill=minusColor, width=lineWidth
+ # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
+ )
+
+ if not self.multipleInterval and INTERCROSS and self.dominanceChecked:
+ plusColor = self.DOMINANCE_COLOR_POSITIVE
+ minusColor = self.DOMINANCE_COLOR_NEGATIVE
+ for k, aPoint in enumerate(DominanceCoordXY):
+ if k > 0:
+ Xc0, Yc0 = DominanceCoordXY[k - 1]
+ Xc, Yc = aPoint
+ if (Yc0 - yZero) * (Yc - yZero) < 0:
+ if Xc == Xc0: # genotype , locus distance is 0
+ Xcm = Xc
+ else:
+ Xcm = (yZero - Yc0) / \
+ ((Yc - Yc0) / (Xc - Xc0)) + Xc0
+ if Yc0 < yZero:
+ im_drawer.line(
+ xy=((Xc0, Yc0), (Xcm, yZero)),
+ fill=plusColor, width=lineWidth
+ # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
+ )
+ im_drawer.line(
+ xy=((Xcm, yZero), (Xc, yZero - (Yc - yZero))),
+ fill=minusColor, width=lineWidth
+ # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
+ )
+ else:
+ im_drawer.line(
+ xy=((Xc0, yZero - (Yc0 - yZero)), (Xcm, yZero)),
+ fill=minusColor, width=lineWidth
+ # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
+ )
+ im_drawer.line(
+ xy=((Xcm, yZero), (Xc, Yc)),
+ fill=plusColor, width=lineWidth
+ # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
+ )
+ elif (Yc0 - yZero) * (Yc - yZero) > 0:
+ if Yc < yZero:
+ im_drawer.line(
+ xy=((Xc0, Yc0), (Xc, Yc)),
+ fill=plusColor, width=lineWidth
+ # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
+ )
+ else:
+ im_drawer.line(
+ xy=((Xc0, yZero - (Yc0 - yZero)),
+ (Xc, yZero - (Yc - yZero))),
+ fill=minusColor, width=lineWidth
+ # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
+ )
+ else:
+ minYc = min(Yc - yZero, Yc0 - yZero)
+ if minYc < 0:
+ im_drawer.line(
+ xy=((Xc0, Yc0), (Xc, Yc)),
+ fill=plusColor, width=lineWidth
+ # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
+ )
+ else:
+ im_drawer.line(
+ xy=((Xc0, yZero - (Yc0 - yZero)),
+ (Xc, yZero - (Yc - yZero))), fill=minusColor,
+ width=lineWidth
+ # , clipX=(xLeftOffset, xLeftOffset + plotWidth)
+ )
+
+ # draw additive scale
+ if not self.multipleInterval and self.additiveChecked:
+ additiveScaleFont = ImageFont.truetype(
+ font=VERDANA_FILE, size=16 * zoom)
+ additiveScale = Plot.detScaleOld(0, additiveMax)
+ additiveStep = (additiveScale[1] - \
+ additiveScale[0]) / additiveScale[2]
+ additiveAxisList = Plot.frange(0, additiveScale[1], additiveStep)
+ addPlotScale = AdditiveHeightThresh / additiveMax
+ TEXT_Y_DISPLACEMENT = -8
+
+ additiveAxisList.append(additiveScale[1])
+ for item in additiveAxisList:
+ additiveY = yZero - item * addPlotScale
+ im_drawer.line(
+ xy=((xLeftOffset + plotWidth, additiveY),
+ (xLeftOffset + 4 + plotWidth, additiveY)),
+ fill=self.ADDITIVE_COLOR_POSITIVE, width=1 * zoom)
+ scaleStr = "%2.3f" % item
+ im_drawer.text(
+ text=scaleStr,
+ xy=(xLeftOffset + plotWidth + 6,
+ additiveY + TEXT_Y_DISPLACEMENT),
+ font=additiveScaleFont, fill=self.ADDITIVE_COLOR_POSITIVE)
+
+ im_drawer.line(
+ xy=((xLeftOffset + plotWidth, additiveY),
+ (xLeftOffset + plotWidth, yZero)),
+ fill=self.ADDITIVE_COLOR_POSITIVE, width=1 * zoom)
+
+ im_drawer.line(
+ xy=((xLeftOffset, yZero), (xLeftOffset, yTopOffset + 30 * (zoom - 1))),
+ fill=self.LRS_COLOR, width=1 * zoom) # the blue line running up the y axis
+
+ def drawGraphBackground(self, canvas, gifmap, offset=(80, 120, 80, 50), zoom=1, startMb=None, endMb=None):
+ # conditions
+ # multiple Chromosome view
+ # single Chromosome Physical
+ # single Chromosome Genetic
+ im_drawer = ImageDraw.Draw(canvas)
+ xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
+ plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
+ plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
+ yBottom = yTopOffset + plotHeight
+ fontZoom = zoom
+ if zoom == 2:
+ fontZoom = 1.5
+ yTopOffset += 30
+
+ # calculate plot scale
+ if self.plotScale != 'physic':
+ self.ChrLengthDistList = self.ChrLengthCMList
+ drawRegionDistance = self.ChrLengthCMSum
+ else:
+ self.ChrLengthDistList = self.ChrLengthMbList
+ drawRegionDistance = self.ChrLengthMbSum
+
+ if self.selectedChr > -1: # single chromosome view
+ spacingAmt = plotWidth / 13.5
+ i = 0
+ for startPix in Plot.frange(xLeftOffset, xLeftOffset + plotWidth, spacingAmt):
+ if (i % 2 == 0):
+ theBackColor = self.GRAPH_BACK_DARK_COLOR
+ else:
+ theBackColor = self.GRAPH_BACK_LIGHT_COLOR
+ i += 1
+ im_drawer.rectangle(
+ [(startPix, yTopOffset),
+ (min(startPix + spacingAmt, xLeftOffset + plotWidth), yBottom)],
+ outline=theBackColor, fill=theBackColor)
+
+ drawRegionDistance = self.ChrLengthDistList[self.ChrList[self.selectedChr][1]]
+ self.ChrLengthDistList = [drawRegionDistance]
+ if self.plotScale == 'physic':
+ plotXScale = plotWidth / (endMb - startMb)
+ else:
+ plotXScale = plotWidth / drawRegionDistance
+
+ else: # multiple chromosome view
+ plotXScale = plotWidth / \
+ ((len(self.genotype) - 1) * self.GraphInterval + drawRegionDistance)
+
+ startPosX = xLeftOffset
+ if fontZoom == 1.5:
+ chrFontZoom = 2
+ else:
+ chrFontZoom = 1
+ chrLabelFont = ImageFont.truetype(
+ font=VERDANA_FILE, size=24 * chrFontZoom)
+
+ for i, _chr in enumerate(self.genotype):
+ if (i % 2 == 0):
+ theBackColor = self.GRAPH_BACK_DARK_COLOR
+ else:
+ theBackColor = self.GRAPH_BACK_LIGHT_COLOR
+
+ # draw the shaded boxes and the sig/sug thick lines
+ im_drawer.rectangle(
+ ((startPosX, yTopOffset),
+ (startPosX + self.ChrLengthDistList[i] * plotXScale, yBottom)),
+ outline=GAINSBORO,
+ fill=theBackColor)
+
+ chrNameWidth, chrNameHeight = im_drawer.textsize(
+ _chr.name, font=chrLabelFont)
+ chrStartPix = startPosX + \
+ (self.ChrLengthDistList[i] * plotXScale - chrNameWidth) / 2
+ chrEndPix = startPosX + \
+ (self.ChrLengthDistList[i] * plotXScale + chrNameWidth) / 2
+
+ TEXT_Y_DISPLACEMENT = 0
+ im_drawer.text(xy=(chrStartPix, yTopOffset + TEXT_Y_DISPLACEMENT),
+ text=_chr.name, font=chrLabelFont, fill=BLACK)
+ COORDS = "%d,%d,%d,%d" % (
+ chrStartPix, yTopOffset, chrEndPix, yTopOffset + 20)
+
+ # add by NL 09-03-2010
+ HREF = "javascript:chrView(%d,%s);" % (i, self.ChrLengthMbList)
+ Areas = HtmlGenWrapper.create_area_tag(
+ shape='rect',
+ coords=COORDS,
+ href=HREF)
+ gifmap.append(Areas)
+ startPosX += (self.ChrLengthDistList[i] + \
+ self.GraphInterval) * plotXScale
+
+ return plotXScale
+
+ def drawPermutationHistogram(self):
+ #########################################
+ # Permutation Graph
+ #########################################
+ myCanvas = Image.new("RGBA", size=(500, 300))
+ if 'lod_score' in self.qtlresults[0] and self.LRS_LOD == "LRS":
+ perm_output = [value * 4.61 for value in self.perm_output]
+ elif 'lod_score' not in self.qtlresults[0] and self.LRS_LOD == "LOD":
+ perm_output = [value / 4.61 for value in self.perm_output]
+ else:
+ perm_output = self.perm_output
+
+ filename = webqtlUtil.genRandStr("Reg_")
+ Plot.plotBar(myCanvas, perm_output, XLabel=self.LRS_LOD,
+ YLabel='Frequency', title=' Histogram of Permutation Test')
+ myCanvas.save("{}.gif".format(GENERATED_IMAGE_DIR + filename),
+ format='gif')
+
+ return filename
+
+ def geneTable(self, geneCol, refGene=None):
+ if self.dataset.group.species == 'mouse' or self.dataset.group.species == 'rat':
+ self.gene_table_header = self.getGeneTableHeaderList(refGene=None)
+ self.gene_table_body = self.getGeneTableBody(geneCol, refGene=None)
+ else:
+ self.gene_table_header = None
+ self.gene_table_body = None
+
+ def getGeneTableHeaderList(self, refGene=None):
+
+ gene_table_header_list = []
+ if self.dataset.group.species == "mouse":
+ if refGene:
+ gene_table_header_list = ["Index",
+ "Symbol",
+ "Mb Start",
+ "Length (Kb)",
+ "SNP Count",
+ "SNP Density",
+ "Avg Expr",
+ "Human Chr",
+ "Mb Start (hg19)",
+ "Literature Correlation",
+ "Gene Description"]
+ else:
+ gene_table_header_list = ["",
+ "Index",
+ "Symbol",
+ "Mb Start",
+ "Length (Kb)",
+ "SNP Count",
+ "SNP Density",
+ "Avg Expr",
+ "Human Chr",
+ "Mb Start (hg19)",
+ "Gene Description"]
+ elif self.dataset.group.species == "rat":
+ gene_table_header_list = ["",
+ "Index",
+ "Symbol",
+ "Mb Start",
+ "Length (Kb)",
+ "Avg Expr",
+ "Mouse Chr",
+ "Mb Start (mm9)",
+ "Human Chr",
+ "Mb Start (hg19)",
+ "Gene Description"]
+
+ return gene_table_header_list
+
+ def getGeneTableBody(self, geneCol, refGene=None):
+ gene_table_body = []
+
+ tableIterationsCnt = 0
+ if self.dataset.group.species == "mouse":
+ for gIndex, theGO in enumerate(geneCol):
+
+ tableIterationsCnt = tableIterationsCnt + 1
+
+ this_row = [] # container for the cells of each row
+ selectCheck = HtmlGenWrapper.create_input_tag(
+ type_="checkbox",
+ name="selectCheck",
+ value=theGO["GeneSymbol"],
+ Class="checkbox trait_checkbox") # checkbox for each row
+
+ geneLength = (theGO["TxEnd"] - theGO["TxStart"]) * 1000.0
+ tenPercentLength = geneLength * 0.0001
+ txStart = theGO["TxStart"]
+ txEnd = theGO["TxEnd"]
+ theGO["snpDensity"] = theGO["snpCount"] / geneLength
+ if self.ALEX_DEBUG_BOOL_PRINT_GENE_LIST:
+ geneIdString = 'http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=gene&cmd=Retrieve&dopt=Graphics&list_uids=%s' % theGO[
+ "GeneID"]
+
+ if theGO["snpCount"]:
+ snpString = HT.Link(
+ (f"http://genenetwork.org/webqtl/main.py?FormID=snpBrowser&"
+ f"chr={theGO['Chr']}&"
+ f"start={theGO['TxStart']}&"
+ f"end={theGO['TxEnd']}&"
+ f"geneName={theGO['GeneSymbol']}&"
+ f"s1={self.diffCol[0]}&s2=%d"),
+ str(theGO["snpCount"]) # The text to display
+ )
+ snpString.set_blank_target()
+ snpString.set_attribute("class", "normalsize")
+ else:
+ snpString = 0
+
+ mouseStartString = "http://genome.ucsc.edu/cgi-bin/hgTracks?clade=vertebrate&org=Mouse&db=mm10&position=chr" + \
+ theGO["Chr"] + "%3A" + str(int(theGO["TxStart"] * 1000000.0)) + "-" + str(
+ int(theGO["TxEnd"] * 1000000.0)) + "&pix=620&Submit=submit"
+
+ # the chromosomes for human 1 are 1qXX.XX
+ if 'humanGene' in theGO:
+ if theGO['humanGene']["TxStart"] == '':
+ humanStartDisplay = ""
+ else:
+ humanStartDisplay = "%0.6f" % theGO['humanGene']["TxStart"]
+
+ humanChr = theGO['humanGene']["Chr"]
+ humanTxStart = theGO['humanGene']["TxStart"]
+
+ humanStartString = "http://genome.ucsc.edu/cgi-bin/hgTracks?clade=vertebrate&org=Human&db=hg17&position=chr%s:%d-%d" % (
+ humanChr, int(1000000 * theGO['humanGene']["TxStart"]), int(1000000 * theGO['humanGene']["TxEnd"]))
+ else:
+ humanStartString = humanChr = humanStartDisplay = "--"
+
+ geneDescription = theGO["GeneDescription"]
+ if len(geneDescription) > 70:
+ geneDescription = geneDescription[:70] + "..."
+
+ if theGO["snpDensity"] < 0.000001:
+ snpDensityStr = "0"
+ else:
+ snpDensityStr = "%0.6f" % theGO["snpDensity"]
+
+ avgExpr = [] # theGO["avgExprVal"]
+ if avgExpr in ([], None):
+ avgExpr = "--"
+ else:
+ avgExpr = "%0.6f" % avgExpr
+
+ # If we have a referenceGene then we will show the Literature Correlation
+ if theGO["Chr"] == "X":
+ chr_as_int = 19
+ else:
+ chr_as_int = int(theGO["Chr"]) - 1
+ if refGene:
+ literatureCorrelationString = str(self.getLiteratureCorrelation(
+ self.cursor, refGene, theGO['GeneID']) or "N/A")
+
+ this_row = [selectCheck.__str__(),
+ str(tableIterationsCnt),
+ str(HtmlGenWrapper.create_link_tag(
+ geneIdString,
+ theGO["GeneSymbol"],
+ target="_blank")
+ ),
+ str(HtmlGenWrapper.create_link_tag(
+ mouseStartString,
+ "{:.6f}".format(txStart),
+ target="_blank")
+ ),
+ str(HtmlGenWrapper.create_link_tag(
+ "javascript:rangeView('{}', {:f}, {:f})".format(
+ str(chr_as_int),
+ txStart - tenPercentLength,
+ txEnd + tenPercentLength),
+ "{:.3f}".format(geneLength))),
+ snpString,
+ snpDensityStr,
+ avgExpr,
+ humanChr,
+ str(HtmlGenWrapper.create_link_tag(
+ humanStartString,
+ humanStartDisplay,
+ target="_blank")),
+ literatureCorrelationString,
+ geneDescription]
+ else:
+ this_row = [selectCheck.__str__(),
+ str(tableIterationsCnt),
+ str(HtmlGenWrapper.create_link_tag(
+ geneIdString, theGO["GeneSymbol"],
+ target="_blank")),
+ str(HtmlGenWrapper.create_link_tag(
+ mouseStartString,
+ "{:.6f}".format(txStart),
+ target="_blank")),
+ str(HtmlGenWrapper.create_link_tag(
+ "javascript:rangeView('{}', {:f}, {:f})".format(
+ str(chr_as_int),
+ txStart - tenPercentLength,
+ txEnd + tenPercentLength),
+ "{:.3f}".format(geneLength))),
+ snpString,
+ snpDensityStr,
+ avgExpr,
+ humanChr,
+ str(HtmlGenWrapper.create_link_tag(
+ humanStartString,
+ humanStartDisplay,
+ target="_blank")),
+ geneDescription]
+
+ gene_table_body.append(this_row)
+
+ elif self.dataset.group.species == 'rat':
+ for gIndex, theGO in enumerate(geneCol):
+ this_row = [] # container for the cells of each row
+ selectCheck = str(HtmlGenWrapper.create_input_tag(
+ type_="checkbox",
+ name="selectCheck",
+ Class="checkbox trait_checkbox")) # checkbox for each row
+
+ if theGO["GeneID"] != "":
+ geneSymbolNCBI = str(HtmlGenWrapper.create_link_tag(
+ "http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=gene&cmd=Retrieve&dopt=Graphics&list_uids={}".format(
+ theGO["GeneID"]),
+ theGO["GeneSymbol"],
+ Class="normalsize",
+ target="_blank"))
+ else:
+ geneSymbolNCBI = theGO["GeneSymbol"]
+
+ if theGO["Chr"] == "X":
+ chr_as_int = 20
+ else:
+ chr_as_int = int(theGO["Chr"]) - 1
+
+ geneLength = (float(theGO["TxEnd"]) - float(theGO["TxStart"]))
+ geneLengthURL = "javascript:rangeView('%s', %f, %f)" % (theGO["Chr"], float(
+ theGO["TxStart"]) - (geneLength * 0.1), float(theGO["TxEnd"]) + (geneLength * 0.1))
+
+ avgExprVal = []
+ if avgExprVal != "" and avgExprVal:
+ avgExprVal = "%0.5f" % float(avgExprVal)
+ else:
+ avgExprVal = ""
+
+ # Mouse Gene
+ if theGO['mouseGene']:
+ mouseChr = theGO['mouseGene']["Chr"]
+ mouseTxStart = "%0.6f" % theGO['mouseGene']["TxStart"]
+ else:
+ mouseChr = mouseTxStart = ""
+
+ # the chromosomes for human 1 are 1qXX.XX
+ if 'humanGene' in theGO:
+ humanChr = theGO['humanGene']["Chr"]
+ humanTxStart = "%0.6f" % theGO['humanGene']["TxStart"]
+ else:
+ humanChr = humanTxStart = ""
+
+ geneDesc = theGO["GeneDescription"]
+ if geneDesc == "---":
+ geneDesc = ""
+
+ this_row = [selectCheck.__str__(),
+ str(gIndex + 1),
+ geneSymbolNCBI,
+ "%0.6f" % theGO["TxStart"],
+ str(HtmlGenWrapper.create_link_tag(
+ geneLengthURL,
+ "{:.3f}".format(geneLength * 1000.0))),
+ avgExprVal,
+ mouseChr,
+ mouseTxStart,
+ humanChr,
+ humanTxStart,
+ geneDesc]
+
+ gene_table_body.append(this_row)
+
+ return gene_table_body
+
+ def getLiteratureCorrelation(cursor, geneId1=None, geneId2=None):
+ if not geneId1 or not geneId2:
+ return None
+ if geneId1 == geneId2:
+ return 1.0
+ geneId1 = str(geneId1)
+ geneId2 = str(geneId2)
+ lCorr = None
+ try:
+ query = 'SELECT Value FROM LCorrRamin3 WHERE GeneId1 = %s and GeneId2 = %s'
+ for x, y in [(geneId1, geneId2), (geneId2, geneId1)]:
+ cursor.execute(query, (x, y))
+ lCorr = cursor.fetchone()
+ if lCorr:
+ lCorr = lCorr[0]
+ break
+ except:
+ raise # lCorr = None
+ return lCorr
diff --git a/gn2/wqflask/marker_regression/exceptions.py b/gn2/wqflask/marker_regression/exceptions.py
new file mode 100644
index 00000000..8c7e822b
--- /dev/null
+++ b/gn2/wqflask/marker_regression/exceptions.py
@@ -0,0 +1,13 @@
+"""Mapping Exception classes."""
+
+class NoMappingResultsError(Exception):
+ "Exception to raise if no results are computed."
+
+ def __init__(self, trait, dataset, mapping_method):
+ self.trait = trait
+ self.dataset = dataset
+ self.mapping_method = mapping_method
+ self.message = (
+ f"The mapping of trait '{trait}' from dataset '{dataset}' using "
+ f"the '{mapping_method}' mapping method returned no results.")
+ super().__init__(self.message, trait, mapping_method)
diff --git a/gn2/wqflask/marker_regression/gemma_mapping.py b/gn2/wqflask/marker_regression/gemma_mapping.py
new file mode 100644
index 00000000..d8851486
--- /dev/null
+++ b/gn2/wqflask/marker_regression/gemma_mapping.py
@@ -0,0 +1,245 @@
+import os
+import math
+import string
+import random
+import json
+
+from gn2.base import webqtlConfig
+from gn2.base.trait import create_trait
+from gn2.base.data_set import create_dataset
+from gn2.utility.redis_tools import get_redis_conn
+from gn2.utility.tools import flat_files, assert_file
+from gn2.utility.tools import GEMMA_WRAPPER_COMMAND
+from gn2.utility.tools import TEMPDIR
+from gn2.utility.tools import WEBSERVER_MODE
+from gn2.utility.tools import get_setting
+from gn2.wqflask.database import database_connection
+from gn3.computations.gemma import generate_hash_of_string
+
+
+GEMMAOPTS = "-debug"
+if WEBSERVER_MODE == 'PROD':
+ GEMMAOPTS = "-no-check"
+
+
+def generate_random_n_string(n):
+ return ''.join(random.choice(string.ascii_uppercase + string.digits)
+ for _ in range(n))
+
+
+def run_gemma(this_trait, this_dataset, samples, vals, covariates, use_loco,
+ maf=0.01, first_run=True, output_files=None):
+ """Generates p-values for each marker using GEMMA"""
+
+ if this_dataset.group.genofile is not None:
+ genofile_name = this_dataset.group.genofile[:-5]
+ else:
+ genofile_name = this_dataset.group.name
+
+ if first_run:
+ pheno_filename = gen_pheno_txt_file(this_dataset, genofile_name, vals)
+
+ if not os.path.isfile(f"{webqtlConfig.GENERATED_IMAGE_DIR}"
+ f"{genofile_name}_output.assoc.txt"):
+ open((f"{webqtlConfig.GENERATED_IMAGE_DIR}"
+ f"{genofile_name}_output.assoc.txt"),
+ "w+")
+
+ k_output_filename = (f"{this_dataset.group.name}_K_"
+ f"{generate_random_n_string(6)}")
+ gwa_output_filename = (f"{this_dataset.group.name}_GWA_"
+ f"{generate_random_n_string(6)}")
+
+
+ this_chromosomes_name = []
+ with database_connection(get_setting("SQL_URI")) as conn, conn.cursor() as db_cursor:
+ for this_chr in this_dataset.species.chromosomes.chromosomes(db_cursor):
+ this_chromosomes_name.append(this_dataset.species.chromosomes.chromosomes(db_cursor)[this_chr].name)
+
+ chr_list_string = ",".join(this_chromosomes_name)
+ if covariates != "":
+ covar_filename = gen_covariates_file(this_dataset, covariates, samples)
+ if str(use_loco).lower() == "true":
+ bimbam_dir = flat_files('genotype/bimbam')
+ geno_filepath = assert_file(
+ f"{bimbam_dir}/{genofile_name}_geno.txt")
+ pheno_filepath = f"{TEMPDIR}/gn2/{pheno_filename}.txt"
+ snps_filepath = assert_file(
+ f"{bimbam_dir}/{genofile_name}_snps.txt")
+ k_json_output_filepath = f"{TEMPDIR}/gn2/{k_output_filename}.json"
+ generate_k_command = (f"{GEMMA_WRAPPER_COMMAND} --json --loco "
+ f"{chr_list_string} -- {GEMMAOPTS} "
+ f"-g {geno_filepath} -p "
+ f"{pheno_filepath} -a "
+ f"{snps_filepath} -gk > "
+ f"{k_json_output_filepath}")
+ os.system(generate_k_command)
+
+ gemma_command = (f"{GEMMA_WRAPPER_COMMAND} --json --loco "
+ f"--input {k_json_output_filepath} "
+ f"-- {GEMMAOPTS} "
+ f"-g {geno_filepath} "
+ f"-p {pheno_filepath} ")
+ if covariates != "":
+ gemma_command += (f"-c {flat_files('mapping')}/"
+ f"{covar_filename}.txt "
+ f"-a {flat_files('genotype/bimbam')}/"
+ f"{genofile_name}_snps.txt "
+ f"-lmm 9 -maf {maf} > {TEMPDIR}/gn2/"
+ f"{gwa_output_filename}.json")
+ else:
+ gemma_command += (f"-a {flat_files('genotype/bimbam')}/"
+ f"{genofile_name}_snps.txt -lmm 9 -maf "
+ f"{maf} > "
+ f"{TEMPDIR}/gn2/{gwa_output_filename}.json")
+
+ else:
+ generate_k_command = (f"{GEMMA_WRAPPER_COMMAND} --json -- "
+ f"{GEMMAOPTS} "
+ f" -g {flat_files('genotype/bimbam')}/"
+ f"{genofile_name}_geno.txt -p "
+ f"{TEMPDIR}/gn2/{pheno_filename}.txt -a "
+ f"{flat_files('genotype/bimbam')}/"
+ f"{genofile_name}_snps.txt -gk > "
+ f"{TEMPDIR}/gn2/{k_output_filename}.json")
+
+ os.system(generate_k_command)
+
+ gemma_command = (f"{GEMMA_WRAPPER_COMMAND} --json --input "
+ f"{TEMPDIR}/gn2/{k_output_filename}.json -- "
+ f"{GEMMAOPTS} "
+ f"-a {flat_files('genotype/bimbam')}/"
+ f"{genofile_name}_snps.txt "
+ f"-lmm 9 -g {flat_files('genotype/bimbam')}/"
+ f"{genofile_name}_geno.txt -p "
+ f"{TEMPDIR}/gn2/{pheno_filename}.txt ")
+
+ if covariates != "":
+ gemma_command += (f" -c {flat_files('mapping')}/"
+ f"{covar_filename}.txt > "
+ f"{TEMPDIR}/gn2/{gwa_output_filename}.json")
+ else:
+ gemma_command += f" > {TEMPDIR}/gn2/{gwa_output_filename}.json"
+
+ os.system(gemma_command)
+ else:
+ gwa_output_filename = output_files
+
+ if use_loco == "True":
+ marker_obs = parse_loco_output(this_dataset, gwa_output_filename)
+ return marker_obs, gwa_output_filename
+ else:
+ marker_obs = parse_loco_output(
+ this_dataset, gwa_output_filename, use_loco)
+ return marker_obs, gwa_output_filename
+
+
+def gen_pheno_txt_file(this_dataset, genofile_name, vals):
+ """Generates phenotype file for GEMMA"""
+
+ filename = "PHENO_" + generate_hash_of_string(this_dataset.name + str(vals)).replace("/", "_")
+
+ with open(f"{TEMPDIR}/gn2/{filename}.txt", "w") as outfile:
+ for value in vals:
+ if value == "x":
+ outfile.write("NA\n")
+ else:
+ outfile.write(value + "\n")
+
+ return filename
+
+
+def gen_covariates_file(this_dataset, covariates, samples):
+ covariate_list = covariates.split(",")
+ covariate_data_object = []
+ for covariate in covariate_list:
+ this_covariate_data = []
+ trait_name = covariate.split(":")[0]
+ dataset_name = covariate.split(":")[1]
+ if dataset_name == "Temp":
+ temp_group = trait_name.split("_")[2]
+ dataset_ob = create_dataset(
+ dataset_name="Temp", dataset_type="Temp", group_name=temp_group)
+ else:
+ dataset_ob = create_dataset(covariate.split(":")[1])
+ trait_ob = create_trait(dataset=dataset_ob,
+ name=trait_name,
+ cellid=None)
+ this_dataset.group.get_samplelist(redis_conn=get_redis_conn())
+ trait_samples = this_dataset.group.samplelist
+ trait_sample_data = trait_ob.data
+ for index, sample in enumerate(trait_samples):
+ if sample in samples:
+ if sample in trait_sample_data:
+ sample_value = trait_sample_data[sample].value
+ this_covariate_data.append(sample_value)
+ else:
+ this_covariate_data.append("-9")
+ covariate_data_object.append(this_covariate_data)
+
+ filename = "COVAR_" + generate_hash_of_string(this_dataset.name + str(covariate_data_object)).replace("/", "_")
+
+ with open((f"{flat_files('mapping')}/"
+ f"{filename}.txt"),
+ "w") as outfile:
+ for i in range(len(covariate_data_object[0])):
+ for this_covariate in covariate_data_object:
+ outfile.write(str(this_covariate[i]) + "\t")
+ outfile.write("\n")
+
+ return filename
+
+
+def parse_loco_output(this_dataset, gwa_output_filename, loco="True"):
+
+ output_filename = f"{TEMPDIR}/gn2/{gwa_output_filename}.json"
+ if os.stat(output_filename).st_size == 0:
+ return {}
+
+ output_filelist = []
+ with open(output_filename) as data_file:
+ data = json.load(data_file)
+
+ files = data['files']
+ for file in files:
+ output_filelist.append(file[2])
+
+ included_markers = []
+ p_values = []
+ marker_obs = []
+ previous_chr = 0
+
+ for this_file in output_filelist:
+ if not os.path.isfile(this_file):
+ break
+ with open(this_file) as output_file:
+ for line in output_file:
+ if line.startswith("chr\t"):
+ continue
+ else:
+ marker = {}
+ marker['name'] = line.split("\t")[1]
+ if line.split("\t")[0] != "X" and line.split("\t")[0] != "X/Y" and line.split("\t")[0] != "Y" and line.split("\t")[0] != "M":
+ if "chr" in line.split("\t")[0]:
+ marker['chr'] = int(line.split("\t")[0][3:])
+ else:
+ marker['chr'] = int(line.split("\t")[0])
+ if marker['chr'] > int(previous_chr):
+ previous_chr = marker['chr']
+ elif marker['chr'] < int(previous_chr):
+ break
+ else:
+ marker['chr'] = line.split("\t")[0]
+ marker['Mb'] = float(line.split("\t")[2]) / 1000000
+ marker['p_value'] = float(line.split("\t")[10])
+ marker['additive'] = -(float(line.split("\t")[7])/2)
+ if math.isnan(marker['p_value']) or (marker['p_value'] <= 0):
+ marker['lod_score'] = marker['p_value'] = 0
+ else:
+ marker['lod_score'] = -math.log10(marker['p_value'])
+ marker_obs.append(marker)
+
+ included_markers.append(line.split("\t")[1])
+ p_values.append(float(line.split("\t")[9]))
+
+ return marker_obs
diff --git a/gn2/wqflask/marker_regression/plink_mapping.py b/gn2/wqflask/marker_regression/plink_mapping.py
new file mode 100644
index 00000000..7427717a
--- /dev/null
+++ b/gn2/wqflask/marker_regression/plink_mapping.py
@@ -0,0 +1,167 @@
+import string
+import os
+
+from gn2.base.webqtlConfig import TMPDIR
+from gn2.utility import webqtlUtil
+from gn2.utility.tools import flat_files, PLINK_COMMAND
+
+
+def run_plink(this_trait, dataset, species, vals, maf):
+ plink_output_filename = webqtlUtil.genRandStr(
+ f"{dataset.group.name}_{this_trait.name}_")
+ gen_pheno_txt_file(dataset, vals)
+
+ plink_command = f"{PLINK_COMMAND} --noweb --bfile {flat_files('mapping')}/{dataset.group.name} --no-pheno --no-fid --no-parents --no-sex --maf {maf} --out { TMPDIR}{plink_output_filename} --assoc "
+
+ os.system(plink_command)
+
+ count, p_values = parse_plink_output(plink_output_filename, species)
+
+ dataset.group.markers.add_pvalues(p_values)
+
+ return dataset.group.markers.markers
+
+
+def gen_pheno_txt_file(this_dataset, vals):
+ """Generates phenotype file for GEMMA/PLINK"""
+
+ current_file_data = []
+ with open(f"{flat_files('mapping')}/{this_dataset.group.name}.fam", "r") as outfile:
+ for i, line in enumerate(outfile):
+ split_line = line.split()
+ current_file_data.append(split_line)
+
+ with open(f"{flat_files('mapping')}/{this_dataset.group.name}.fam", "w") as outfile:
+ for i, line in enumerate(current_file_data):
+ if vals[i] == "x":
+ this_val = -9
+ else:
+ this_val = vals[i]
+ outfile.write("0 " + line[1] + " " + line[2] + " " + \
+ line[3] + " " + line[4] + " " + str(this_val) + "\n")
+
+
+def gen_pheno_txt_file_plink(this_trait, dataset, vals, pheno_filename=''):
+ ped_sample_list = get_samples_from_ped_file(dataset)
+ output_file = open(f"{TMPDIR}{pheno_filename}.txt", "wb")
+ header = f"FID\tIID\t{this_trait.name}\n"
+ output_file.write(header)
+
+ new_value_list = []
+
+ # if valueDict does not include some strain, value will be set to -9999 as missing value
+ for i, sample in enumerate(ped_sample_list):
+ try:
+ value = vals[i]
+ value = str(value).replace('value=', '')
+ value = value.strip()
+ except:
+ value = -9999
+
+ new_value_list.append(value)
+
+ new_line = ''
+ for i, sample in enumerate(ped_sample_list):
+ j = i + 1
+ value = new_value_list[i]
+ new_line += f"{sample}\t{sample}\t{value}\n"
+
+ if j % 1000 == 0:
+ output_file.write(newLine)
+ new_line = ''
+
+ if new_line:
+ output_file.write(new_line)
+
+ output_file.close()
+
+# get strain name from ped file in order
+
+
+def get_samples_from_ped_file(dataset):
+ ped_file = open(f"{flat_files('mapping')}{dataset.group.name}.ped", "r")
+ line = ped_file.readline()
+ sample_list = []
+
+ while line:
+ lineList = line.strip().split('\t')
+ lineList = [item.strip() for item in lineList]
+
+ sample_name = lineList[0]
+ sample_list.append(sample_name)
+
+ line = ped_file.readline()
+
+ return sample_list
+
+
+def parse_plink_output(output_filename, species):
+ plink_results = {}
+
+ threshold_p_value = 1
+
+ result_fp = open(f"{TMPDIR}{output_filename}.qassoc", "rb")
+
+ line = result_fp.readline()
+
+ value_list = [] # initialize value list, this list will include snp, bp and pvalue info
+ p_value_dict = {}
+ count = 0
+
+ while line:
+ # convert line from str to list
+ line_list = build_line_list(line=line)
+
+ # only keep the records whose chromosome name is in db
+ if int(line_list[0]) in species.chromosomes.chromosomes and line_list[-1] and line_list[-1].strip() != 'NA':
+
+ chr_name = species.chromosomes.chromosomes[int(line_list[0])]
+ snp = line_list[1]
+ BP = line_list[2]
+ p_value = float(line_list[-1])
+ if threshold_p_value >= 0 and threshold_p_value <= 1:
+ if p_value < threshold_p_value:
+ p_value_dict[snp] = float(p_value)
+
+ if chr_name in plink_results:
+ value_list = plink_results[chr_name]
+
+ # pvalue range is [0,1]
+ if threshold_p_value >= 0 and threshold_p_value <= 1:
+ if p_value < threshold_p_value:
+ value_list.append((snp, BP, p_value))
+ count += 1
+
+ plink_results[chr_name] = value_list
+ value_list = []
+ else:
+ if threshold_p_value >= 0 and threshold_p_value <= 1:
+ if p_value < threshold_p_value:
+ value_list.append((snp, BP, p_value))
+ count += 1
+
+ if value_list:
+ plink_results[chr_name] = value_list
+
+ value_list = []
+
+ line = result_fp.readline()
+ else:
+ line = result_fp.readline()
+
+ return count, p_value_dict
+
+######################################################
+# input: line: str,one line read from file
+# function: convert line from str to list;
+# output: lineList list
+#######################################################
+
+
+def build_line_list(line=""):
+ # irregular number of whitespaces between columns
+ line_list = line.strip().split(' ')
+ line_list = [item for item in line_list if item != '']
+ line_list = [item.strip() for item in line_list]
+
+ return line_list
diff --git a/gn2/wqflask/marker_regression/qtlreaper_mapping.py b/gn2/wqflask/marker_regression/qtlreaper_mapping.py
new file mode 100644
index 00000000..2c9ca1b2
--- /dev/null
+++ b/gn2/wqflask/marker_regression/qtlreaper_mapping.py
@@ -0,0 +1,193 @@
+import os
+import math
+import string
+import random
+import json
+import re
+
+from gn2.base import webqtlConfig
+from gn2.base.trait import GeneralTrait
+from gn2.base.data_set import create_dataset
+from gn2.utility.tools import flat_files, REAPER_COMMAND, TEMPDIR
+
+
+def run_reaper(this_trait, this_dataset, samples, vals, json_data, num_perm, boot_check, num_bootstrap, do_control, control_marker, manhattan_plot, first_run=True, output_files=None):
+ """Generates p-values for each marker using qtlreaper"""
+
+ if first_run:
+ if this_dataset.group.genofile != None:
+ genofile_name = this_dataset.group.genofile[:-5]
+ else:
+ genofile_name = this_dataset.group.name
+
+ trait_filename = f"{str(this_trait.name)}_{str(this_dataset.name)}_pheno"
+ gen_pheno_txt_file(samples, vals, trait_filename)
+
+ output_filename = (f"{this_dataset.group.name}_GWA_"
+ + ''.join(random.choice(string.ascii_uppercase + string.digits)
+ for _ in range(6))
+ )
+ bootstrap_filename = None
+ permu_filename = None
+
+ opt_list = []
+ if boot_check and num_bootstrap > 0:
+ bootstrap_filename = (f"{this_dataset.group.name}_BOOTSTRAP_"
+ + ''.join(random.choice(string.ascii_uppercase + string.digits)
+ for _ in range(6))
+ )
+
+ opt_list.append("-b")
+ opt_list.append(f"--n_bootstrap {str(num_bootstrap)}")
+ opt_list.append(
+ f"--bootstrap_output {webqtlConfig.GENERATED_IMAGE_DIR}{bootstrap_filename}.txt")
+ if num_perm > 0:
+ permu_filename = ("{this_dataset.group.name}_PERM_"
+ + ''.join(random.choice(string.ascii_uppercase
+ + string.digits) for _ in range(6))
+ )
+ opt_list.append("-n " + str(num_perm))
+ opt_list.append(
+ "--permu_output " + webqtlConfig.GENERATED_IMAGE_DIR + permu_filename + ".txt")
+ if control_marker != "" and do_control == "true":
+ opt_list.append("-c " + control_marker)
+ if manhattan_plot != True:
+ opt_list.append("--interval 1")
+
+ reaper_command = (REAPER_COMMAND +
+ ' --geno {0}/{1}.geno --traits {2}/gn2/{3}.txt {4} -o {5}{6}.txt'.format(flat_files('genotype'),
+
+ genofile_name,
+ TEMPDIR,
+ trait_filename,
+ " ".join(
+ opt_list),
+ webqtlConfig.GENERATED_IMAGE_DIR,
+ output_filename))
+ os.system(reaper_command)
+ else:
+ output_filename, permu_filename, bootstrap_filename = output_files
+
+ marker_obs, permu_vals, bootstrap_vals = parse_reaper_output(
+ output_filename, permu_filename, bootstrap_filename)
+
+ suggestive = 0
+ significant = 0
+ if len(permu_vals) > 0:
+ suggestive = permu_vals[int(num_perm * 0.37 - 1)]
+ significant = permu_vals[int(num_perm * 0.95 - 1)]
+
+ return (marker_obs, permu_vals, suggestive, significant, bootstrap_vals,
+ [output_filename, permu_filename, bootstrap_filename])
+
+
+def gen_pheno_txt_file(samples, vals, trait_filename):
+ """Generates phenotype file for GEMMA"""
+
+ with open(f"{TEMPDIR}/gn2/{trait_filename}.txt", "w") as outfile:
+ outfile.write("Trait\t")
+
+ filtered_sample_list = []
+ filtered_vals_list = []
+ for i, sample in enumerate(samples):
+ if vals[i] != "x":
+ filtered_sample_list.append(sample)
+ filtered_vals_list.append(vals[i])
+
+ samples_string = "\t".join(filtered_sample_list)
+ outfile.write(samples_string + "\n")
+ outfile.write("T1\t")
+ values_string = "\t".join(filtered_vals_list)
+ outfile.write(values_string)
+
+
+def parse_reaper_output(gwa_filename, permu_filename, bootstrap_filename):
+ included_markers = []
+ p_values = []
+ marker_obs = []
+
+ only_cm = False
+ only_mb = False
+
+ with open(f"{webqtlConfig.GENERATED_IMAGE_DIR}{gwa_filename}.txt") as output_file:
+ for line in output_file:
+ if line.startswith("ID\t"):
+ if len(line.split("\t")) < 8:
+ if 'cM' in line.split("\t"):
+ only_cm = True
+ else:
+ only_mb = True
+ continue
+ else:
+ marker = {}
+ marker['name'] = line.split("\t")[1]
+ try:
+ marker['chr'] = int(line.split("\t")[2])
+ except:
+ marker['chr'] = line.split("\t")[2]
+ if only_cm or only_mb:
+ if only_cm:
+ marker['cM'] = float(line.split("\t")[3])
+ else:
+ if float(line.split("\t")[3]) > 1000:
+ marker['Mb'] = float(line.split("\t")[3]) / 1000000
+ else:
+ marker['Mb'] = float(line.split("\t")[3])
+ if float(line.split("\t")[6]) != 1:
+ marker['p_value'] = float(line.split("\t")[6])
+ marker['lrs_value'] = float(line.split("\t")[4])
+ marker['lod_score'] = marker['lrs_value'] / 4.61
+ marker['additive'] = float(line.split("\t")[5])
+ else:
+ marker['cM'] = float(line.split("\t")[3])
+ if float(line.split("\t")[4]) > 1000:
+ marker['Mb'] = float(line.split("\t")[4]) / 1000000
+ else:
+ marker['Mb'] = float(line.split("\t")[4])
+ if float(line.split("\t")[7]) != 1:
+ marker['p_value'] = float(line.split("\t")[7])
+ marker['lrs_value'] = float(line.split("\t")[5])
+ marker['lod_score'] = marker['lrs_value'] / 4.61
+ marker['additive'] = float(line.split("\t")[6])
+ marker_obs.append(marker)
+
+ # ZS: Results have to be reordered because the new reaper returns results sorted alphabetically by chr for some reason, resulting in chr 1 being followed by 10, etc
+ sorted_indices = natural_sort(marker_obs)
+
+ permu_vals = []
+ if permu_filename:
+ with open(f"{webqtlConfig.GENERATED_IMAGE_DIR}{permu_filename}.txt") as permu_file:
+ for line in permu_file:
+ permu_vals.append(float(line))
+
+ bootstrap_vals = []
+ if bootstrap_filename:
+ with open(f"{webqtlConfig.GENERATED_IMAGE_DIR}{bootstrap_filename}.txt") as bootstrap_file:
+ for line in bootstrap_file:
+ bootstrap_vals.append(int(line))
+
+ marker_obs = [marker_obs[i] for i in sorted_indices]
+ if len(bootstrap_vals) > 0:
+ bootstrap_vals = [bootstrap_vals[i] for i in sorted_indices]
+
+ return marker_obs, permu_vals, bootstrap_vals
+
+
+def natural_sort(marker_list):
+ """
+ Function to naturally sort numbers + strings, adopted from user Mark Byers here: https://stackoverflow.com/questions/4836710/does-python-have-a-built-in-function-for-string-natural-sort
+ Changed to return indices instead of values, though, since the same reordering needs to be applied to bootstrap results
+ """
+
+ def convert(text):
+ if text.isdigit():
+ return int(text)
+ else:
+ if text != "M":
+ return text.lower()
+ else:
+ return "z"
+
+ alphanum_key = lambda key: [convert(c) for c in re.split(
+ '([0-9]+)', str(marker_list[key]['chr']))]
+ return sorted(list(range(len(marker_list))), key=alphanum_key)
diff --git a/gn2/wqflask/marker_regression/rqtl_mapping.py b/gn2/wqflask/marker_regression/rqtl_mapping.py
new file mode 100644
index 00000000..b7739228
--- /dev/null
+++ b/gn2/wqflask/marker_regression/rqtl_mapping.py
@@ -0,0 +1,167 @@
+import csv
+import hashlib
+import io
+import json
+import requests
+import shutil
+from typing import Dict
+from typing import List
+from typing import Optional
+from typing import TextIO
+
+import numpy as np
+
+from gn2.base.webqtlConfig import TMPDIR
+from gn2.base.trait import create_trait
+from gn2.utility.redis_tools import get_redis_conn
+from gn2.utility.tools import locate, get_setting, GN3_LOCAL_URL
+from gn2.wqflask.database import database_connection
+
+
+def run_rqtl(trait_name, vals, samples, dataset, pair_scan, mapping_scale, model, method, num_perm, perm_strata_list, do_control, control_marker, manhattan_plot, cofactors):
+ """Run R/qtl by making a request to the GN3 endpoint and reading in the output file(s)"""
+
+ pheno_file = write_phenotype_file(trait_name, samples, vals, dataset, cofactors, perm_strata_list)
+ if dataset.group.genofile:
+ geno_file = locate(dataset.group.genofile, "genotype")
+ else:
+ geno_file = locate(dataset.group.name + ".geno", "genotype")
+
+ post_data = {
+ "pheno_file": pheno_file,
+ "geno_file": geno_file,
+ "model": model,
+ "method": method,
+ "nperm": num_perm,
+ "scale": mapping_scale
+ }
+
+ if pair_scan:
+ post_data["pairscan"] = True
+
+ if cofactors:
+ covarstruct_file = write_covarstruct_file(cofactors)
+ post_data["covarstruct"] = covarstruct_file
+
+ if do_control == "true" and control_marker:
+ post_data["control"] = control_marker
+
+ if not manhattan_plot and not pair_scan:
+ post_data["interval"] = True
+ if cofactors:
+ post_data["addcovar"] = True
+
+ if perm_strata_list:
+ post_data["pstrata"] = True
+
+ rqtl_output = requests.post(GN3_LOCAL_URL + "api/rqtl/compute", data=post_data).json()
+ if num_perm > 0:
+ return rqtl_output['perm_results'], rqtl_output['suggestive'], rqtl_output['significant'], rqtl_output['results']
+ else:
+ return rqtl_output['results']
+
+
+def get_hash_of_textio(the_file: TextIO) -> str:
+ """Given a StringIO, return the hash of its contents"""
+
+ the_file.seek(0)
+ hash_of_file = hashlib.md5(the_file.read().encode()).hexdigest()
+ hash_of_file = hash_of_file.replace("/", "_") # Replace / with _ to prevent issue with filenames being translated to directories
+
+ return hash_of_file
+
+
+def write_covarstruct_file(cofactors: str) -> str:
+ """
+ Given list of cofactors (as comma-delimited string), write
+ a comma-delimited file where the first column consists of cofactor names
+ and the second column indicates whether they're numerical or categorical
+ """
+ trait_datatype_json = None
+ with database_connection(get_setting("SQL_URI")) as conn, conn.cursor() as cursor:
+ cursor.execute("SELECT value FROM TraitMetadata WHERE type='trait_data_type'")
+ trait_datatype_json = json.loads(cursor.fetchone()[0])
+
+ covar_struct_file = io.StringIO()
+ writer = csv.writer(covar_struct_file, delimiter="\t", quoting = csv.QUOTE_NONE)
+ for cofactor in cofactors.split(","):
+ datatype = trait_datatype_json[cofactor] if cofactor in trait_datatype_json else "numerical"
+ cofactor_name = cofactor.split(":")[0]
+ writer.writerow([cofactor_name, datatype])
+
+ hash_of_file = get_hash_of_textio(covar_struct_file)
+ file_path = TMPDIR + hash_of_file + ".csv"
+
+ with open(file_path, "w") as fd:
+ covar_struct_file.seek(0)
+ shutil.copyfileobj(covar_struct_file, fd)
+
+ return file_path
+
+
+def write_phenotype_file(trait_name: str,
+ samples: List[str],
+ vals: List,
+ dataset_ob,
+ cofactors: Optional[str] = None,
+ perm_strata_list: Optional[List] = None) -> TextIO:
+ """Given trait name, sample list, value list, dataset ob, and optional string
+ representing cofactors, return the file's full path/name
+
+ """
+ cofactor_data = cofactors_to_dict(cofactors, dataset_ob, samples)
+
+ pheno_file = io.StringIO()
+ writer = csv.writer(pheno_file, delimiter="\t", quoting=csv.QUOTE_NONE)
+
+ header_row = ["Samples", trait_name]
+ header_row += [cofactor for cofactor in cofactor_data]
+ if perm_strata_list:
+ header_row.append("Strata")
+
+ writer.writerow(header_row)
+ for i, sample in enumerate(samples):
+ this_row = [sample]
+ if vals[i] != "x":
+ this_row.append(str(round(float(vals[i]), 3)))
+ else:
+ this_row.append("NA")
+ for cofactor in cofactor_data:
+ this_row.append(cofactor_data[cofactor][i])
+ if perm_strata_list:
+ this_row.append(perm_strata_list[i])
+ writer.writerow(this_row)
+
+ hash_of_file = get_hash_of_textio(pheno_file)
+ file_path = TMPDIR + hash_of_file + ".csv"
+
+ with open(file_path, "w") as fd:
+ pheno_file.seek(0)
+ shutil.copyfileobj(pheno_file, fd)
+
+ return file_path
+
+
+def cofactors_to_dict(cofactors: str, dataset_ob, samples) -> Dict:
+ """Given a string of cofactors, the trait being mapped's dataset ob,
+ and list of samples, return cofactor data as a Dict
+
+ """
+ cofactor_dict = {}
+ if cofactors:
+ dataset_ob.group.get_samplelist(redis_conn=get_redis_conn())
+ sample_list = dataset_ob.group.samplelist
+ for cofactor in cofactors.split(","):
+ cofactor_name, cofactor_dataset = cofactor.split(":")
+ if cofactor_dataset == dataset_ob.name:
+ cofactor_dict[cofactor_name] = []
+ trait_ob = create_trait(dataset=dataset_ob,
+ name=cofactor_name)
+ sample_data = trait_ob.data
+ for index, sample in enumerate(samples):
+ if sample in sample_data:
+ sample_value = str(round(float(sample_data[sample].value), 3))
+ cofactor_dict[cofactor_name].append(sample_value)
+ else:
+ cofactor_dict[cofactor_name].append("NA")
+ return cofactor_dict
diff --git a/gn2/wqflask/marker_regression/run_mapping.py b/gn2/wqflask/marker_regression/run_mapping.py
new file mode 100644
index 00000000..7d2d40f7
--- /dev/null
+++ b/gn2/wqflask/marker_regression/run_mapping.py
@@ -0,0 +1,743 @@
+from gn2.base.trait import GeneralTrait
+from gn2.base import data_set # import create_dataset
+
+from pprint import pformat as pf
+
+import string
+import math
+from decimal import Decimal
+import random
+import sys
+import datetime
+import os
+import collections
+import uuid
+
+import numpy as np
+
+import pickle as pickle
+import itertools
+
+import simplejson as json
+
+from redis import Redis
+Redis = Redis()
+
+from flask import Flask, g
+
+from gn2.base.trait import GeneralTrait
+from gn2.base import data_set
+from gn2.base import species
+from gn2.base import webqtlConfig
+from gn2.utility import webqtlUtil, helper_functions, hmac, Plot, Bunch, temp_data
+from gn2.utility.redis_tools import get_redis_conn
+from gn2.wqflask.database import database_connection
+from gn2.wqflask.marker_regression import gemma_mapping, rqtl_mapping, qtlreaper_mapping, plink_mapping
+from gn2.wqflask.show_trait.SampleList import SampleList
+
+from gn2.utility.tools import locate, get_setting, locate_ignore_error, GEMMA_COMMAND, PLINK_COMMAND, TEMPDIR
+from gn2.utility.external import shell
+from gn2.base.webqtlConfig import TMPDIR, GENERATED_TEXT_DIR
+
+Redis = get_redis_conn()
+
+class RunMapping:
+
+ def __init__(self, start_vars, temp_uuid):
+ helper_functions.get_species_dataset_trait(self, start_vars)
+
+ # needed to pass temp_uuid to gn1 mapping code (marker_regression_gn1.py)
+ self.temp_uuid = temp_uuid
+
+ # ZS: Needed to zoom in or remap temp traits like PCA traits
+ if "temp_trait" in start_vars and start_vars['temp_trait'] != "False":
+ self.temp_trait = "True"
+ self.group = self.dataset.group.name
+
+ self.hash_of_inputs = start_vars['hash_of_inputs']
+ self.dataid = start_vars['dataid']
+
+ self.json_data = {}
+ self.json_data['lodnames'] = ['lod.hk']
+
+ # ZS: Sometimes a group may have a genofile that only includes a subset of samples
+ genofile_samplelist = []
+ if 'genofile' in start_vars:
+ if start_vars['genofile'] != "":
+ self.genofile_string = start_vars['genofile']
+ self.dataset.group.genofile = self.genofile_string.split(":")[
+ 0]
+ genofile_samplelist = get_genofile_samplelist(self.dataset)
+
+ all_samples_ordered = self.dataset.group.all_samples_ordered()
+
+ self.vals = []
+ self.samples = []
+ self.sample_vals = start_vars['sample_vals']
+ self.vals_hash = start_vars['vals_hash']
+ sample_val_dict = json.loads(self.sample_vals)
+ samples = sample_val_dict.keys()
+ if (len(genofile_samplelist) != 0):
+ for sample in genofile_samplelist:
+ self.samples.append(sample)
+ if sample in samples:
+ self.vals.append(sample_val_dict[sample])
+ else:
+ self.vals.append("x")
+ else:
+ for sample in self.dataset.group.samplelist:
+ if sample in samples:
+ self.vals.append(sample_val_dict[sample])
+ self.samples.append(sample)
+
+ if 'n_samples' in start_vars:
+ self.n_samples = start_vars['n_samples']
+ else:
+ self.n_samples = len([val for val in self.vals if val != "x"])
+
+ # ZS: Check if genotypes exist in the DB in order to create links for markers
+
+ self.geno_db_exists = geno_db_exists(self.dataset)
+
+ self.mapping_method = start_vars['method']
+ if "results_path" in start_vars:
+ self.mapping_results_path = start_vars['results_path']
+ else:
+ mapping_results_filename = "_".join([self.dataset.group.name, self.mapping_method, self.vals_hash]).replace("/", "_")
+ self.mapping_results_path = "{}{}.csv".format(
+ webqtlConfig.GENERATED_IMAGE_DIR, mapping_results_filename)
+
+ self.pair_scan = False
+ self.manhattan_plot = False
+ if 'manhattan_plot' in start_vars:
+ if start_vars['manhattan_plot'].lower() != "false":
+ self.color_scheme = "alternating"
+ if "color_scheme" in start_vars:
+ self.color_scheme = start_vars['color_scheme']
+ if self.color_scheme == "single":
+ self.manhattan_single_color = start_vars['manhattan_single_color']
+ self.manhattan_plot = True
+
+ self.maf = start_vars['maf'] # Minor allele frequency
+ if "use_loco" in start_vars:
+ self.use_loco = start_vars['use_loco']
+ else:
+ self.use_loco = None
+ self.suggestive = ""
+ self.significant = ""
+ if 'transform' in start_vars:
+ self.transform = start_vars['transform']
+ else:
+ self.transform = ""
+ self.score_type = "LRS" # ZS: LRS or LOD
+ self.mapping_scale = "physic"
+ if "mapping_scale" in start_vars:
+ self.mapping_scale = start_vars['mapping_scale']
+ self.num_perm = 0
+ self.perm_output = []
+ self.bootstrap_results = []
+ self.covariates = start_vars['covariates'] if "covariates" in start_vars else ""
+ self.categorical_vars = []
+
+ # ZS: This is passed to GN1 code for single chr mapping
+ self.selected_chr = -1
+ if "selected_chr" in start_vars:
+ # ZS: Needs to be -1 if showing full map; there's probably a better way to fix this
+ if int(start_vars['selected_chr']) != -1:
+ self.selected_chr = int(start_vars['selected_chr']) + 1
+ else:
+ self.selected_chr = int(start_vars['selected_chr'])
+ if "startMb" in start_vars:
+ self.startMb = start_vars['startMb']
+ if "endMb" in start_vars:
+ self.endMb = start_vars['endMb']
+ if "graphWidth" in start_vars:
+ self.graphWidth = start_vars['graphWidth']
+ if "lrsMax" in start_vars:
+ self.lrsMax = start_vars['lrsMax']
+ if "haplotypeAnalystCheck" in start_vars:
+ self.haplotypeAnalystCheck = start_vars['haplotypeAnalystCheck']
+ if "startMb" in start_vars: # This is to ensure showGenes, Legend, etc are checked the first time you open the mapping page, since startMb will only not be set during the first load
+ if "permCheck" in start_vars:
+ self.permCheck = "ON"
+ else:
+ self.permCheck = False
+ self.num_perm = int(start_vars['num_perm'])
+
+ self.LRSCheck = start_vars['LRSCheck']
+
+ if "showSNP" in start_vars:
+ self.showSNP = start_vars['showSNP']
+ else:
+ self.showSNP = False
+
+ if "showHomology" in start_vars:
+ self.showHomology = start_vars['showHomology']
+ else:
+ self.showHomology = False
+
+ if "showGenes" in start_vars:
+ self.showGenes = start_vars['showGenes']
+ else:
+ self.showGenes = False
+
+ if "viewLegend" in start_vars:
+ self.viewLegend = start_vars['viewLegend']
+ else:
+ self.viewLegend = False
+ else:
+ try:
+ if int(start_vars['num_perm']) > 0:
+ self.num_perm = int(start_vars['num_perm'])
+ except:
+ self.num_perm = 0
+
+ if self.num_perm > 0:
+ self.permCheck = "ON"
+ else:
+ self.permCheck = False
+ self.showSNP = "ON"
+ self.showGenes = "ON"
+ self.viewLegend = "ON"
+
+ # self.dataset.group.get_markers()
+ if self.mapping_method == "gemma":
+ self.first_run = True
+ self.output_files = None
+ if 'output_files' in start_vars:
+ self.output_files = start_vars['output_files']
+ # ZS: check if first run so existing result files can be used if it isn't (for example zooming on a chromosome, etc)
+ if 'first_run' in start_vars:
+ self.first_run = False
+ self.score_type = "-logP"
+ self.manhattan_plot = True
+ if self.use_loco == "True":
+ marker_obs, self.output_files = gemma_mapping.run_gemma(
+ self.this_trait, self.dataset, self.samples, self.vals, self.covariates, self.use_loco, self.maf, self.first_run, self.output_files)
+ else:
+ marker_obs, self.output_files = gemma_mapping.run_gemma(
+ self.this_trait, self.dataset, self.samples, self.vals, self.covariates, self.use_loco, self.maf, self.first_run, self.output_files)
+ results = marker_obs
+ elif self.mapping_method == "rqtl_plink":
+ results = self.run_rqtl_plink()
+ elif self.mapping_method == "rqtl_geno":
+ self.perm_strata = []
+ if "perm_strata" in start_vars and "categorical_vars" in start_vars:
+ self.categorical_vars = start_vars["categorical_vars"].split(
+ ",")
+ if len(self.categorical_vars) and start_vars["perm_strata"] == "True":
+ primary_samples = SampleList(dataset=self.dataset,
+ sample_names=self.samples,
+ this_trait=self.this_trait)
+
+ self.perm_strata = get_perm_strata(
+ self.this_trait, primary_samples, self.categorical_vars, self.samples)
+ self.score_type = "-logP"
+ self.control_marker = start_vars['control_marker']
+ self.do_control = start_vars['do_control']
+ if 'mapmethod_rqtl' in start_vars:
+ self.method = start_vars['mapmethod_rqtl']
+ else:
+ self.method = "em"
+ self.model = start_vars['mapmodel_rqtl']
+ self.pair_scan = False
+ if start_vars['pair_scan'] == "true":
+ self.pair_scan = True
+ if self.permCheck and self.num_perm > 0:
+ self.perm_output, self.suggestive, self.significant, results = rqtl_mapping.run_rqtl(
+ self.this_trait.name, self.vals, self.samples, self.dataset, self.pair_scan, self.mapping_scale, self.model, self.method, self.num_perm, self.perm_strata, self.do_control, self.control_marker, self.manhattan_plot, self.covariates)
+ else:
+ results = rqtl_mapping.run_rqtl(self.this_trait.name, self.vals, self.samples, self.dataset, self.pair_scan, self.mapping_scale, self.model, self.method,
+ self.num_perm, self.perm_strata, self.do_control, self.control_marker, self.manhattan_plot, self.covariates)
+ elif self.mapping_method == "reaper":
+ if "startMb" in start_vars: # ZS: Check if first time page loaded, so it can default to ON
+ if "additiveCheck" in start_vars:
+ self.additiveCheck = start_vars['additiveCheck']
+ else:
+ self.additiveCheck = False
+
+ if "bootCheck" in start_vars:
+ self.bootCheck = "ON"
+ else:
+ self.bootCheck = False
+ self.num_bootstrap = int(start_vars['num_bootstrap'])
+ else:
+ self.additiveCheck = "ON"
+ try:
+ if int(start_vars['num_bootstrap']) > 0:
+ self.bootCheck = "ON"
+ self.num_bootstrap = int(start_vars['num_bootstrap'])
+ else:
+ self.bootCheck = False
+ self.num_bootstrap = 0
+ except:
+ self.bootCheck = False
+ self.num_bootstrap = 0
+
+ self.control_marker = start_vars['control_marker']
+ self.do_control = start_vars['do_control']
+
+ self.first_run = True
+ self.output_files = None
+ # ZS: check if first run so existing result files can be used if it isn't (for example zooming on a chromosome, etc)
+ if 'first_run' in start_vars:
+ self.first_run = False
+ if 'output_files' in start_vars:
+ self.output_files = start_vars['output_files'].split(
+ ",")
+
+ results, self.perm_output, self.suggestive, self.significant, self.bootstrap_results, self.output_files = qtlreaper_mapping.run_reaper(self.this_trait,
+ self.dataset,
+ self.samples,
+ self.vals,
+ self.json_data,
+ self.num_perm,
+ self.bootCheck,
+ self.num_bootstrap,
+ self.do_control,
+ self.control_marker,
+ self.manhattan_plot,
+ self.first_run,
+ self.output_files)
+ elif self.mapping_method == "plink":
+ self.score_type = "-logP"
+ self.manhattan_plot = True
+ results = plink_mapping.run_plink(
+ self.this_trait, self.dataset, self.species, self.vals, self.maf)
+ #results = self.run_plink()
+
+ self.no_results = False
+ if len(results) == 0:
+ self.no_results = True
+ else:
+ if self.pair_scan == True:
+ self.figure_data = results[0]
+ self.table_data = results[1]
+ else:
+ self.qtl_results = []
+ self.results_for_browser = []
+ self.annotations_for_browser = []
+ highest_chr = 1 # This is needed in order to convert the highest chr to X/Y
+ for marker in results:
+ marker['hmac'] = hmac.data_hmac('{}:{}'.format(marker['name'], self.dataset.group.name + "Geno"))
+ if 'Mb' in marker:
+ this_ps = marker['Mb'] * 1000000
+ else:
+ this_ps = marker['cM'] * 1000000
+
+ browser_marker = dict(
+ chr=str(marker['chr']),
+ rs=marker['name'],
+ ps=this_ps,
+ url="/show_trait?trait_id=" + \
+ marker['name'] + "&dataset=" + \
+ self.dataset.group.name + "Geno"
+ )
+
+ if self.geno_db_exists == "True":
+ annot_marker = dict(
+ name=str(marker['name']),
+ chr=str(marker['chr']),
+ rs=marker['name'],
+ pos=this_ps,
+ url="/show_trait?trait_id=" + \
+ marker['name'] + "&dataset=" + \
+ self.dataset.group.name + "Geno"
+ )
+ else:
+ annot_marker = dict(
+ name=str(marker['name']),
+ chr=str(marker['chr']),
+ rs=marker['name'],
+ pos=this_ps
+ )
+
+ if 'lrs_value' in marker and marker['lrs_value'] > 0:
+ browser_marker['p_wald'] = 10**- \
+ (marker['lrs_value'] / 4.61)
+ elif 'lod_score' in marker and marker['lod_score'] > 0:
+ browser_marker['p_wald'] = 10**-(marker['lod_score'])
+ else:
+ browser_marker['p_wald'] = 0
+
+ self.results_for_browser.append(browser_marker)
+ self.annotations_for_browser.append(annot_marker)
+ if str(marker['chr']) > '0' or str(marker['chr']) == "X" or str(marker['chr']) == "X/Y":
+ if str(marker['chr']) > str(highest_chr) or str(marker['chr']) == "X" or str(marker['chr']) == "X/Y":
+ highest_chr = marker['chr']
+ if ('lod_score' in marker.keys()) or ('lrs_value' in marker.keys()):
+ if 'Mb' in marker.keys():
+ marker['display_pos'] = "Chr" + \
+ str(marker['chr']) + ": " + \
+ "{:.6f}".format(marker['Mb'])
+ elif 'cM' in marker.keys():
+ marker['display_pos'] = "Chr" + \
+ str(marker['chr']) + ": " + \
+ "{:.3f}".format(marker['cM'])
+ else:
+ marker['display_pos'] = "N/A"
+ self.qtl_results.append(marker)
+
+ total_markers = len(self.qtl_results)
+ export_mapping_results(self.dataset, self.this_trait, self.qtl_results, self.mapping_results_path,
+ self.mapping_method, self.mapping_scale, self.score_type,
+ self.transform, self.covariates, self.n_samples, self.vals_hash)
+
+ if len(self.qtl_results) > 30000:
+ self.qtl_results = trim_markers_for_figure(
+ self.qtl_results)
+ self.results_for_browser = trim_markers_for_figure(
+ self.results_for_browser)
+ filtered_annotations = []
+ for marker in self.results_for_browser:
+ for annot_marker in self.annotations_for_browser:
+ if annot_marker['rs'] == marker['rs']:
+ filtered_annotations.append(annot_marker)
+ break
+ self.annotations_for_browser = filtered_annotations
+ browser_files = write_input_for_browser(
+ self.dataset, self.results_for_browser, self.annotations_for_browser)
+ else:
+ browser_files = write_input_for_browser(
+ self.dataset, self.results_for_browser, self.annotations_for_browser)
+
+ self.trimmed_markers = trim_markers_for_table(results)
+
+ chr_lengths = get_chr_lengths(
+ self.mapping_scale, self.mapping_method, self.dataset, self.qtl_results)
+
+ # ZS: For zooming into genome browser, need to pass chromosome name instead of number
+ if self.dataset.group.species == "mouse":
+ if self.selected_chr == 20:
+ this_chr = "X"
+ else:
+ this_chr = str(self.selected_chr)
+ elif self.dataset.group.species == "rat":
+ if self.selected_chr == 21:
+ this_chr = "X"
+ else:
+ this_chr = str(self.selected_chr)
+ else:
+ if self.selected_chr == 22:
+ this_chr = "X"
+ elif self.selected_chr == 23:
+ this_chr = "Y"
+ else:
+ this_chr = str(self.selected_chr)
+
+ if self.mapping_method != "gemma":
+ if self.score_type == "LRS":
+ significant_for_browser = self.significant / 4.61
+ else:
+ significant_for_browser = self.significant
+
+ self.js_data = dict(
+ #result_score_type = self.score_type,
+ #this_trait = self.this_trait.name,
+ #data_set = self.dataset.name,
+ #maf = self.maf,
+ #manhattan_plot = self.manhattan_plot,
+ #mapping_scale = self.mapping_scale,
+ #chromosomes = chromosome_mb_lengths,
+ #qtl_results = self.qtl_results,
+ categorical_vars=self.categorical_vars,
+ chr_lengths=chr_lengths,
+ num_perm=self.num_perm,
+ perm_results=self.perm_output,
+ significant=significant_for_browser,
+ browser_files=browser_files,
+ selected_chr=this_chr,
+ total_markers=total_markers
+ )
+ else:
+ self.js_data = dict(
+ chr_lengths=chr_lengths,
+ browser_files=browser_files,
+ selected_chr=this_chr,
+ total_markers=total_markers
+ )
+
+ def run_rqtl_plink(self):
+ # os.chdir("") never do this inside a webserver!!
+
+ output_filename = webqtlUtil.genRandStr("%s_%s_" % (
+ self.dataset.group.name, self.this_trait.name))
+
+ plink_mapping.gen_pheno_txt_file_plink(
+ self.this_trait, self.dataset, self.vals, pheno_filename=output_filename)
+
+ rqtl_command = './plink --noweb --ped %s.ped --no-fid --no-parents --no-sex --no-pheno --map %s.map --pheno %s/%s.txt --pheno-name %s --maf %s --missing-phenotype -9999 --out %s%s --assoc ' % (
+ self.dataset.group.name, self.dataset.group.name, TMPDIR, plink_output_filename, self.this_trait.name, self.maf, TMPDIR, plink_output_filename)
+
+ os.system(rqtl_command)
+
+ count, p_values = self.parse_rqtl_output(plink_output_filename)
+
+ def identify_empty_samples(self):
+ no_val_samples = []
+ for sample_count, val in enumerate(self.vals):
+ if val == "x":
+ no_val_samples.append(sample_count)
+ return no_val_samples
+
+ def trim_genotypes(self, genotype_data, no_value_samples):
+ trimmed_genotype_data = []
+ for marker in genotype_data:
+ new_genotypes = []
+ for item_count, genotype in enumerate(marker):
+ if item_count in no_value_samples:
+ continue
+ try:
+ genotype = float(genotype)
+ except ValueError:
+ genotype = np.nan
+ pass
+ new_genotypes.append(genotype)
+ trimmed_genotype_data.append(new_genotypes)
+ return trimmed_genotype_data
+
+
+def export_mapping_results(dataset, trait, markers, results_path, mapping_method, mapping_scale, score_type, transform, covariates, n_samples, vals_hash):
+ if mapping_scale == "physic":
+ scale_string = "Mb"
+ else:
+ scale_string = "cM"
+ with open(results_path, "w+") as output_file:
+ output_file.write(
+ "Time/Date: " + datetime.datetime.now().strftime("%x / %X") + "\n")
+ output_file.write(
+ "Population: " + dataset.group.species.title() + " " + dataset.group.name + "\n")
+ output_file.write("Data Set: " + dataset.fullname + "\n")
+ output_file.write("Trait: " + trait.display_name + "\n")
+ output_file.write("Trait Hash: " + vals_hash + "\n")
+ output_file.write("N Samples: " + str(n_samples) + "\n")
+ output_file.write("Mapping Tool: " + str(mapping_method) + "\n")
+ if len(transform) > 0:
+ transform_text = "Transform - "
+ if transform == "qnorm":
+ transform_text += "Quantile Normalized"
+ elif transform == "log2" or transform == "log10":
+ transform_text += transform.capitalize()
+ elif transform == "sqrt":
+ transform_text += "Square Root"
+ elif transform == "zscore":
+ transform_text += "Z-Score"
+ elif transform == "invert":
+ transform_text += "Invert +/-"
+ else:
+ transform_text = ""
+ output_file.write(transform_text + "\n")
+ if dataset.type == "ProbeSet":
+ if trait.symbol:
+ output_file.write("Gene Symbol: " + trait.symbol + "\n")
+ output_file.write("Location: " + str(trait.chr) + \
+ " @ " + str(trait.mb) + " Mb\n")
+ if len(covariates) > 0:
+ output_file.write("Cofactors (dataset - trait):\n")
+ for covariate in covariates.split(","):
+ trait_name = covariate.split(":")[0]
+ dataset_name = covariate.split(":")[1]
+ output_file.write(dataset_name + " - " + trait_name + "\n")
+ output_file.write("\n")
+ output_file.write("Name,Chr,")
+ if score_type.lower() == "-logP":
+ score_type = "-logP"
+ output_file.write(scale_string + "," + score_type)
+ if "additive" in list(markers[0].keys()):
+ output_file.write(",Additive")
+ if "dominance" in list(markers[0].keys()):
+ output_file.write(",Dominance")
+ output_file.write("\n")
+ for i, marker in enumerate(markers):
+ output_file.write(marker['name'] + "," + str(marker['chr']) + ",")
+ output_file.write(str(marker[scale_string]) + ",")
+ if score_type == "-logP":
+ output_file.write(str(marker['lod_score']))
+ else:
+ output_file.write(str(marker['lrs_value']))
+ if "additive" in list(marker.keys()):
+ output_file.write("," + str(marker['additive']))
+ if "dominance" in list(marker.keys()):
+ output_file.write("," + str(marker['dominance']))
+ if i < (len(markers) - 1):
+ output_file.write("\n")
+
+
+def trim_markers_for_figure(markers):
+ if 'p_wald' in list(markers[0].keys()):
+ score_type = 'p_wald'
+ elif 'lod_score' in list(markers[0].keys()):
+ score_type = 'lod_score'
+ else:
+ score_type = 'lrs_value'
+
+ filtered_markers = []
+ low_counter = 0
+ med_counter = 0
+ high_counter = 0
+ for marker in markers:
+ if score_type == 'p_wald':
+ if marker[score_type] > 0.1:
+ if low_counter % 20 == 0:
+ filtered_markers.append(marker)
+ low_counter += 1
+ elif 0.1 >= marker[score_type] > 0.01:
+ if med_counter % 10 == 0:
+ filtered_markers.append(marker)
+ med_counter += 1
+ elif 0.01 >= marker[score_type] > 0.001:
+ if high_counter % 2 == 0:
+ filtered_markers.append(marker)
+ high_counter += 1
+ else:
+ filtered_markers.append(marker)
+ elif score_type == 'lod_score':
+ if marker[score_type] < 1:
+ if low_counter % 20 == 0:
+ filtered_markers.append(marker)
+ low_counter += 1
+ elif 1 <= marker[score_type] < 2:
+ if med_counter % 10 == 0:
+ filtered_markers.append(marker)
+ med_counter += 1
+ elif 2 <= marker[score_type] <= 3:
+ if high_counter % 2 == 0:
+ filtered_markers.append(marker)
+ high_counter += 1
+ else:
+ filtered_markers.append(marker)
+ else:
+ if marker[score_type] < 4.61:
+ if low_counter % 20 == 0:
+ filtered_markers.append(marker)
+ low_counter += 1
+ elif 4.61 <= marker[score_type] < (2 * 4.61):
+ if med_counter % 10 == 0:
+ filtered_markers.append(marker)
+ med_counter += 1
+ elif (2 * 4.61) <= marker[score_type] <= (3 * 4.61):
+ if high_counter % 2 == 0:
+ filtered_markers.append(marker)
+ high_counter += 1
+ else:
+ filtered_markers.append(marker)
+ return filtered_markers
+
+
+def trim_markers_for_table(markers):
+ if 'lod_score' in list(markers[0].keys()):
+ sorted_markers = sorted(
+ markers, key=lambda k: k['lod_score'], reverse=True)
+ else:
+ sorted_markers = sorted(
+ markers, key=lambda k: k['lrs_value'], reverse=True)
+
+ #ZS: So we end up with a list of just 2000 markers
+ if len(sorted_markers) >= 25000:
+ trimmed_sorted_markers = sorted_markers[:25000]
+ return trimmed_sorted_markers
+ else:
+ return sorted_markers
+
+
+def write_input_for_browser(this_dataset, gwas_results, annotations):
+ file_base = this_dataset.group.name + "_" + \
+ ''.join(random.choice(string.ascii_uppercase + string.digits)
+ for _ in range(6))
+ gwas_filename = file_base + "_GWAS"
+ annot_filename = file_base + "_ANNOT"
+ gwas_path = "{}/gn2/".format(TEMPDIR) + gwas_filename
+ annot_path = "{}/gn2/".format(TEMPDIR) + annot_filename
+
+ with open(gwas_path + ".json", "w") as gwas_file, open(annot_path + ".json", "w") as annot_file:
+ gwas_file.write(json.dumps(gwas_results))
+ annot_file.write(json.dumps(annotations))
+
+ return [gwas_filename, annot_filename]
+
+
+def geno_db_exists(this_dataset):
+ geno_db_name = this_dataset.group.name + "Geno"
+ try:
+ geno_db = data_set.create_dataset(
+ dataset_name=geno_db_name, get_samplelist=False)
+ return "True"
+ except:
+ return "False"
+
+
+def get_chr_lengths(mapping_scale, mapping_method, dataset, qtl_results):
+ chr_lengths = []
+ if mapping_scale == "physic":
+ with database_connection(get_setting("SQL_URI")) as conn, conn.cursor() as db_cursor:
+ for i, the_chr in enumerate(dataset.species.chromosomes.chromosomes(db_cursor)):
+ this_chr = {
+ "chr": dataset.species.chromosomes.chromosomes(db_cursor)[the_chr].name,
+ "size": str(dataset.species.chromosomes.chromosomes(db_cursor)[the_chr].length)
+ }
+ chr_lengths.append(this_chr)
+ else:
+ this_chr = 1
+ highest_pos = 0
+ for i, result in enumerate(qtl_results):
+ chr_as_num = 0
+ try:
+ chr_as_num = int(result['chr'])
+ except:
+ chr_as_num = 20
+ if chr_as_num > this_chr or i == (len(qtl_results) - 1):
+ if i == (len(qtl_results) - 1):
+ if mapping_method == "reaper":
+ highest_pos = float(result['cM']) * 1000000
+ else:
+ highest_pos = float(result['Mb']) * 1000000
+ chr_lengths.append(
+ {"chr": str(this_chr), "size": str(highest_pos)})
+ else:
+ chr_lengths.append(
+ {"chr": str(this_chr), "size": str(highest_pos)})
+ this_chr = chr_as_num
+ else:
+ if mapping_method == "reaper":
+ if float(result['cM']) > highest_pos:
+ highest_pos = float(result['cM']) * 1000000
+ else:
+ if float(result['Mb']) > highest_pos:
+ highest_pos = float(result['Mb']) * 1000000
+
+ return chr_lengths
+
+
+def get_genofile_samplelist(dataset):
+ genofile_samplelist = []
+
+ genofile_json = dataset.group.get_genofiles()
+ for genofile in genofile_json:
+ if genofile['location'] == dataset.group.genofile and 'sample_list' in genofile:
+ genofile_samplelist = genofile['sample_list']
+
+ return genofile_samplelist
+
+
+def get_perm_strata(this_trait, sample_list, categorical_vars, used_samples):
+ perm_strata_strings = []
+ for sample in used_samples:
+ if sample in list(sample_list.sample_attribute_values.keys()):
+ combined_string = ""
+ for var in categorical_vars:
+ if var in sample_list.sample_attribute_values[sample]:
+ combined_string += str(
+ sample_list.sample_attribute_values[sample][var])
+ else:
+ combined_string += "NA"
+ else:
+ combined_string = "NA"
+
+ perm_strata_strings.append(combined_string)
+
+ d = dict([(y, x + 1)
+ for x, y in enumerate(sorted(set(perm_strata_strings)))])
+ list_to_numbers = [d[x] for x in perm_strata_strings]
+ perm_strata = list_to_numbers
+
+ return perm_strata