diff options
author | Arthur Centeno | 2021-10-25 21:04:23 +0000 |
---|---|---|
committer | Arthur Centeno | 2021-10-25 21:04:23 +0000 |
commit | 499a80f138030c4de1629c043c8f9401a99894ea (patch) | |
tree | 449dcae965d13f966fb6d52625fbc86661c8c6a0 /wqflask/utility | |
parent | 6151faa9ea67af4bf4ea95fb681a9dc4319474b6 (diff) | |
parent | 700802303e5e8221a9d591ba985d6607aa61e1ce (diff) | |
download | genenetwork2-499a80f138030c4de1629c043c8f9401a99894ea.tar.gz |
Merge github.com:genenetwork/genenetwork2 into acenteno
Diffstat (limited to 'wqflask/utility')
27 files changed, 1584 insertions, 1121 deletions
diff --git a/wqflask/utility/Plot.py b/wqflask/utility/Plot.py index cce8435d..d4256a46 100644 --- a/wqflask/utility/Plot.py +++ b/wqflask/utility/Plot.py @@ -24,32 +24,36 @@ # # Last updated by GeneNetwork Core Team 2010/10/20 -from __future__ import print_function - -import piddle as pid -from pprint import pformat as pf +from PIL import ImageColor +from PIL import ImageDraw +from PIL import ImageFont from math import * -import random -import sys, os -from numarray import linear_algebra as la -from numarray import ones, array, dot, swapaxes - -import reaper -import webqtlUtil -import corestats +import utility.corestats as corestats from base import webqtlConfig - +from utility.pillow_utils import draw_rotated_text import utility.logger -logger = utility.logger.getLogger(__name__ ) +logger = utility.logger.getLogger(__name__) + +# ---- Define common colours ---- # +BLUE = ImageColor.getrgb("blue") +BLACK = ImageColor.getrgb("black") +# ---- END: Define common colours ---- # + +# ---- FONT FILES ---- # +VERDANA_FILE = "./wqflask/static/fonts/verdana.ttf" +COUR_FILE = "./wqflask/static/fonts/courbd.ttf" +TAHOMA_FILE = "./wqflask/static/fonts/tahoma.ttf" +# ---- END: FONT FILES ---- # + def cformat(d, rank=0): 'custom string format' strD = "%2.6f" % d if rank == 0: - while strD[-1] in ('0','.'): + while strD[-1] in ('0', '.'): if strD[-1] == '0' and strD[-2] == '.' and len(strD) <= 4: break elif strD[-1] == '.': @@ -65,22 +69,24 @@ def cformat(d, rank=0): strD = '0.0' return strD + def frange(start, end=None, inc=1.0): "A faster range-like function that does accept float increments..." if end == None: end = start + 0.0 start = 0.0 else: - start += 0.0 # force it to be a float + start += 0.0 # force it to be a float count = int((end - start) / inc) if start + count * inc != end: - # Need to adjust the count. AFAICT, it always comes up one short. + # Need to adjust the count. AFAICT, it always comes up one short. count += 1 L = [start] * count - for i in xrange(1, count): + for i in range(1, count): L[i] = start + i * inc return L + def find_outliers(vals): """Calculates the upper and lower bounds of a set of sample/case values @@ -88,7 +94,7 @@ def find_outliers(vals): >>> find_outliers([3.504, 5.234, 6.123, 7.234, 3.542, 5.341, 7.852, 4.555, 12.537]) (11.252500000000001, 0.5364999999999993) - >>> >>> find_outliers([9,12,15,17,31,50,7,5,6,8]) + >>> find_outliers([9,12,15,17,31,50,7,5,6,8]) (32.0, -8.0) If there are no vals, returns None for the upper and lower bounds, @@ -116,129 +122,163 @@ def find_outliers(vals): # parameter: data is either object returned by reaper permutation function (called by MarkerRegressionPage.py) # or the first object returned by direct (pair-scan) permu function (called by DirectPlotPage.py) -def plotBar(canvas, data, barColor=pid.blue, axesColor=pid.black, labelColor=pid.black, XLabel=None, YLabel=None, title=None, offset= (60, 20, 40, 40), zoom = 1): + + +def plotBar(canvas, data, barColor=BLUE, axesColor=BLACK, labelColor=BLACK, XLabel=None, YLabel=None, title=None, offset=(60, 20, 40, 40), zoom=1): + im_drawer = ImageDraw.Draw(canvas) xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset plotWidth = canvas.size[0] - xLeftOffset - xRightOffset plotHeight = canvas.size[1] - yTopOffset - yBottomOffset - if plotHeight<=0 or plotWidth<=0: - return + if plotHeight <= 0 or plotWidth <= 0: + return if len(data) < 2: - return + return max_D = max(data) min_D = min(data) - #add by NL 06-20-2011: fix the error: when max_D is infinite, log function in detScale will go wrong - if max_D == float('inf') or max_D>webqtlConfig.MAXLRS: - max_D=webqtlConfig.MAXLRS #maximum LRS value + # add by NL 06-20-2011: fix the error: when max_D is infinite, log function in detScale will go wrong + if (max_D == float('inf') or max_D > webqtlConfig.MAXLRS) and min_D < webqtlConfig.MAXLRS: + max_D = webqtlConfig.MAXLRS # maximum LRS value xLow, xTop, stepX = detScale(min_D, max_D) - #reduce data - #ZS: Used to determine number of bins for permutation output - step = ceil((xTop-xLow)/50.0) + # reduce data + # ZS: Used to determine number of bins for permutation output + step = ceil((xTop - xLow) / 50.0) j = xLow dataXY = [] Count = [] while j <= xTop: - dataXY.append(j) - Count.append(0) - j += step + dataXY.append(j) + Count.append(0) + j += step for i, item in enumerate(data): - if item == float('inf') or item>webqtlConfig.MAXLRS: - item = webqtlConfig.MAXLRS #maximum LRS value - j = int((item-xLow)/step) - Count[j] += 1 + if (item == float('inf') or item > webqtlConfig.MAXLRS) and min_D < webqtlConfig.MAXLRS: + item = webqtlConfig.MAXLRS # maximum LRS value + j = int((item - xLow) / step) + Count[j] += 1 - yLow, yTop, stepY=detScale(0,max(Count)) + yLow, yTop, stepY = detScale(0, max(Count)) - #draw data - xScale = plotWidth/(xTop-xLow) - yScale = plotHeight/(yTop-yLow) - barWidth = xScale*step + # draw data + xScale = plotWidth / (xTop - xLow) + yScale = plotHeight / (yTop - yLow) + barWidth = xScale * step for i, count in enumerate(Count): - if count: - xc = (dataXY[i]-xLow)*xScale+xLeftOffset - yc =-(count-yLow)*yScale+yTopOffset+plotHeight - canvas.drawRect(xc+2,yc,xc+barWidth-2,yTopOffset+plotHeight,edgeColor=barColor,fillColor=barColor) - - #draw drawing region - canvas.drawRect(xLeftOffset, yTopOffset, xLeftOffset+plotWidth, yTopOffset+plotHeight) - - #draw scale - scaleFont=pid.Font(ttf="cour",size=11,bold=1) - x=xLow - for i in range(int(stepX)+1): - xc=xLeftOffset+(x-xLow)*xScale - canvas.drawLine(xc,yTopOffset+plotHeight,xc,yTopOffset+plotHeight+5, color=axesColor) - strX = cformat(d=x, rank=0) - canvas.drawString(strX,xc-canvas.stringWidth(strX,font=scaleFont)/2,yTopOffset+plotHeight+14,font=scaleFont) - x+= (xTop - xLow)/stepX - - y=yLow - for i in range(int(stepY)+1): - yc=yTopOffset+plotHeight-(y-yLow)*yScale - canvas.drawLine(xLeftOffset,yc,xLeftOffset-5,yc, color=axesColor) - strY = "%d" %y - canvas.drawString(strY,xLeftOffset-canvas.stringWidth(strY,font=scaleFont)-6,yc+5,font=scaleFont) - y+= (yTop - yLow)/stepY - - #draw label - labelFont=pid.Font(ttf="tahoma",size=17,bold=0) + if count: + xc = (dataXY[i] - xLow) * xScale + xLeftOffset + yc = -(count - yLow) * yScale + yTopOffset + plotHeight + im_drawer.rectangle( + xy=((xc + 2, yc), (xc + barWidth - 2, yTopOffset + plotHeight)), + outline=barColor, fill=barColor) + + # draw drawing region + im_drawer.rectangle( + xy=((xLeftOffset, yTopOffset), + (xLeftOffset + plotWidth, yTopOffset + plotHeight)) + ) + + # draw scale + scaleFont = ImageFont.truetype(font=COUR_FILE, size=11) + x = xLow + for i in range(int(stepX) + 1): + xc = xLeftOffset + (x - xLow) * xScale + im_drawer.line( + xy=((xc, yTopOffset + plotHeight), + (xc, yTopOffset + plotHeight + 5)), + fill=axesColor) + strX = cformat(d=x, rank=0) + im_drawer.text( + text=strX, + xy=(xc - im_drawer.textsize(strX, font=scaleFont)[0] / 2, + yTopOffset + plotHeight + 14), font=scaleFont) + x += (xTop - xLow) / stepX + + y = yLow + for i in range(int(stepY) + 1): + yc = yTopOffset + plotHeight - (y - yLow) * yScale + im_drawer.line( + xy=((xLeftOffset, yc), (xLeftOffset - 5, yc)), fill=axesColor) + strY = "%d" % y + im_drawer.text( + text=strY, + xy=(xLeftOffset - im_drawer.textsize(strY, + font=scaleFont)[0] - 6, yc + 5), + font=scaleFont) + y += (yTop - yLow) / stepY + + # draw label + labelFont = ImageFont.truetype(font=TAHOMA_FILE, size=17) if XLabel: - canvas.drawString(XLabel,xLeftOffset+(plotWidth-canvas.stringWidth(XLabel,font=labelFont))/2.0, - yTopOffset+plotHeight+yBottomOffset-10,font=labelFont,color=labelColor) + im_drawer.text( + text=XLabel, + xy=(xLeftOffset + ( + plotWidth - im_drawer.textsize(XLabel, font=labelFont)[0]) / 2.0, + yTopOffset + plotHeight + yBottomOffset - 10), + font=labelFont, fill=labelColor) if YLabel: - canvas.drawString(YLabel, 19, yTopOffset+plotHeight-(plotHeight-canvas.stringWidth(YLabel,font=labelFont))/2.0, - font=labelFont,color=labelColor,angle=90) - - labelFont=pid.Font(ttf="verdana",size=16,bold=0) + draw_rotated_text(canvas, text=YLabel, + xy=(19, + yTopOffset + plotHeight - ( + plotHeight - im_drawer.textsize( + YLabel, font=labelFont)[0]) / 2.0), + font=labelFont, fill=labelColor, angle=90) + + labelFont = ImageFont.truetype(font=VERDANA_FILE, size=16) if title: - canvas.drawString(title,xLeftOffset+(plotWidth-canvas.stringWidth(title,font=labelFont))/2.0, - 20,font=labelFont,color=labelColor) + im_drawer.text( + text=title, + xy=(xLeftOffset + (plotWidth - im_drawer.textsize( + title, font=labelFont)[0]) / 2.0, + 20), + font=labelFont, fill=labelColor) # This function determines the scale of the plot -def detScaleOld(min,max): - if min>=max: + + +def detScaleOld(min, max): + if min >= max: return None elif min == -1.0 and max == 1.0: - return [-1.2,1.2,12] + return [-1.2, 1.2, 12] else: - a=max-min - b=floor(log10(a)) - c=pow(10.0,b) - if a < c*5.0: - c/=2.0 - #print a,b,c - low=c*floor(min/c) - high=c*ceil(max/c) - return [low,high,round((high-low)/c)] - -def detScale(min=0,max=0): - - if min>=max: + a = max - min + b = floor(log10(a)) + c = pow(10.0, b) + if a < c * 5.0: + c /= 2.0 + # print a,b,c + low = c * floor(min / c) + high = c * ceil(max / c) + return [low, high, round((high - low) / c)] + + +def detScale(min=0, max=0): + + if min >= max: return None elif min == -1.0 and max == 1.0: - return [-1.2,1.2,12] + return [-1.2, 1.2, 12] else: - a=max-min + a = max - min if max != 0: - max += 0.1*a + max += 0.1 * a if min != 0: - if min > 0 and min < 0.1*a: + if min > 0 and min < 0.1 * a: min = 0.0 else: - min -= 0.1*a - a=max-min - b=floor(log10(a)) - c=pow(10.0,b) - low=c*floor(min/c) - high=c*ceil(max/c) - n = round((high-low)/c) + min -= 0.1 * a + a = max - min + b = floor(log10(a)) + c = pow(10.0, b) + low = c * floor(min / c) + high = c * ceil(max / c) + n = round((high - low) / c) div = 2.0 while n < 5 or n > 15: if n < 5: @@ -246,50 +286,60 @@ def detScale(min=0,max=0): else: c *= div if div == 2.0: - div =5.0 + div = 5.0 else: - div =2.0 - low=c*floor(min/c) - high=c*ceil(max/c) - n = round((high-low)/c) + div = 2.0 + low = c * floor(min / c) + high = c * ceil(max / c) + n = round((high - low) / c) + + return [low, high, n] - return [low,high,n] def bluefunc(x): - return 1.0 / (1.0 + exp(-10*(x-0.6))) + return 1.0 / (1.0 + exp(-10 * (x - 0.6))) + def redfunc(x): - return 1.0 / (1.0 + exp(10*(x-0.5))) + return 1.0 / (1.0 + exp(10 * (x - 0.5))) + def greenfunc(x): - return 1 - pow(redfunc(x+0.2),2) - bluefunc(x-0.3) + return 1 - pow(redfunc(x + 0.2), 2) - bluefunc(x - 0.3) + def colorSpectrum(n=100): multiple = 10 if n == 1: - return [pid.Color(1,0,0)] + return [ImageColor.getrgb("rgb(100%,0%,0%)")] elif n == 2: - return [pid.Color(1,0,0),pid.Color(0,0,1)] + return [ImageColor.getrgb("100%,0%,0%)"), + ImageColor.getrgb("rgb(0%,0%,100%)")] elif n == 3: - return [pid.Color(1,0,0),pid.Color(0,1,0),pid.Color(0,0,1)] - N = n*multiple - out = [None]*N; + return [ImageColor.getrgb("rgb(100%,0%,0%)"), + ImageColor.getrgb("rgb(0%,100%,0%)"), + ImageColor.getrgb("rgb(0%,0%,100%)")] + N = n * multiple + out = [None] * N for i in range(N): - x = float(i)/N - out[i] = pid.Color(redfunc(x), greenfunc(x), bluefunc(x)); + x = float(i) / N + out[i] = ImageColor.getrgb("rgb({}%,{}%,{}%".format( + *[int(i * 100) for i in ( + redfunc(x), greenfunc(x), bluefunc(x))])) out2 = [out[0]] - step = N/float(n-1) + step = N / float(n - 1) j = 0 - for i in range(n-2): + for i in range(n - 2): j += step out2.append(out[int(j)]) out2.append(out[-1]) return out2 + def _test(): import doctest doctest.testmod() -if __name__=="__main__": - _test()
\ No newline at end of file +if __name__ == "__main__": + _test() diff --git a/wqflask/utility/TDCell.py b/wqflask/utility/TDCell.py index 8de8e050..4b0f4b1d 100644 --- a/wqflask/utility/TDCell.py +++ b/wqflask/utility/TDCell.py @@ -33,9 +33,9 @@ class TDCell: def __init__(self, html="", text="", val=0.0): - self.html = html #html, for web page - self.text = text #text value, for output to a text file - self.val = val #sort by value + self.html = html # html, for web page + self.text = text # text value, for output to a text file + self.val = val # sort by value def __str__(self): return self.text diff --git a/wqflask/utility/THCell.py b/wqflask/utility/THCell.py index dde221b5..f533dcb8 100644 --- a/wqflask/utility/THCell.py +++ b/wqflask/utility/THCell.py @@ -33,10 +33,10 @@ class THCell: def __init__(self, html="", text="", sort=1, idx=-1): - self.html = html #html, for web page - self.text = text #Column text value - self.sort = sort #0: not sortable, 1: yes - self.idx = idx #sort by value + self.html = html # html, for web page + self.text = text # Column text value + self.sort = sort # 0: not sortable, 1: yes + self.idx = idx # sort by value def __str__(self): return self.text diff --git a/wqflask/utility/__init__.py b/wqflask/utility/__init__.py index d9856eed..25273fa0 100644 --- a/wqflask/utility/__init__.py +++ b/wqflask/utility/__init__.py @@ -2,16 +2,18 @@ from pprint import pformat as pf # Todo: Move these out of __init__ -class Bunch(object): + +class Bunch: """Like a dictionary but using object notation""" - def __init__(self, **kw): - self.__dict__ = kw + + def __init__(self, **kw): + self.__dict__ = kw def __repr__(self): return pf(self.__dict__) -class Struct(object): +class Struct: '''The recursive class for building and representing objects with. From http://stackoverflow.com/a/6573827/1175849 @@ -19,7 +21,7 @@ class Struct(object): ''' def __init__(self, obj): - for k, v in obj.iteritems(): + for k, v in list(obj.items()): if isinstance(v, dict): setattr(self, k, Struct(v)) else: @@ -30,6 +32,4 @@ class Struct(object): def __repr__(self): return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for - (k, v) in self.__dict__.iteritems())) - - + (k, v) in list(self.__dict__.items()))) diff --git a/wqflask/utility/after.py b/wqflask/utility/after.py index b628a0a4..2b560e48 100644 --- a/wqflask/utility/after.py +++ b/wqflask/utility/after.py @@ -1,5 +1,3 @@ -from __future__ import print_function, division, absolute_import - """ See: http://flask.pocoo.org/docs/patterns/deferredcallbacks/#deferred-callbacks @@ -9,8 +7,9 @@ from flask import g from wqflask import app + def after_this_request(f): if not hasattr(g, 'after_request_callbacks'): g.after_request_callbacks = [] g.after_request_callbacks.append(f) - return f
\ No newline at end of file + return f diff --git a/wqflask/utility/authentication_tools.py b/wqflask/utility/authentication_tools.py new file mode 100644 index 00000000..c4801c8c --- /dev/null +++ b/wqflask/utility/authentication_tools.py @@ -0,0 +1,153 @@ +import json +import requests + +from deprecated import deprecated +from flask import g +from base import webqtlConfig + +from utility.redis_tools import (get_redis_conn, + get_resource_info, + get_resource_id, + add_resource) +from utility.tools import GN_PROXY_URL + +Redis = get_redis_conn() + +def check_resource_availability(dataset, trait_id=None): + # At least for now assume temporary entered traits are accessible + if type(dataset) == str or dataset.type == "Temp": + return webqtlConfig.DEFAULT_PRIVILEGES + + resource_id = get_resource_id(dataset, trait_id) + + # ZS: This should never be false, but it's technically possible if + # a non-Temp dataset somehow had a type other than + # Publish/ProbeSet/Geno + if resource_id: + resource_info = get_resource_info(resource_id) + + # If resource isn't already in redis, add it with default + # privileges + if not resource_info: + resource_info = add_new_resource(dataset, trait_id) + + # Check if super-user - we should probably come up with some + # way to integrate this into the proxy + if g.user_session.user_id in Redis.smembers("super_users"): + return webqtlConfig.SUPER_PRIVILEGES + + response = None + + the_url = GN_PROXY_URL + "available?resource={}&user={}".format( + resource_id, g.user_session.user_id) + + try: + response = json.loads(requests.get(the_url).content) + except: + response = resource_info['default_mask'] + + return response + + +def add_new_resource(dataset, trait_id=None): + resource_ob = { + 'owner_id': "none", # webqtlConfig.DEFAULT_OWNER_ID, + 'default_mask': webqtlConfig.DEFAULT_PRIVILEGES, + 'group_masks': {} + } + + if dataset.type == "Publish": + group_code = get_group_code(dataset) + if group_code is None: + group_code = "" + resource_ob['name'] = group_code + "_" + str(trait_id) + resource_ob['data'] = { + 'dataset': dataset.id, + 'trait': trait_id + } + resource_ob['type'] = 'dataset-publish' + elif dataset.type == "Geno": + resource_ob['name'] = dataset.name + resource_ob['data'] = { + 'dataset': dataset.id + } + resource_ob['type'] = 'dataset-geno' + else: + resource_ob['name'] = dataset.name + resource_ob['data'] = { + 'dataset': dataset.id + } + resource_ob['type'] = 'dataset-probeset' + + resource_info = add_resource(resource_ob, update=False) + + return resource_info + + +def get_group_code(dataset): + results = g.db.execute( + "SELECT InbredSetCode from InbredSet where Name='{}'".format( + dataset.group.name)).fetchone() + if results[0]: + return results[0] + else: + return "" + + +def check_admin(resource_id=None): + the_url = GN_PROXY_URL + "available?resource={}&user={}".format( + resource_id, g.user_session.user_id) + try: + response = json.loads(requests.get(the_url).content)['admin'] + except: + resource_info = get_resource_info(resource_id) + response = resource_info['default_mask']['admin'] + + if type(response) is list: + if 'edit-admins' in response: + return 'edit_admins' + elif 'edit-access' in response: + return 'edit-access' + + return response + + +def check_owner(dataset=None, trait_id=None, resource_id=None): + if resource_id: + resource_info = get_resource_info(resource_id) + if g.user_session.user_id == resource_info['owner_id']: + return resource_id + else: + resource_id = get_resource_id(dataset, trait_id) + if resource_id: + resource_info = get_resource_info(resource_id) + if g.user_session.user_id == resource_info['owner_id']: + return resource_id + + return False + + +@deprecated +def check_owner_or_admin(dataset=None, trait_id=None, resource_id=None): + if not resource_id: + if dataset.type == "Temp": + return "not-admin" + else: + resource_id = get_resource_id(dataset, trait_id) + + try: + user_id = g.user_session.user_id.encode('utf-8') + except: + user_id = g.user_session.user_id + + if user_id in Redis.smembers("super_users"): + return "owner" + + resource_info = get_resource_info(resource_id) + if resource_info: + if user_id == resource_info['owner_id']: + return "owner" + else: + return check_admin(resource_id) + + return "not-admin" diff --git a/wqflask/utility/benchmark.py b/wqflask/utility/benchmark.py index 8f1c916b..6ece2f21 100644 --- a/wqflask/utility/benchmark.py +++ b/wqflask/utility/benchmark.py @@ -1,14 +1,13 @@ -from __future__ import print_function, division, absolute_import - import collections import inspect import time from utility.tools import LOG_BENCH from utility.logger import getLogger -logger = getLogger(__name__ ) +logger = getLogger(__name__) + -class Bench(object): +class Bench: entries = collections.OrderedDict() def __init__(self, name=None, write_output=LOG_BENCH): @@ -20,7 +19,8 @@ class Bench(object): if self.name: logger.debug("Starting benchmark: %s" % (self.name)) else: - logger.debug("Starting benchmark at: %s [%i]" % (inspect.stack()[1][3], inspect.stack()[1][2])) + logger.debug("Starting benchmark at: %s [%i]" % ( + inspect.stack()[1][3], inspect.stack()[1][2])) self.start_time = time.time() def __exit__(self, type, value, traceback): @@ -34,14 +34,16 @@ class Bench(object): logger.info(" %s took: %f seconds" % (name, (time_taken))) if self.name: - Bench.entries[self.name] = Bench.entries.get(self.name, 0) + time_taken + Bench.entries[self.name] = Bench.entries.get( + self.name, 0) + time_taken @classmethod def report(cls): - total_time = sum((time_taken for time_taken in cls.entries.itervalues())) + total_time = sum( + (time_taken for time_taken in list(cls.entries.values()))) print("\nTiming report\n") - for name, time_taken in cls.entries.iteritems(): - percent = int(round((time_taken/total_time) * 100)) + for name, time_taken in list(cls.entries.items()): + percent = int(round((time_taken / total_time) * 100)) print("[{}%] {}: {}".format(percent, name, time_taken)) print() diff --git a/wqflask/utility/chunks.py b/wqflask/utility/chunks.py index b0e33c08..484b5de6 100644 --- a/wqflask/utility/chunks.py +++ b/wqflask/utility/chunks.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, print_function, division - import math import time @@ -28,69 +26,6 @@ def divide_into_chunks(the_list, number_chunks): chunks = [] for counter in range(0, length, chunksize): - chunks.append(the_list[counter:counter+chunksize]) + chunks.append(the_list[counter:counter + chunksize]) return chunks - -def _confirm_chunk(original, result): - all_chunked = [] - for chunk in result: - all_chunked.extend(chunk) - print("length of all chunked:", len(all_chunked)) - assert original == all_chunked, "You didn't chunk right" - - -def _chunk_test(divide_func): - import random - random.seed(7) - - number_exact = 0 - total_amount_off = 0 - - for test in range(1, 1001): - print("\n\ntest:", test) - number_chunks = random.randint(1, 20) - number_elements = random.randint(0, 100) - the_list = list(range(1, number_elements)) - result = divide_func(the_list, number_chunks) - - print("Dividing list of length {} into approximately {} chunks - got {} chunks".format( - len(the_list), number_chunks, len(result))) - print("result:", result) - - _confirm_chunk(the_list, result) - - amount_off = abs(number_chunks - len(result)) - if amount_off == 0: - number_exact += 1 - else: - total_amount_off += amount_off - - - print("\n{} exact out of {} [Total amount off: {}]".format(number_exact, - test, - total_amount_off)) - assert number_exact == 558 - assert total_amount_off == 1580 - return number_exact, total_amount_off - - -def _main(): - info = dict() - #funcs = (("sam", sam_divide_into_chunks), ("zach", zach_divide_into_chunks)) - funcs = (("only one", divide_into_chunks),) - for name, func in funcs: - start = time.time() - number_exact, total_amount_off = _chunk_test(func) - took = time.time() - start - info[name] = dict(number_exact=number_exact, - total_amount_off=total_amount_off, - took=took) - - print("info is:", info) - -if __name__ == '__main__': - _main() - print("\nConfirming doctests...") - import doctest - doctest.testmod() diff --git a/wqflask/utility/corestats.py b/wqflask/utility/corestats.py index 67ca3ad3..da0a21db 100644 --- a/wqflask/utility/corestats.py +++ b/wqflask/utility/corestats.py @@ -15,7 +15,9 @@ import sys -#ZS: Should switch to using some third party library for this; maybe scipy has an equivalent +# ZS: Should switch to using some third party library for this; maybe scipy has an equivalent + + class Stats: def __init__(self, sequence): @@ -63,7 +65,8 @@ class Stats: if len(self.sequence) < 1: value = None elif (percentile >= 100): - sys.stderr.write('ERROR: percentile must be < 100. you supplied: %s\n'% percentile) + sys.stderr.write( + 'ERROR: percentile must be < 100. you supplied: %s\n' % percentile) value = None else: element_idx = int(len(self.sequence) * (percentile / 100.0)) @@ -80,4 +83,4 @@ class Stats: # stats = corestats.Stats(sequence) # print stats.avg() # print stats.percentile(90) -# -------------------------------------------
\ No newline at end of file +# ------------------------------------------- diff --git a/wqflask/utility/corr_result_helpers.py b/wqflask/utility/corr_result_helpers.py index b543c589..ea3ababf 100644 --- a/wqflask/utility/corr_result_helpers.py +++ b/wqflask/utility/corr_result_helpers.py @@ -14,15 +14,11 @@ def normalize_values(a_values, b_values): min_length = min(len(a_values), len(b_values)) a_new = [] b_new = [] - for counter in range(min_length): - if (a_values[counter] or a_values[counter] == 0) and (b_values[counter] or b_values[counter] == 0): - a_new.append(a_values[counter]) - b_new.append(b_values[counter]) - - num_overlap = len(a_new) - assert num_overlap == len(b_new), "Lengths should be the same" - - return a_new, b_new, num_overlap + for a, b in zip(a_values, b_values): + if not (a == None or b == None): + a_new.append(a) + b_new.append(b) + return a_new, b_new, len(a_new) def common_keys(a_samples, b_samples): @@ -37,20 +33,10 @@ def common_keys(a_samples, b_samples): def normalize_values_with_samples(a_samples, b_samples): common_samples = common_keys(a_samples, b_samples) - a_new = {} b_new = {} for sample in common_samples: a_new[sample] = a_samples[sample] b_new[sample] = b_samples[sample] - num_overlap = len(a_new) - assert num_overlap == len(b_new), "Lengths should be the same" - - return a_new, b_new, num_overlap - - - -if __name__ == '__main__': - import doctest - doctest.testmod()
\ No newline at end of file + return a_new, b_new, len(a_new) diff --git a/wqflask/utility/db_tools.py b/wqflask/utility/db_tools.py index 4034f39c..6e19778f 100644 --- a/wqflask/utility/db_tools.py +++ b/wqflask/utility/db_tools.py @@ -1,6 +1,5 @@ -from __future__ import absolute_import, print_function, division +from MySQLdb import escape_string as escape_ -from MySQLdb import escape_string as escape def create_in_clause(items): """Create an in clause for mysql""" @@ -8,8 +7,11 @@ def create_in_clause(items): in_clause = '( {} )'.format(in_clause) return in_clause + def mescape(*items): """Multiple escape""" - escaped = [escape(str(item)) for item in items] - #print("escaped is:", escaped) - return escaped + return [escape_(str(item)).decode('utf8') for item in items] + + +def escape(string_): + return escape_(string_).decode('utf8') diff --git a/wqflask/utility/elasticsearch_tools.py b/wqflask/utility/elasticsearch_tools.py index 15cdd0bc..eae3ba03 100644 --- a/wqflask/utility/elasticsearch_tools.py +++ b/wqflask/utility/elasticsearch_tools.py @@ -47,11 +47,14 @@ logger = getLogger(__name__) from utility.tools import ELASTICSEARCH_HOST, ELASTICSEARCH_PORT + def test_elasticsearch_connection(): - es = Elasticsearch(['http://'+ELASTICSEARCH_HOST+":"+str(ELASTICSEARCH_PORT)+'/'], verify_certs=True) + es = Elasticsearch(['http://' + ELASTICSEARCH_HOST + \ + ":" + str(ELASTICSEARCH_PORT) + '/'], verify_certs=True) if not es.ping(): logger.warning("Elasticsearch is DOWN") + def get_elasticsearch_connection(for_user=True): """Return a connection to ES. Returns None on failure""" logger.info("get_elasticsearch_connection") @@ -59,7 +62,7 @@ def get_elasticsearch_connection(for_user=True): try: assert(ELASTICSEARCH_HOST) assert(ELASTICSEARCH_PORT) - logger.info("ES HOST",ELASTICSEARCH_HOST) + logger.info("ES HOST", ELASTICSEARCH_HOST) es = Elasticsearch([{ "host": ELASTICSEARCH_HOST, "port": ELASTICSEARCH_PORT @@ -77,6 +80,7 @@ def get_elasticsearch_connection(for_user=True): return es + def setup_users_index(es_connection): if es_connection: index_settings = { @@ -85,20 +89,24 @@ def setup_users_index(es_connection): "type": "keyword"}}} es_connection.indices.create(index='users', ignore=400) - es_connection.indices.put_mapping(body=index_settings, index="users", doc_type="local") + es_connection.indices.put_mapping( + body=index_settings, index="users", doc_type="local") + def get_user_by_unique_column(es, column_name, column_value, index="users", doc_type="local"): return get_item_by_unique_column(es, column_name, column_value, index=index, doc_type=doc_type) + def save_user(es, user, user_id): es_save_data(es, "users", "local", user, user_id) + def get_item_by_unique_column(es, column_name, column_value, index, doc_type): item_details = None try: response = es.search( - index = index, doc_type = doc_type, body = { - "query": { "match": { column_name: column_value } } + index=index, doc_type=doc_type, body={ + "query": {"match": {column_name: column_value}} }) if len(response["hits"]["hits"]) > 0: item_details = response["hits"]["hits"][0]["_source"] @@ -106,7 +114,8 @@ def get_item_by_unique_column(es, column_name, column_value, index, doc_type): pass return item_details + def es_save_data(es, index, doc_type, data_item, data_id,): from time import sleep es.create(index, doc_type, body=data_item, id=data_id) - sleep(1) # Delay 1 second to allow indexing + sleep(1) # Delay 1 second to allow indexing diff --git a/wqflask/utility/external.py b/wqflask/utility/external.py index 50afea08..805d2ffe 100644 --- a/wqflask/utility/external.py +++ b/wqflask/utility/external.py @@ -4,6 +4,7 @@ import os import sys import subprocess + def shell(command): if subprocess.call(command, shell=True) != 0: - raise Exception("ERROR: failed on "+command) + raise Exception("ERROR: failed on " + command) diff --git a/wqflask/utility/formatting.py b/wqflask/utility/formatting.py index e53dda22..1da3e9b7 100644 --- a/wqflask/utility/formatting.py +++ b/wqflask/utility/formatting.py @@ -28,21 +28,20 @@ def numify(number, singular=None, plural=None): '12,334 hippopotami' """ - num_repr = {1 : "one", - 2 : "two", - 3 : "three", - 4 : "four", - 5 : "five", - 6 : "six", - 7 : "seven", - 8 : "eight", - 9 : "nine", - 10 : "ten", - 11 : "eleven", - 12 : "twelve"} - - #Below line commented out cause doesn't work in Python 2.4 - #assert all((singular, plural)) or not any((singular, plural)), "Need to pass two words or none" + num_repr = {0: "zero", + 1: "one", + 2: "two", + 3: "three", + 4: "four", + 5: "five", + 6: "six", + 7: "seven", + 8: "eight", + 9: "nine", + 10: "ten", + 11: "eleven", + 12: "twelve"} + if number == 1: word = singular else: diff --git a/wqflask/utility/gen_geno_ob.py b/wqflask/utility/gen_geno_ob.py index 23b0b650..e619b7b6 100644 --- a/wqflask/utility/gen_geno_ob.py +++ b/wqflask/utility/gen_geno_ob.py @@ -1,9 +1,8 @@ -from __future__ import absolute_import, division, print_function - import utility.logger -logger = utility.logger.getLogger(__name__ ) +logger = utility.logger.getLogger(__name__) + -class genotype(object): +class genotype: """ Replacement for reaper.Dataset so we can remove qtlreaper use while still generating mapping output figure """ @@ -20,7 +19,7 @@ class genotype(object): self.filler = False self.mb_exists = False - #ZS: This is because I'm not sure if some files switch the column that contains Mb/cM positions; might be unnecessary + # ZS: This is because I'm not sure if some files switch the column that contains Mb/cM positions; might be unnecessary self.cm_column = 2 self.mb_column = 3 @@ -38,14 +37,16 @@ class genotype(object): return len(self.chromosomes) def read_rdata_output(self, qtl_results): - #ZS: This is necessary because R/qtl requires centimorgan marker positions, which it normally gets from the .geno file, but that doesn't exist for HET3-ITP (which only has RData), so it needs to read in the marker cM positions from the results - self.chromosomes = [] #ZS: Overwriting since the .geno file's contents are just placeholders + # ZS: This is necessary because R/qtl requires centimorgan marker positions, which it normally gets from the .geno file, but that doesn't exist for HET3-ITP (which only has RData), so it needs to read in the marker cM positions from the results + # ZS: Overwriting since the .geno file's contents are just placeholders + self.chromosomes = [] - this_chr = "" #ZS: This is so it can track when the chromosome changes as it iterates through markers + this_chr = "" # ZS: This is so it can track when the chromosome changes as it iterates through markers chr_ob = None for marker in qtl_results: locus = Locus(self) - if (str(marker['chr']) != this_chr) and this_chr != "X": #ZS: This is really awkward but works as a temporary fix + # ZS: This is really awkward but works as a temporary fix + if (str(marker['chr']) != this_chr) and this_chr != "X": if this_chr != "": self.chromosomes.append(chr_ob) this_chr = str(marker['chr']) @@ -70,7 +71,7 @@ class genotype(object): with open(filename, 'r') as geno_file: lines = geno_file.readlines() - this_chr = "" #ZS: This is so it can track when the chromosome changes as it iterates through markers + this_chr = "" # ZS: This is so it can track when the chromosome changes as it iterates through markers chr_ob = None for line in lines: if line[0] == "#": @@ -121,7 +122,8 @@ class genotype(object): self.chromosomes.append(chr_ob) -class Chr(object): + +class Chr: def __init__(self, name, geno_ob): self.name = name self.loci = [] @@ -142,8 +144,9 @@ class Chr(object): def add_marker(self, marker_row): self.loci.append(Locus(self.geno_ob, marker_row)) -class Locus(object): - def __init__(self, geno_ob, marker_row = None): + +class Locus: + def __init__(self, geno_ob, marker_row=None): self.chr = None self.name = None self.cM = None @@ -155,9 +158,11 @@ class Locus(object): try: self.cM = float(marker_row[geno_ob.cm_column]) except: - self.cM = float(marker_row[geno_ob.mb_column]) if geno_ob.mb_exists else 0 + self.cM = float( + marker_row[geno_ob.mb_column]) if geno_ob.mb_exists else 0 try: - self.Mb = float(marker_row[geno_ob.mb_column]) if geno_ob.mb_exists else None + self.Mb = float( + marker_row[geno_ob.mb_column]) if geno_ob.mb_exists else None except: self.Mb = self.cM @@ -175,7 +180,7 @@ class Locus(object): start_pos = 3 for allele in marker_row[start_pos:]: - if allele in geno_table.keys(): + if allele in list(geno_table.keys()): self.genotype.append(geno_table[allele]) - else: #ZS: Some genotype appears that isn't specified in the metadata, make it unknown - self.genotype.append("U")
\ No newline at end of file + else: # ZS: Some genotype appears that isn't specified in the metadata, make it unknown + self.genotype.append("U") diff --git a/wqflask/utility/genofile_parser.py b/wqflask/utility/genofile_parser.py index af306731..86d9823e 100644 --- a/wqflask/utility/genofile_parser.py +++ b/wqflask/utility/genofile_parser.py @@ -1,7 +1,6 @@ # CTL analysis for GN2 # Author / Maintainer: Danny Arends <Danny.Arends@gmail.com> -from __future__ import print_function, division, absolute_import import sys import os import glob @@ -13,88 +12,89 @@ import simplejson as json from pprint import pformat as pf -class Marker(object): - def __init__(self): - self.name = None - self.chr = None - self.cM = None - self.Mb = None - self.genotypes = [] +class Marker: + def __init__(self): + self.name = None + self.chr = None + self.cM = None + self.Mb = None + self.genotypes = [] -class ConvertGenoFile(object): - def __init__(self, input_file): - self.mb_exists = False - self.cm_exists = False - self.markers = [] +class ConvertGenoFile: - self.latest_row_pos = None - self.latest_col_pos = None + def __init__(self, input_file): + self.mb_exists = False + self.cm_exists = False + self.markers = [] - self.latest_row_value = None - self.latest_col_value = None - self.input_fh = open(input_file) - print("!!!!!!!!!!!!!!!!PARSER!!!!!!!!!!!!!!!!!!") - self.haplotype_notation = { - '@mat': "1", - '@pat': "2", - '@het': "-999", - '@unk': "-999" - } - self.configurations = {} + self.latest_row_pos = None + self.latest_col_pos = None - def process_rows(self): - for self.latest_row_pos, row in enumerate(self.input_fh): - self.latest_row_value = row - # Take care of headers - if not row.strip(): - continue - if row.startswith('#'): - continue - if row.startswith('Chr'): - if 'Mb' in row.split(): - self.mb_exists = True - if 'cM' in row.split(): - self.cm_exists = True - skip = 2 + self.cm_exists + self.mb_exists - self.individuals = row.split()[skip:] - continue - if row.startswith('@'): - key, _separater, value = row.partition(':') - key = key.strip() - value = value.strip() - if key in self.haplotype_notation: - self.configurations[value] = self.haplotype_notation[key] - continue - if not len(self.configurations): - raise EmptyConfigurations - yield row + self.latest_row_value = None + self.latest_col_value = None + self.input_fh = open(input_file) + print("!!!!!!!!!!!!!!!!PARSER!!!!!!!!!!!!!!!!!!") + self.haplotype_notation = { + '@mat': "1", + '@pat': "2", + '@het': "-999", + '@unk': "-999" + } + self.configurations = {} - def process_csv(self): - for row in self.process_rows(): - row_items = row.split("\t") + def process_rows(self): + for self.latest_row_pos, row in enumerate(self.input_fh): + self.latest_row_value = row + # Take care of headers + if not row.strip(): + continue + if row.startswith('#'): + continue + if row.startswith('Chr'): + if 'Mb' in row.split(): + self.mb_exists = True + if 'cM' in row.split(): + self.cm_exists = True + skip = 2 + self.cm_exists + self.mb_exists + self.individuals = row.split()[skip:] + continue + if row.startswith('@'): + key, _separater, value = row.partition(':') + key = key.strip() + value = value.strip() + if key in self.haplotype_notation: + self.configurations[value] = self.haplotype_notation[key] + continue + if not len(self.configurations): + raise EmptyConfigurations + yield row - this_marker = Marker() - this_marker.name = row_items[1] - this_marker.chr = row_items[0] - if self.cm_exists and self.mb_exists: - this_marker.cM = row_items[2] - this_marker.Mb = row_items[3] - genotypes = row_items[4:] - elif self.cm_exists: - this_marker.cM = row_items[2] - genotypes = row_items[3:] - elif self.mb_exists: - this_marker.Mb = row_items[2] - genotypes = row_items[3:] - else: - genotypes = row_items[2:] - for item_count, genotype in enumerate(genotypes): - if genotype.upper().strip() in self.configurations: - this_marker.genotypes.append(self.configurations[genotype.upper().strip()]) - else: - print("WARNING:", genotype.upper()) - this_marker.genotypes.append("NA") - self.markers.append(this_marker.__dict__) + def process_csv(self): + for row in self.process_rows(): + row_items = row.split("\t") + this_marker = Marker() + this_marker.name = row_items[1] + this_marker.chr = row_items[0] + if self.cm_exists and self.mb_exists: + this_marker.cM = row_items[2] + this_marker.Mb = row_items[3] + genotypes = row_items[4:] + elif self.cm_exists: + this_marker.cM = row_items[2] + genotypes = row_items[3:] + elif self.mb_exists: + this_marker.Mb = row_items[2] + genotypes = row_items[3:] + else: + genotypes = row_items[2:] + for item_count, genotype in enumerate(genotypes): + if genotype.upper().strip() in self.configurations: + this_marker.genotypes.append( + self.configurations[genotype.upper().strip()]) + else: + print("WARNING:", genotype.upper()) + this_marker.genotypes.append("NA") + self.markers.append(this_marker.__dict__) diff --git a/wqflask/utility/helper_functions.py b/wqflask/utility/helper_functions.py index e7c04fef..27dd0729 100644 --- a/wqflask/utility/helper_functions.py +++ b/wqflask/utility/helper_functions.py @@ -1,72 +1,68 @@ -from __future__ import absolute_import, print_function, division - -from base.trait import GeneralTrait from base import data_set +from base.trait import create_trait from base.species import TheSpecies from utility import hmac -from flask import Flask, g +from flask import g import logging -logger = logging.getLogger(__name__ ) +logger = logging.getLogger(__name__) def get_species_dataset_trait(self, start_vars): - #assert type(read_genotype) == type(bool()), "Expecting boolean value for read_genotype" - if "temp_trait" in start_vars.keys(): - if start_vars['temp_trait'] == "True": - self.dataset = data_set.create_dataset(dataset_name = "Temp", dataset_type = "Temp", group_name = start_vars['group']) - else: - self.dataset = data_set.create_dataset(start_vars['dataset']) + if "temp_trait" in list(start_vars.keys()): + if start_vars['temp_trait'] == "True": + self.dataset = data_set.create_dataset( + dataset_name="Temp", + dataset_type="Temp", + group_name=start_vars['group']) + else: + self.dataset = data_set.create_dataset(start_vars['dataset']) else: - self.dataset = data_set.create_dataset(start_vars['dataset']) - logger.debug("After creating dataset") + self.dataset = data_set.create_dataset(start_vars['dataset']) self.species = TheSpecies(dataset=self.dataset) - logger.debug("After creating species") - self.this_trait = GeneralTrait(dataset=self.dataset, + self.this_trait = create_trait(dataset=self.dataset, name=start_vars['trait_id'], cellid=None, get_qtl_info=True) - logger.debug("After creating trait") - - #if read_genotype: - #self.dataset.group.read_genotype_file() - #self.genotype = self.dataset.group.genotype - def get_trait_db_obs(self, trait_db_list): - if isinstance(trait_db_list, basestring): + if isinstance(trait_db_list, str): trait_db_list = trait_db_list.split(",") self.trait_list = [] for trait in trait_db_list: data, _separator, hmac_string = trait.rpartition(':') data = data.strip() - assert hmac_string==hmac.hmac_creation(data), "Data tampering?" - trait_name, dataset_name = data.split(":") + assert hmac_string == hmac.hmac_creation(data), "Data tampering?" + trait_name, dataset_name = data.split(":")[:2] if dataset_name == "Temp": - dataset_ob = data_set.create_dataset(dataset_name=dataset_name, dataset_type="Temp", group_name=trait_name.split("_")[2]) + dataset_ob = data_set.create_dataset( + dataset_name=dataset_name, dataset_type="Temp", + group_name=trait_name.split("_")[2]) else: dataset_ob = data_set.create_dataset(dataset_name) - trait_ob = GeneralTrait(dataset=dataset_ob, - name=trait_name, - cellid=None) - self.trait_list.append((trait_ob, dataset_ob)) - -def get_species_groups(): + trait_ob = create_trait(dataset=dataset_ob, + name=trait_name, + cellid=None) + if trait_ob: + self.trait_list.append((trait_ob, dataset_ob)) - species_query = "SELECT SpeciesId, MenuName FROM Species" - species_ids_and_names = g.db.execute(species_query).fetchall() - species_and_groups = [] - for species_id, species_name in species_ids_and_names: - this_species_groups = {} - this_species_groups['species'] = species_name - groups_query = "SELECT InbredSetName FROM InbredSet WHERE SpeciesId = %s" % (species_id) - groups = [group[0] for group in g.db.execute(groups_query).fetchall()] - - this_species_groups['groups'] = groups - species_and_groups.append(this_species_groups) +def get_species_groups(): + """Group each species into a group""" + _menu = {} - return species_and_groups + for species, group_name in g.db.execute( + "SELECT s.MenuName, i.InbredSetName FROM InbredSet i " + "INNER JOIN Species s ON s.SpeciesId = i.SpeciesId " + "ORDER BY i.SpeciesId ASC, i.Name ASC").fetchall(): + if species in _menu: + if _menu.get(species): + _menu = _menu[species].append(group_name) + else: + _menu[species] = [group_name] + return [{"species": key, + "groups": value} for key, value in + list(_menu.items())] diff --git a/wqflask/utility/hmac.py b/wqflask/utility/hmac.py index d8a0eace..d6e515ed 100644 --- a/wqflask/utility/hmac.py +++ b/wqflask/utility/hmac.py @@ -1,16 +1,21 @@ -from __future__ import print_function, division, absolute_import - import hmac import hashlib +from deprecated import deprecated +from flask import url_for + from wqflask import app + +@deprecated("This function leads to circular imports. " + "If possible use wqflask.decorators.create_hmac instead.") def hmac_creation(stringy): """Helper function to create the actual hmac""" secret = app.config['SECRET_HMAC_CODE'] - - hmaced = hmac.new(secret, stringy, hashlib.sha1) + hmaced = hmac.new(bytearray(secret, "latin-1"), + bytearray(stringy, "utf-8"), + hashlib.sha1) hm = hmaced.hexdigest() # ZS: Leaving the below comment here to ask Pjotr about # "Conventional wisdom is that you don't lose much in terms of security if you throw away up to half of the output." @@ -18,10 +23,12 @@ def hmac_creation(stringy): hm = hm[:20] return hm + def data_hmac(stringy): - """Takes arbitray data string and appends :hmac so we know data hasn't been tampered with""" + """Takes arbitrary data string and appends :hmac so we know data hasn't been tampered with""" return stringy + ":" + hmac_creation(stringy) + def url_for_hmac(endpoint, **values): """Like url_for but adds an hmac at the end to insure the url hasn't been tampered with""" @@ -34,5 +41,6 @@ def url_for_hmac(endpoint, **values): combiner = "?" return url + combiner + "hm=" + hm + app.jinja_env.globals.update(url_for_hmac=url_for_hmac, - data_hmac=data_hmac)
\ No newline at end of file + data_hmac=data_hmac) diff --git a/wqflask/utility/logger.py b/wqflask/utility/logger.py index 510b1041..d706e32a 100644 --- a/wqflask/utility/logger.py +++ b/wqflask/utility/logger.py @@ -35,6 +35,7 @@ import datetime from utility.tools import LOG_LEVEL, LOG_LEVEL_DEBUG, LOG_SQL + class GNLogger: """A logger class with some additional functionality, such as multiple parameter logging, SQL logging, timing, colors, and lazy @@ -42,52 +43,52 @@ class GNLogger: """ - def __init__(self,name): + def __init__(self, name): self.logger = logging.getLogger(name) - def setLevel(self,value): + def setLevel(self, value): """Set the undelying log level""" self.logger.setLevel(value) - def debug(self,*args): + def debug(self, *args): """Call logging.debug for multiple args. Use (lazy) debugf and level=num to filter on LOG_LEVEL_DEBUG. """ - self.collect(self.logger.debug,*args) + self.collect(self.logger.debug, *args) - def debug20(self,*args): + def debug20(self, *args): """Call logging.debug for multiple args. Use level=num to filter on LOG_LEVEL_DEBUG (NYI). """ if level <= LOG_LEVEL_DEBUG: if self.logger.getEffectiveLevel() < 20: - self.collect(self.logger.debug,*args) + self.collect(self.logger.debug, *args) - def info(self,*args): + def info(self, *args): """Call logging.info for multiple args""" - self.collect(self.logger.info,*args) + self.collect(self.logger.info, *args) - def warning(self,*args): + def warning(self, *args): """Call logging.warning for multiple args""" - self.collect(self.logger.warning,*args) + self.collect(self.logger.warning, *args) # self.logger.warning(self.collect(*args)) - def error(self,*args): + def error(self, *args): """Call logging.error for multiple args""" now = datetime.datetime.utcnow() time_str = now.strftime('%H:%M:%S UTC %Y%m%d') - l = [time_str]+list(args) - self.collect(self.logger.error,*l) + l = [time_str] + list(args) + self.collect(self.logger.error, *l) - def infof(self,*args): + def infof(self, *args): """Call logging.info for multiple args lazily""" # only evaluate function when logging if self.logger.getEffectiveLevel() < 30: - self.collectf(self.logger.debug,*args) + self.collectf(self.logger.debug, *args) - def debugf(self,level=0,*args): + def debugf(self, level=0, *args): """Call logging.debug for multiple args lazily and handle LOG_LEVEL_DEBUG correctly @@ -95,38 +96,38 @@ LOG_LEVEL_DEBUG (NYI). # only evaluate function when logging if level <= LOG_LEVEL_DEBUG: if self.logger.getEffectiveLevel() < 20: - self.collectf(self.logger.debug,*args) + self.collectf(self.logger.debug, *args) - def sql(self, sqlcommand, fun = None): + def sql(self, sqlcommand, fun=None): """Log SQL command, optionally invoking a timed fun""" if LOG_SQL: caller = stack()[1][3] - if caller in ['fetchone','fetch1','fetchall']: + if caller in ['fetchone', 'fetch1', 'fetchall']: caller = stack()[2][3] - self.info(caller,sqlcommand) + self.info(caller, sqlcommand) if fun: result = fun(sqlcommand) if LOG_SQL: self.info(result) return result - def collect(self,fun,*args): + def collect(self, fun, *args): """Collect arguments and use fun to output""" - out = "."+stack()[2][3] + out = "." + stack()[2][3] for a in args: - if len(out)>1: + if len(out) > 1: out += ": " if isinstance(a, str): out = out + a else: - out = out + pf(a,width=160) + out = out + pf(a, width=160) fun(out) - def collectf(self,fun,*args): + def collectf(self, fun, *args): """Collect arguments and use fun to output one by one""" - out = "."+stack()[2][3] + out = "." + stack()[2][3] for a in args: - if len(out)>1: + if len(out) > 1: out += ": " if isfunction(a): out += a() @@ -134,12 +135,14 @@ LOG_LEVEL_DEBUG (NYI). if isinstance(a, str): out = out + a else: - out = out + pf(a,width=160) + out = out + pf(a, width=160) fun(out) # Get the module logger. You can override log levels at the # module level -def getLogger(name, level = None): + + +def getLogger(name, level=None): gnlogger = GNLogger(name) logger = gnlogger.logger @@ -148,5 +151,6 @@ def getLogger(name, level = None): else: logger.setLevel(LOG_LEVEL) - logger.info("Log level of "+name+" set to "+logging.getLevelName(logger.getEffectiveLevel())) + logger.info("Log level of " + name + " set to " + \ + logging.getLevelName(logger.getEffectiveLevel())) return gnlogger diff --git a/wqflask/utility/pillow_utils.py b/wqflask/utility/pillow_utils.py new file mode 100644 index 00000000..5713e155 --- /dev/null +++ b/wqflask/utility/pillow_utils.py @@ -0,0 +1,31 @@ +from PIL import Image, ImageColor, ImageDraw, ImageFont + +from utility.tools import TEMPDIR + +import utility.logger +logger = utility.logger.getLogger(__name__) + +BLACK = ImageColor.getrgb("black") +WHITE = ImageColor.getrgb("white") + +# def draw_rotated_text(canvas: Image, text: str, font: ImageFont, xy: tuple, fill: ImageColor=BLACK, angle: int=-90): + + +def draw_rotated_text(canvas, text, font, xy, fill=BLACK, angle=-90): + # type: (Image, str, ImageFont, tuple, ImageColor, int) + """Utility function draw rotated text""" + tmp_img = Image.new("RGBA", font.getsize(text), color=(0, 0, 0, 0)) + draw_text = ImageDraw.Draw(tmp_img) + draw_text.text(text=text, xy=(0, 0), font=font, fill=fill) + tmp_img2 = tmp_img.rotate(angle, expand=1) + tmp_img2.save("/{0}/{1}.png".format(TEMPDIR, text), format="png") + canvas.paste(im=tmp_img2, box=tuple([int(i) for i in xy])) + +# def draw_open_polygon(canvas: Image, xy: tuple, fill: ImageColor=WHITE, outline: ImageColor=BLACK): + + +def draw_open_polygon(canvas, xy, fill=None, outline=BLACK, width=0): + # type: (Image, tuple, ImageColor, ImageColor) + draw_ctx = ImageDraw.Draw(canvas) + draw_ctx.polygon(xy, fill=fill) + draw_ctx.line(xy, fill=outline, width=width) diff --git a/wqflask/utility/redis_tools.py b/wqflask/utility/redis_tools.py index ca42f7b7..c2a3b057 100644 --- a/wqflask/utility/redis_tools.py +++ b/wqflask/utility/redis_tools.py @@ -1,17 +1,23 @@ -from __future__ import print_function, division, absolute_import - +import uuid import simplejson as json +import datetime -import redis # used for collections -Redis = redis.StrictRedis() - -import logging - -from flask import (render_template, flash) +import redis # used for collections +from deprecated import deprecated +from utility.hmac import hmac_creation from utility.logger import getLogger logger = getLogger(__name__) + +def get_redis_conn(): + Redis = redis.StrictRedis(port=6379) + return Redis + + +Redis = get_redis_conn() + + def is_redis_available(): try: Redis.ping() @@ -19,8 +25,16 @@ def is_redis_available(): return False return True + +def load_json_from_redis(item_list, column_value): + if type(column_value) == str: + column_value = str.encode(column_value) + return json.loads(item_list[column_value]) + + def get_user_id(column_name, column_value): user_list = Redis.hgetall("users") + key_list = [] for key in user_list: user_ob = json.loads(user_list[key]) if column_name in user_ob and user_ob[column_name] == column_value: @@ -28,6 +42,7 @@ def get_user_id(column_name, column_value): return None + def get_user_by_unique_column(column_name, column_value): item_details = None @@ -38,16 +53,18 @@ def get_user_by_unique_column(column_name, column_value): if column_name in user_ob and user_ob[column_name] == column_value: item_details = user_ob else: - item_details = json.loads(user_list[column_value]) + item_details = load_json_from_redis(user_list, column_value) return item_details + def set_user_attribute(user_id, column_name, column_value): user_info = json.loads(Redis.hget("users", user_id)) user_info[column_name] = column_value Redis.hset("users", user_id, json.dumps(user_info)) + def get_user_collections(user_id): collections = None collections = Redis.hget("collections", user_id) @@ -57,45 +74,64 @@ def get_user_collections(user_id): else: return [] + def save_user(user, user_id): Redis.hset("users", user_id, json.dumps(user)) + def save_collections(user_id, collections_ob): Redis.hset("collections", user_id, collections_ob) + def save_verification_code(user_email, code): Redis.hset("verification_codes", code, user_email) + def check_verification_code(code): email_address = None user_details = None email_address = Redis.hget("verification_codes", code) - return email_address if email_address: - user_details = get_user_by_unique_column('email_address', email_address) - return user_details + user_details = get_user_by_unique_column( + 'email_address', email_address) + if user_details: + return user_details + else: + return None else: return None - flash("Invalid code: Password reset code does not exist or might have expired!", "error") + def get_user_groups(user_id): - #ZS: Get the groups where a user is an admin or a member and return lists corresponding to those two sets of groups - admin_group_ids = [] #ZS: Group IDs where user is an admin - user_group_ids = [] #ZS: Group IDs where user is a regular user + # Get the groups where a user is an admin or a member and + # return lists corresponding to those two sets of groups + admin_group_ids = [] # Group IDs where user is an admin + user_group_ids = [] # Group IDs where user is a regular user groups_list = Redis.hgetall("groups") - for key in groups_list: - group_ob = json.loads(groups_list[key]) - group_admins = set(group_ob['admins']) - group_users = set(group_ob['users']) - if user_id in group_admins: - admin_group_ids.append(group_ob['id']) - elif user_id in group_users: - user_group_ids.append(group_ob['id']) - else: + for group_id, group_details in groups_list.items(): + try: + _details = json.loads(group_details) + group_admins = set([this_admin if this_admin else None for this_admin in _details['admins']]) + group_members = set([this_member if this_member else None for this_member in _details['members']]) + if user_id in group_admins: + admin_group_ids.append(group_id) + elif user_id in group_members: + user_group_ids.append(group_id) + else: + continue + except: continue - return admin_group_ids, user_group_ids + admin_groups = [] + user_groups = [] + for the_id in admin_group_ids: + admin_groups.append(get_group_info(the_id)) + for the_id in user_group_ids: + user_groups.append(get_group_info(the_id)) + + return admin_groups, user_groups + def get_group_info(group_id): group_json = Redis.hget("groups", group_id) @@ -105,23 +141,26 @@ def get_group_info(group_id): return group_info -def create_group(admin_member_ids, user_member_ids = [], group_name = ""): + +def create_group(admin_user_ids, member_user_ids=[], + group_name="Default Group Name"): group_id = str(uuid.uuid4()) new_group = { - "id" : group_id, - "admins": admin_member_ids, - "users" : user_member_ids, - "name" : group_name, + "id": group_id, + "admins": admin_user_ids, + "members": member_user_ids, + "name": group_name, "created_timestamp": datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p'), "changed_timestamp": datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p') } - Redis.hset("groups", group_id, new_group) + Redis.hset("groups", group_id, json.dumps(new_group)) return new_group + def delete_group(user_id, group_id): - #ZS: If user is an admin of a group, remove it from the groups hash + # ZS: If user is an admin of a group, remove it from the groups hash group_info = get_group_info(group_id) if user_id in group_info["admins"]: Redis.hdel("groups", group_id) @@ -129,13 +168,19 @@ def delete_group(user_id, group_id): else: None -def add_users_to_group(user_id, group_id, user_emails = [], admins = False): #ZS "admins" is just to indicate whether the users should be added to the groups admins or regular users set + +# ZS "admins" is just to indicate whether the users should be added to +# the groups admins or regular users set +def add_users_to_group(user_id, group_id, user_emails=[], admins=False): group_info = get_group_info(group_id) - if user_id in group_info["admins"]: #ZS: Just to make sure that the user is an admin for the group, even though they shouldn't be able to reach this point unless they are + # ZS: Just to make sure that the user is an admin for the group, + # even though they shouldn't be able to reach this point unless + # they are + if user_id in group_info["admins"]: if admins: group_users = set(group_info["admins"]) else: - group_users = set(group_info["users"]) + group_users = set(group_info["members"]) for email in user_emails: user_id = get_user_id("email_address", email) @@ -144,27 +189,98 @@ def add_users_to_group(user_id, group_id, user_emails = [], admins = False): #ZS if admins: group_info["admins"] = list(group_users) else: - group_info["users"] = list(group_users) + group_info["members"] = list(group_users) - group_info["changed_timestamp"] = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p') + group_info["changed_timestamp"] = datetime.datetime.utcnow().strftime( + '%b %d %Y %I:%M%p') Redis.hset("groups", group_id, json.dumps(group_info)) return group_info else: return None -def remove_users_from_group(user_id, users_to_remove_ids, group_id, user_type = "users"): #ZS: User type is because I assume admins can remove other admins + +# ZS: User type is because I assume admins can remove other admins +def remove_users_from_group(user_id, + users_to_remove_ids, + group_id, + user_type="members"): group_info = get_group_info(group_id) + if user_id in group_info["admins"]: + users_to_remove_set = set(users_to_remove_ids) + # ZS: Make sure an admin can't remove themselves from a group, + # since I imagine we don't want groups to be able to become + # admin-less + if user_type == "admins" and user_id in users_to_remove_set: + users_to_remove_set.remove(user_id) group_users = set(group_info[user_type]) - group_users -= set(users_to_remove_ids) + group_users -= users_to_remove_set group_info[user_type] = list(group_users) - group_info["changed_timestamp"] = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p') + group_info["changed_timestamp"] = datetime.datetime.utcnow().strftime( + '%b %d %Y %I:%M%p') Redis.hset("groups", group_id, json.dumps(group_info)) + def change_group_name(user_id, group_id, new_name): group_info = get_group_info(group_id) if user_id in group_info["admins"]: group_info["name"] = new_name + Redis.hset("groups", group_id, json.dumps(group_info)) return group_info else: - return None
\ No newline at end of file + return None + + +def get_resources(): + resource_list = Redis.hgetall("resources") + return resource_list + + +def get_resource_id(dataset, trait_id=None): + resource_id = False + if dataset.type == "Publish": + if trait_id: + resource_id = hmac_creation("{}:{}:{}".format( + 'dataset-publish', dataset.id, trait_id)) + elif dataset.type == "ProbeSet": + resource_id = hmac_creation( + "{}:{}".format('dataset-probeset', dataset.id)) + elif dataset.type == "Geno": + resource_id = hmac_creation( + "{}:{}".format('dataset-geno', dataset.id)) + + return resource_id + + +@deprecated +def get_resource_info(resource_id): + resource_info = Redis.hget("resources", resource_id) + if resource_info: + return json.loads(resource_info) + else: + return None + + +def add_resource(resource_info, update=True): + if 'trait' in resource_info['data']: + resource_id = hmac_creation('{}:{}:{}'.format( + str(resource_info['type']), str( + resource_info['data']['dataset']), + str(resource_info['data']['trait']))) + else: + resource_id = hmac_creation('{}:{}'.format( + str(resource_info['type']), str(resource_info['data']['dataset']))) + + if update or not Redis.hexists("resources", resource_id): + Redis.hset("resources", resource_id, json.dumps(resource_info)) + + return resource_info + + +def add_access_mask(resource_id, group_id, access_mask): + the_resource = get_resource_info(resource_id) + the_resource['group_masks'][group_id] = access_mask + + Redis.hset("resources", resource_id, json.dumps(the_resource)) + + return the_resource diff --git a/wqflask/utility/startup_config.py b/wqflask/utility/startup_config.py index 817284dd..778fb64d 100644 --- a/wqflask/utility/startup_config.py +++ b/wqflask/utility/startup_config.py @@ -1,39 +1,44 @@ from wqflask import app -from utility.tools import WEBSERVER_MODE, show_settings, get_setting_int, get_setting, get_setting_bool -import utility.logger -logger = utility.logger.getLogger(__name__ ) +from utility.tools import WEBSERVER_MODE +from utility.tools import show_settings +from utility.tools import get_setting_int +from utility.tools import get_setting +from utility.tools import get_setting_bool -BLUE = '\033[94m' + +BLUE = '\033[94m' GREEN = '\033[92m' -BOLD = '\033[1m' -ENDC = '\033[0m' +BOLD = '\033[1m' +ENDC = '\033[0m' + def app_config(): app.config['SESSION_TYPE'] = 'filesystem' if not app.config.get('SECRET_KEY'): import os app.config['SECRET_KEY'] = str(os.urandom(24)) - mode = WEBSERVER_MODE - if mode == "DEV" or mode == "DEBUG": + if mode in ["DEV", "DEBUG"]: app.config['TEMPLATES_AUTO_RELOAD'] = True - # if mode == "DEBUG": - # app.config['EXPLAIN_TEMPLATE_LOADING'] = True <--- use overriding app param instead + if mode == "DEBUG": + from flask_debugtoolbar import DebugToolbarExtension + app.debug = True + toolbar = DebugToolbarExtension(app) + print("==========================================") + show_settings() port = get_setting_int("SERVER_PORT") if get_setting_bool("USE_GN_SERVER"): - print("GN2 API server URL is ["+BLUE+get_setting("GN_SERVER_URL")+ENDC+"]") + print(f"GN2 API server URL is [{BLUE}GN_SERVER_URL{ENDC}]") import requests page = requests.get(get_setting("GN_SERVER_URL")) if page.status_code != 200: raise Exception("API server not found!") - - # import utility.elasticsearch_tools as es - # es.test_elasticsearch_connection() - - print("GN2 is running. Visit %s[http://localhost:%s/%s](%s)" % (BLUE,str(port),ENDC,get_setting("WEBSERVER_URL"))) + print(f"GN2 is running. Visit {BLUE}" + f"[http://localhost:{str(port)}/{ENDC}]" + f"({get_setting('WEBSERVER_URL')})") diff --git a/wqflask/utility/svg.py b/wqflask/utility/svg.py index db13b9d1..eddb97da 100644 --- a/wqflask/utility/svg.py +++ b/wqflask/utility/svg.py @@ -25,54 +25,56 @@ # Last updated by GeneNetwork Core Team 2010/10/20 #!/usr/bin/env python -##Copyright (c) 2002, Fedor Baart & Hans de Wit (Stichting Farmaceutische Kengetallen) -##All rights reserved. +# Copyright (c) 2002, Fedor Baart & Hans de Wit (Stichting Farmaceutische Kengetallen) +# All rights reserved. ## -##Redistribution and use in source and binary forms, with or without modification, -##are permitted provided that the following conditions are met: +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: ## -##Redistributions of source code must retain the above copyright notice, this -##list of conditions and the following disclaimer. +# Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. ## -##Redistributions in binary form must reproduce the above copyright notice, -##this list of conditions and the following disclaimer in the documentation and/or -##other materials provided with the distribution. +# Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. ## -##Neither the name of the Stichting Farmaceutische Kengetallen nor the names of -##its contributors may be used to endorse or promote products derived from this -##software without specific prior written permission. +# Neither the name of the Stichting Farmaceutische Kengetallen nor the names of +# its contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. ## -##THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -##AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -##IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -##DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -##FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -##DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -##SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -##CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -##OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -##OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Thanks to Gerald Rosennfellner for his help and useful comments. -##Thanks to Gerald Rosennfellner for his help and useful comments. - -__doc__="""Use SVGdraw to generate your SVGdrawings. +import sys +import exceptions +__doc__ = """Use SVGdraw to generate your SVGdrawings. SVGdraw uses an object model drawing and a method toXML to create SVG graphics by using easy to use classes and methods usualy you start by creating a drawing eg d=drawing() - #then you create a SVG root element + # then you create a SVG root element s=svg() - #then you add some elements eg a circle and add it to the svg root element + # then you add some elements eg a circle and add it to the svg root element c=circle() - #you can supply attributes by using named arguments. + # you can supply attributes by using named arguments. c=circle(fill='red',stroke='blue') - #or by updating the attributes attribute: + # or by updating the attributes attribute: c.attributes['stroke-width']=1 s.addElement(c) - #then you add the svg root element to the drawing + # then you add the svg root element to the drawing d.setSVG(s) - #and finaly you xmlify the drawing + # and finaly you xmlify the drawing d.toXml() @@ -82,7 +84,7 @@ This module was created using the SVG specification of www.w3c.org and the O'Reilly (www.oreilly.com) python books as information sources. A svg viewer is available from www.adobe.com""" -__version__="1.0" +__version__ = "1.0" # there are two possibilities to generate svg: # via a dom implementation and directly using <element>text</element> strings @@ -93,33 +95,34 @@ __version__="1.0" # Note that PyXML is required for using the dom implementation. # It is also possible to use the standard minidom. But I didn't try that one. # Anyway the text based approach is about 60 times faster than using the full dom implementation. -use_dom_implementation=0 +use_dom_implementation = 0 -import exceptions -if use_dom_implementation<>0: +if use_dom_implementation != 0: try: from xml.dom import implementation from xml.dom.ext import PrettyPrint except: - raise exceptions.ImportError, "PyXML is required for using the dom implementation" -#The implementation is used for the creating the XML document. -#The prettyprint module is used for converting the xml document object to a xml file + raise exceptions.ImportError( + "PyXML is required for using the dom implementation") +# The implementation is used for the creating the XML document. +# The prettyprint module is used for converting the xml document object to a xml file + +assert sys.version_info[0] >= 2 +if sys.version_info[1] < 2: + True = 1 + False = 0 + file = open + +sys.setrecursionlimit = 50 +# The recursion limit is set conservative so mistakes like s=svg() s.addElement(s) +# won't eat up too much processor time. + +# the following code is pasted form xml.sax.saxutils +# it makes it possible to run the code without the xml sax package installed +# To make it possible to have <rubbish> in your text elements, it is necessary to escape the texts + -import sys -assert sys.version_info[0]>=2 -if sys.version_info[1]<2: - True=1 - False=0 - file=open - -sys.setrecursionlimit=50 -#The recursion limit is set conservative so mistakes like s=svg() s.addElement(s) -#won't eat up too much processor time. - -#the following code is pasted form xml.sax.saxutils -#it makes it possible to run the code without the xml sax package installed -#To make it possible to have <rubbish> in your text elements, it is necessary to escape the texts def _escape(data, entities={}): """Escape &, <, and > in a string of data. @@ -127,13 +130,14 @@ def _escape(data, entities={}): the optional entities parameter. The keys and values must all be strings; each key will be replaced with its corresponding value. """ - #data = data.replace("&", "&") + # data = data.replace("&", "&") data = data.replace("<", "<") data = data.replace(">", ">") - for chars, entity in entities.items(): + for chars, entity in list(entities.items()): data = data.replace(chars, entity) return data + def _quoteattr(data, entities={}): """Escape and quote an attribute value. @@ -156,96 +160,125 @@ def _quoteattr(data, entities={}): return data - def _xypointlist(a): """formats a list of xy pairs""" - s='' - for e in a: #this could be done more elegant - s+=str(e)[1:-1] +' ' + s = '' + for e in a: # this could be done more elegant + s += str(e)[1:-1] + ' ' return s + def _viewboxlist(a): """formats a tuple""" - s='' + s = '' for e in a: - s+=str(e)+' ' + s += str(e) + ' ' return s + def _pointlist(a): """formats a list of numbers""" return str(a)[1:-1] + class pathdata: """class used to create a pathdata object which can be used for a path. although most methods are pretty straightforward it might be useful to look at the SVG specification.""" - #I didn't test the methods below. - def __init__(self,x=None,y=None): - self.path=[] + # I didn't test the methods below. + + def __init__(self, x=None, y=None): + self.path = [] if x is not None and y is not None: - self.path.append('M '+str(x)+' '+str(y)) + self.path.append('M ' + str(x) + ' ' + str(y)) + def closepath(self): """ends the path""" self.path.append('z') - def move(self,x,y): + + def move(self, x, y): """move to absolute""" - self.path.append('M '+str(x)+' '+str(y)) - def relmove(self,x,y): + self.path.append('M ' + str(x) + ' ' + str(y)) + + def relmove(self, x, y): """move to relative""" - self.path.append('m '+str(x)+' '+str(y)) - def line(self,x,y): + self.path.append('m ' + str(x) + ' ' + str(y)) + + def line(self, x, y): """line to absolute""" - self.path.append('L '+str(x)+' '+str(y)) - def relline(self,x,y): + self.path.append('L ' + str(x) + ' ' + str(y)) + + def relline(self, x, y): """line to relative""" - self.path.append('l '+str(x)+' '+str(y)) - def hline(self,x): + self.path.append('l ' + str(x) + ' ' + str(y)) + + def hline(self, x): """horizontal line to absolute""" - self.path.append('H'+str(x)) - def relhline(self,x): + self.path.append('H' + str(x)) + + def relhline(self, x): """horizontal line to relative""" - self.path.append('h'+str(x)) - def vline(self,y): + self.path.append('h' + str(x)) + + def vline(self, y): """verical line to absolute""" - self.path.append('V'+str(y)) - def relvline(self,y): + self.path.append('V' + str(y)) + + def relvline(self, y): """vertical line to relative""" - self.path.append('v'+str(y)) - def bezier(self,x1,y1,x2,y2,x,y): + self.path.append('v' + str(y)) + + def bezier(self, x1, y1, x2, y2, x, y): """bezier with xy1 and xy2 to xy absolut""" - self.path.append('C'+str(x1)+','+str(y1)+' '+str(x2)+','+str(y2)+' '+str(x)+','+str(y)) - def relbezier(self,x1,y1,x2,y2,x,y): + self.path.append('C' + str(x1) + ',' + str(y1) + ' ' + str(x2) + + ',' + str(y2) + ' ' + str(x) + ',' + str(y)) + + def relbezier(self, x1, y1, x2, y2, x, y): """bezier with xy1 and xy2 to xy relative""" - self.path.append('c'+str(x1)+','+str(y1)+' '+str(x2)+','+str(y2)+' '+str(x)+','+str(y)) - def smbezier(self,x2,y2,x,y): + self.path.append('c' + str(x1) + ',' + str(y1) + ' ' + str(x2) + + ',' + str(y2) + ' ' + str(x) + ',' + str(y)) + + def smbezier(self, x2, y2, x, y): """smooth bezier with xy2 to xy absolut""" - self.path.append('S'+str(x2)+','+str(y2)+' '+str(x)+','+str(y)) - def relsmbezier(self,x2,y2,x,y): + self.path.append('S' + str(x2) + ',' + str(y2) + \ + ' ' + str(x) + ',' + str(y)) + + def relsmbezier(self, x2, y2, x, y): """smooth bezier with xy2 to xy relative""" - self.path.append('s'+str(x2)+','+str(y2)+' '+str(x)+','+str(y)) - def qbezier(self,x1,y1,x,y): + self.path.append('s' + str(x2) + ',' + str(y2) + \ + ' ' + str(x) + ',' + str(y)) + + def qbezier(self, x1, y1, x, y): """quadratic bezier with xy1 to xy absolut""" - self.path.append('Q'+str(x1)+','+str(y1)+' '+str(x)+','+str(y)) - def relqbezier(self,x1,y1,x,y): + self.path.append('Q' + str(x1) + ',' + str(y1) + \ + ' ' + str(x) + ',' + str(y)) + + def relqbezier(self, x1, y1, x, y): """quadratic bezier with xy1 to xy relative""" - self.path.append('q'+str(x1)+','+str(y1)+' '+str(x)+','+str(y)) - def smqbezier(self,x,y): + self.path.append('q' + str(x1) + ',' + str(y1) + \ + ' ' + str(x) + ',' + str(y)) + + def smqbezier(self, x, y): """smooth quadratic bezier to xy absolut""" - self.path.append('T'+str(x)+','+str(y)) - def relsmqbezier(self,x,y): + self.path.append('T' + str(x) + ',' + str(y)) + + def relsmqbezier(self, x, y): """smooth quadratic bezier to xy relative""" - self.path.append('t'+str(x)+','+str(y)) - def ellarc(self,rx,ry,xrot,laf,sf,x,y): + self.path.append('t' + str(x) + ',' + str(y)) + + def ellarc(self, rx, ry, xrot, laf, sf, x, y): """elliptival arc with rx and ry rotating with xrot using large-arc-flag and sweep-flag to xy absolut""" - self.path.append('A'+str(rx)+','+str(ry)+' '+str(xrot)+' '+str(laf)+' '+str(sf)+' '+str(x)+' '+str(y)) - def relellarc(self,rx,ry,xrot,laf,sf,x,y): + self.path.append('A' + str(rx) + ',' + str(ry) + ' ' + str(xrot) + + ' ' + str(laf) + ' ' + str(sf) + ' ' + str(x) + ' ' + str(y)) + + def relellarc(self, rx, ry, xrot, laf, sf, x, y): """elliptival arc with rx and ry rotating with xrot using large-arc-flag and sweep-flag to xy relative""" - self.path.append('a'+str(rx)+','+str(ry)+' '+str(xrot)+' '+str(laf)+' '+str(sf)+' '+str(x)+' '+str(y)) + self.path.append('a' + str(rx) + ',' + str(ry) + ' ' + str(xrot) + + ' ' + str(laf) + ' ' + str(sf) + ' ' + str(x) + ' ' + str(y)) + def __repr__(self): return ' '.join(self.path) - - class SVGelement: """SVGelement(type,attributes,elements,text,namespace,**args) Creates a arbitrary svg element and is intended to be subclassed not used on its own. @@ -256,62 +289,67 @@ class SVGelement: namespace. Note the elements==None, if elements = None:self.elements=[] construction. This is done because if you default to elements=[] every object has a reference to the same empty list.""" - def __init__(self,type='',attributes=None,elements=None,text='',namespace='',cdata=None, **args): - self.type=type - if attributes==None: - self.attributes={} + + def __init__(self, type='', attributes=None, elements=None, text='', namespace='', cdata=None, **args): + self.type = type + if attributes == None: + self.attributes = {} else: - self.attributes=attributes - if elements==None: - self.elements=[] + self.attributes = attributes + if elements == None: + self.elements = [] else: - self.elements=elements - self.text=text - self.namespace=namespace - self.cdata=cdata - for arg in args.keys(): + self.elements = elements + self.text = text + self.namespace = namespace + self.cdata = cdata + for arg in list(args.keys()): arg2 = arg.replace("__", ":") arg2 = arg2.replace("_", "-") - self.attributes[arg2]=args[arg] - def addElement(self,SVGelement): + self.attributes[arg2] = args[arg] + + def addElement(self, SVGelement): """adds an element to a SVGelement SVGelement.addElement(SVGelement) """ self.elements.append(SVGelement) - def toXml(self,level,f): - f.write('\t'*level) - f.write('<'+self.type) - for attkey in self.attributes.keys(): - f.write(' '+_escape(str(attkey))+'='+_quoteattr(str(self.attributes[attkey]))) + def toXml(self, level, f): + f.write('\t' * level) + f.write('<' + self.type) + for attkey in list(self.attributes.keys()): + f.write(' ' + _escape(str(attkey)) + '=' + + _quoteattr(str(self.attributes[attkey]))) if self.namespace: - f.write(' xmlns="'+ _escape(str(self.namespace))+'" xmlns:xlink="http://www.w3.org/1999/xlink"') + f.write(' xmlns="' + _escape(str(self.namespace)) + + '" xmlns:xlink="http://www.w3.org/1999/xlink"') if self.elements or self.text or self.cdata: f.write('>') if self.elements: f.write('\n') for element in self.elements: - element.toXml(level+1,f) + element.toXml(level + 1, f) if self.cdata: - f.write('\n'+'\t'*(level+1)+'<![CDATA[') + f.write('\n' + '\t' * (level + 1) + '<![CDATA[') for line in self.cdata.splitlines(): - f.write('\n'+'\t'*(level+2)+line) - f.write('\n'+'\t'*(level+1)+']]>\n') + f.write('\n' + '\t' * (level + 2) + line) + f.write('\n' + '\t' * (level + 1) + ']]>\n') if self.text: - if type(self.text)==type(''): #If the text is only text + if isinstance(self.text, type('')): # If the text is only text f.write(_escape(str(self.text))) - else: #If the text is a spannedtext class + else: # If the text is a spannedtext class f.write(str(self.text)) if self.elements: - f.write('\t'*level+'</'+self.type+'>\n') + f.write('\t' * level + '</' + self.type + '>\n') elif self.text: - f.write('</'+self.type+'>\n') + f.write('</' + self.type + '>\n') elif self.cdata: - f.write('\t'*level+'</'+self.type+'>\n') + f.write('\t' * level + '</' + self.type + '>\n') else: f.write('/>\n') + class tspan(SVGelement): """ts=tspan(text='',**args) @@ -323,19 +361,22 @@ class tspan(SVGelement): st.addtspan(ts) t=text(3,5,st) """ - def __init__(self,text=None,**args): - SVGelement.__init__(self,'tspan',**args) - if self.text<>None: - self.text=text + + def __init__(self, text=None, **args): + SVGelement.__init__(self, 'tspan', **args) + if self.text != None: + self.text = text + def __repr__(self): - s="<tspan" - for key,value in self.attributes.items(): - s+= ' %s="%s"' % (key,value) - s+='>' - s+=self.text - s+='</tspan>' + s = "<tspan" + for key, value in list(self.attributes.items()): + s += ' %s="%s"' % (key, value) + s += '>' + s += self.text + s += '</tspan>' return s + class tref(SVGelement): """tr=tref(link='',**args) @@ -346,16 +387,19 @@ class tref(SVGelement): st.addtref(tr) t=text(3,5,st) """ - def __init__(self,link,**args): - SVGelement.__init__(self,'tref',{'xlink:href':link},**args) + + def __init__(self, link, **args): + SVGelement.__init__(self, 'tref', {'xlink:href': link}, **args) + def __repr__(self): - s="<tref" + s = "<tref" - for key,value in self.attributes.items(): - s+= ' %s="%s"' % (key,value) - s+='/>' + for key, value in list(self.attributes.items()): + s += ' %s="%s"' % (key, value) + s += '/>' return s + class spannedtext: """st=spannedtext(textlist=[]) @@ -374,72 +418,74 @@ class spannedtext: st.addtext('This text is not bold') t=text(3,5,st) """ - def __init__(self,textlist=None): - if textlist==None: - self.textlist=[] + + def __init__(self, textlist=None): + if textlist == None: + self.textlist = [] else: - self.textlist=textlist - def addtext(self,text=''): + self.textlist = textlist + + def addtext(self, text=''): self.textlist.append(text) - def addtspan(self,tspan): + + def addtspan(self, tspan): self.textlist.append(tspan) - def addtref(self,tref): + + def addtref(self, tref): self.textlist.append(tref) + def __repr__(self): - s="" + s = "" for element in self.textlist: - s+=str(element) + s += str(element) return s + class rect(SVGelement): """r=rect(width,height,x,y,fill,stroke,stroke_width,**args) a rectangle is defined by a width and height and a xy pair """ - def __init__(self,x=None,y=None,width=None,height=None,fill=None,stroke=None,stroke_width=None,**args): - if width==None or height==None: - if width<>None: - raise ValueError, 'height is required' - if height<>None: - raise ValueError, 'width is required' - else: - raise ValueError, 'both height and width are required' - SVGelement.__init__(self,'rect',{'width':width,'height':height},**args) - if x<>None: - self.attributes['x']=x - if y<>None: - self.attributes['y']=y - if fill<>None: - self.attributes['fill']=fill - if stroke<>None: - self.attributes['stroke']=stroke - if stroke_width<>None: - self.attributes['stroke-width']=stroke_width + + def __init__(self, x=None, y=None, width=None, height=None, fill=None, stroke=None, stroke_width=None, **args): + if width == None or height == None: + raise ValueError('both height and width are required') + + SVGelement.__init__( + self, 'rect', {'width': width, 'height': height}, **args) + if x != None: + self.attributes['x'] = x + if y != None: + self.attributes['y'] = y + if fill != None: + self.attributes['fill'] = fill + if stroke != None: + self.attributes['stroke'] = stroke + if stroke_width != None: + self.attributes['stroke-width'] = stroke_width + class ellipse(SVGelement): """e=ellipse(rx,ry,x,y,fill,stroke,stroke_width,**args) an ellipse is defined as a center and a x and y radius. """ - def __init__(self,cx=None,cy=None,rx=None,ry=None,fill=None,stroke=None,stroke_width=None,**args): - if rx==None or ry== None: - if rx<>None: - raise ValueError, 'rx is required' - if ry<>None: - raise ValueError, 'ry is required' - else: - raise ValueError, 'both rx and ry are required' - SVGelement.__init__(self,'ellipse',{'rx':rx,'ry':ry},**args) - if cx<>None: - self.attributes['cx']=cx - if cy<>None: - self.attributes['cy']=cy - if fill<>None: - self.attributes['fill']=fill - if stroke<>None: - self.attributes['stroke']=stroke - if stroke_width<>None: - self.attributes['stroke-width']=stroke_width + + def __init__(self, cx=None, cy=None, rx=None, ry=None, fill=None, stroke=None, stroke_width=None, **args): + if rx == None or ry == None: + raise ValueError('both rx and ry are required') + + SVGelement.__init__(self, 'ellipse', {'rx': rx, 'ry': ry}, **args) + if cx != None: + self.attributes['cx'] = cx + if cy != None: + self.attributes['cy'] = cy + if fill != None: + self.attributes['fill'] = fill + if stroke != None: + self.attributes['stroke'] = stroke + if stroke_width != None: + self.attributes['stroke-width'] = stroke_width class circle(SVGelement): @@ -447,20 +493,22 @@ class circle(SVGelement): The circle creates an element using a x, y and radius values eg """ - def __init__(self,cx=None,cy=None,r=None,fill=None,stroke=None,stroke_width=None,**args): - if r==None: - raise ValueError, 'r is required' - SVGelement.__init__(self,'circle',{'r':r},**args) - if cx<>None: - self.attributes['cx']=cx - if cy<>None: - self.attributes['cy']=cy - if fill<>None: - self.attributes['fill']=fill - if stroke<>None: - self.attributes['stroke']=stroke - if stroke_width<>None: - self.attributes['stroke-width']=stroke_width + + def __init__(self, cx=None, cy=None, r=None, fill=None, stroke=None, stroke_width=None, **args): + if r == None: + raise ValueError('r is required') + SVGelement.__init__(self, 'circle', {'r': r}, **args) + if cx != None: + self.attributes['cx'] = cx + if cy != None: + self.attributes['cy'] = cy + if fill != None: + self.attributes['fill'] = fill + if stroke != None: + self.attributes['stroke'] = stroke + if stroke_width != None: + self.attributes['stroke-width'] = stroke_width + class point(circle): """p=point(x,y,color) @@ -468,72 +516,83 @@ class point(circle): A point is defined as a circle with a size 1 radius. It may be more efficient to use a very small rectangle if you use many points because a circle is difficult to render. """ - def __init__(self,x,y,fill='black',**args): - circle.__init__(self,x,y,1,fill,**args) + + def __init__(self, x, y, fill='black', **args): + circle.__init__(self, x, y, 1, fill, **args) + class line(SVGelement): """l=line(x1,y1,x2,y2,stroke,stroke_width,**args) A line is defined by a begin x,y pair and an end x,y pair """ - def __init__(self,x1=None,y1=None,x2=None,y2=None,stroke=None,stroke_width=None,**args): - SVGelement.__init__(self,'line',**args) - if x1<>None: - self.attributes['x1']=x1 - if y1<>None: - self.attributes['y1']=y1 - if x2<>None: - self.attributes['x2']=x2 - if y2<>None: - self.attributes['y2']=y2 - if stroke_width<>None: - self.attributes['stroke-width']=stroke_width - if stroke<>None: - self.attributes['stroke']=stroke + + def __init__(self, x1=None, y1=None, x2=None, y2=None, stroke=None, stroke_width=None, **args): + SVGelement.__init__(self, 'line', **args) + if x1 != None: + self.attributes['x1'] = x1 + if y1 != None: + self.attributes['y1'] = y1 + if x2 != None: + self.attributes['x2'] = x2 + if y2 != None: + self.attributes['y2'] = y2 + if stroke_width != None: + self.attributes['stroke-width'] = stroke_width + if stroke != None: + self.attributes['stroke'] = stroke + class polyline(SVGelement): """pl=polyline([[x1,y1],[x2,y2],...],fill,stroke,stroke_width,**args) a polyline is defined by a list of xy pairs """ - def __init__(self,points,fill=None,stroke=None,stroke_width=None,**args): - SVGelement.__init__(self,'polyline',{'points':_xypointlist(points)},**args) - if fill<>None: - self.attributes['fill']=fill - if stroke_width<>None: - self.attributes['stroke-width']=stroke_width - if stroke<>None: - self.attributes['stroke']=stroke + + def __init__(self, points, fill=None, stroke=None, stroke_width=None, **args): + SVGelement.__init__(self, 'polyline', { + 'points': _xypointlist(points)}, **args) + if fill != None: + self.attributes['fill'] = fill + if stroke_width != None: + self.attributes['stroke-width'] = stroke_width + if stroke != None: + self.attributes['stroke'] = stroke + class polygon(SVGelement): """pl=polyline([[x1,y1],[x2,y2],...],fill,stroke,stroke_width,**args) a polygon is defined by a list of xy pairs """ - def __init__(self,points,fill=None,stroke=None,stroke_width=None,**args): - SVGelement.__init__(self,'polygon',{'points':_xypointlist(points)},**args) - if fill<>None: - self.attributes['fill']=fill - if stroke_width<>None: - self.attributes['stroke-width']=stroke_width - if stroke<>None: - self.attributes['stroke']=stroke + + def __init__(self, points, fill=None, stroke=None, stroke_width=None, **args): + SVGelement.__init__( + self, 'polygon', {'points': _xypointlist(points)}, **args) + if fill != None: + self.attributes['fill'] = fill + if stroke_width != None: + self.attributes['stroke-width'] = stroke_width + if stroke != None: + self.attributes['stroke'] = stroke + class path(SVGelement): """p=path(path,fill,stroke,stroke_width,**args) a path is defined by a path object and optional width, stroke and fillcolor """ - def __init__(self,pathdata,fill=None,stroke=None,stroke_width=None,id=None,**args): - SVGelement.__init__(self,'path',{'d':str(pathdata)},**args) - if stroke<>None: - self.attributes['stroke']=stroke - if fill<>None: - self.attributes['fill']=fill - if stroke_width<>None: - self.attributes['stroke-width']=stroke_width - if id<>None: - self.attributes['id']=id + + def __init__(self, pathdata, fill=None, stroke=None, stroke_width=None, id=None, **args): + SVGelement.__init__(self, 'path', {'d': str(pathdata)}, **args) + if stroke != None: + self.attributes['stroke'] = stroke + if fill != None: + self.attributes['fill'] = fill + if stroke_width != None: + self.attributes['stroke-width'] = stroke_width + if id != None: + self.attributes['id'] = id class text(SVGelement): @@ -541,20 +600,21 @@ class text(SVGelement): a text element can bge used for displaying text on the screen """ - def __init__(self,x=None,y=None,text=None,font_size=None,font_family=None,text_anchor=None,**args): - SVGelement.__init__(self,'text',**args) - if x<>None: - self.attributes['x']=x - if y<>None: - self.attributes['y']=y - if font_size<>None: - self.attributes['font-size']=font_size - if font_family<>None: - self.attributes['font-family']=font_family - if text<>None: - self.text=text - if text_anchor<>None: - self.attributes['text-anchor']=text_anchor + + def __init__(self, x=None, y=None, text=None, font_size=None, font_family=None, text_anchor=None, **args): + SVGelement.__init__(self, 'text', **args) + if x != None: + self.attributes['x'] = x + if y != None: + self.attributes['y'] = y + if font_size != None: + self.attributes['font-size'] = font_size + if font_family != None: + self.attributes['font-family'] = font_family + if text != None: + self.text = text + if text_anchor != None: + self.attributes['text-anchor'] = text_anchor class textpath(SVGelement): @@ -562,10 +622,12 @@ class textpath(SVGelement): a textpath places a text on a path which is referenced by a link. """ - def __init__(self,link,text=None,**args): - SVGelement.__init__(self,'textPath',{'xlink:href':link},**args) - if text<>None: - self.text=text + + def __init__(self, link, text=None, **args): + SVGelement.__init__(self, 'textPath', {'xlink:href': link}, **args) + if text != None: + self.text = text + class pattern(SVGelement): """p=pattern(x,y,width,height,patternUnits,**args) @@ -574,18 +636,20 @@ class pattern(SVGelement): graphic object which can be replicated ("tiled") at fixed intervals in x and y to cover the areas to be painted. """ - def __init__(self,x=None,y=None,width=None,height=None,patternUnits=None,**args): - SVGelement.__init__(self,'pattern',**args) - if x<>None: - self.attributes['x']=x - if y<>None: - self.attributes['y']=y - if width<>None: - self.attributes['width']=width - if height<>None: - self.attributes['height']=height - if patternUnits<>None: - self.attributes['patternUnits']=patternUnits + + def __init__(self, x=None, y=None, width=None, height=None, patternUnits=None, **args): + SVGelement.__init__(self, 'pattern', **args) + if x != None: + self.attributes['x'] = x + if y != None: + self.attributes['y'] = y + if width != None: + self.attributes['width'] = width + if height != None: + self.attributes['height'] = height + if patternUnits != None: + self.attributes['patternUnits'] = patternUnits + class title(SVGelement): """t=title(text,**args) @@ -593,10 +657,12 @@ class title(SVGelement): a title is a text element. The text is displayed in the title bar add at least one to the root svg element """ - def __init__(self,text=None,**args): - SVGelement.__init__(self,'title',**args) - if text<>None: - self.text=text + + def __init__(self, text=None, **args): + SVGelement.__init__(self, 'title', **args) + if text != None: + self.text = text + class description(SVGelement): """d=description(text,**args) @@ -604,10 +670,12 @@ class description(SVGelement): a description can be added to any element and is used for a tooltip Add this element before adding other elements. """ - def __init__(self,text=None,**args): - SVGelement.__init__(self,'desc',**args) - if text<>None: - self.text=text + + def __init__(self, text=None, **args): + SVGelement.__init__(self, 'desc', **args) + if text != None: + self.text = text + class lineargradient(SVGelement): """lg=lineargradient(x1,y1,x2,y2,id,**args) @@ -615,18 +683,20 @@ class lineargradient(SVGelement): defines a lineargradient using two xy pairs. stop elements van be added to define the gradient colors. """ - def __init__(self,x1=None,y1=None,x2=None,y2=None,id=None,**args): - SVGelement.__init__(self,'linearGradient',**args) - if x1<>None: - self.attributes['x1']=x1 - if y1<>None: - self.attributes['y1']=y1 - if x2<>None: - self.attributes['x2']=x2 - if y2<>None: - self.attributes['y2']=y2 - if id<>None: - self.attributes['id']=id + + def __init__(self, x1=None, y1=None, x2=None, y2=None, id=None, **args): + SVGelement.__init__(self, 'linearGradient', **args) + if x1 != None: + self.attributes['x1'] = x1 + if y1 != None: + self.attributes['y1'] = y1 + if x2 != None: + self.attributes['x2'] = x2 + if y2 != None: + self.attributes['y2'] = y2 + if id != None: + self.attributes['id'] = id + class radialgradient(SVGelement): """rg=radialgradient(cx,cy,r,fx,fy,id,**args) @@ -634,38 +704,43 @@ class radialgradient(SVGelement): defines a radial gradient using a outer circle which are defined by a cx,cy and r and by using a focalpoint. stop elements van be added to define the gradient colors. """ - def __init__(self,cx=None,cy=None,r=None,fx=None,fy=None,id=None,**args): - SVGelement.__init__(self,'radialGradient',**args) - if cx<>None: - self.attributes['cx']=cx - if cy<>None: - self.attributes['cy']=cy - if r<>None: - self.attributes['r']=r - if fx<>None: - self.attributes['fx']=fx - if fy<>None: - self.attributes['fy']=fy - if id<>None: - self.attributes['id']=id + + def __init__(self, cx=None, cy=None, r=None, fx=None, fy=None, id=None, **args): + SVGelement.__init__(self, 'radialGradient', **args) + if cx != None: + self.attributes['cx'] = cx + if cy != None: + self.attributes['cy'] = cy + if r != None: + self.attributes['r'] = r + if fx != None: + self.attributes['fx'] = fx + if fy != None: + self.attributes['fy'] = fy + if id != None: + self.attributes['id'] = id + class stop(SVGelement): """st=stop(offset,stop_color,**args) Puts a stop color at the specified radius """ - def __init__(self,offset,stop_color=None,**args): - SVGelement.__init__(self,'stop',{'offset':offset},**args) - if stop_color<>None: - self.attributes['stop-color']=stop_color + + def __init__(self, offset, stop_color=None, **args): + SVGelement.__init__(self, 'stop', {'offset': offset}, **args) + if stop_color != None: + self.attributes['stop-color'] = stop_color + class style(SVGelement): """st=style(type,cdata=None,**args) Add a CDATA element to this element for defing in line stylesheets etc.. """ - def __init__(self,type,cdata=None,**args): - SVGelement.__init__(self,'style',{'type':type},cdata=cdata, **args) + + def __init__(self, type, cdata=None, **args): + SVGelement.__init__(self, 'style', {'type': type}, cdata=cdata, **args) class image(SVGelement): @@ -673,27 +748,26 @@ class image(SVGelement): adds an image to the drawing. Supported formats are .png, .jpg and .svg. """ - def __init__(self,url,x=None,y=None,width=None,height=None,**args): - if width==None or height==None: - if width<>None: - raise ValueError, 'height is required' - if height<>None: - raise ValueError, 'width is required' - else: - raise ValueError, 'both height and width are required' - SVGelement.__init__(self,'image',{'xlink:href':url,'width':width,'height':height},**args) - if x<>None: - self.attributes['x']=x - if y<>None: - self.attributes['y']=y + + def __init__(self, url, x=None, y=None, width=None, height=None, **args): + if width == None or height == None: + raise ValueError('both height and width are required') + SVGelement.__init__( + self, 'image', {'xlink:href': url, 'width': width, 'height': height}, **args) + if x != None: + self.attributes['x'] = x + if y != None: + self.attributes['y'] = y + class cursor(SVGelement): """c=cursor(url,**args) defines a custom cursor for a element or a drawing """ - def __init__(self,url,**args): - SVGelement.__init__(self,'cursor',{'xlink:href':url},**args) + + def __init__(self, url, **args): + SVGelement.__init__(self, 'cursor', {'xlink:href': url}, **args) class marker(SVGelement): @@ -702,20 +776,22 @@ class marker(SVGelement): defines a marker which can be used as an endpoint for a line or other pathtypes add an element to it which should be used as a marker. """ - def __init__(self,id=None,viewBox=None,refx=None,refy=None,markerWidth=None,markerHeight=None,**args): - SVGelement.__init__(self,'marker',**args) - if id<>None: - self.attributes['id']=id - if viewBox<>None: - self.attributes['viewBox']=_viewboxlist(viewBox) - if refx<>None: - self.attributes['refX']=refx - if refy<>None: - self.attributes['refY']=refy - if markerWidth<>None: - self.attributes['markerWidth']=markerWidth - if markerHeight<>None: - self.attributes['markerHeight']=markerHeight + + def __init__(self, id=None, viewBox=None, refx=None, refy=None, markerWidth=None, markerHeight=None, **args): + SVGelement.__init__(self, 'marker', **args) + if id != None: + self.attributes['id'] = id + if viewBox != None: + self.attributes['viewBox'] = _viewboxlist(viewBox) + if refx != None: + self.attributes['refX'] = refx + if refy != None: + self.attributes['refY'] = refy + if markerWidth != None: + self.attributes['markerWidth'] = markerWidth + if markerHeight != None: + self.attributes['markerHeight'] = markerHeight + class group(SVGelement): """g=group(id,**args) @@ -723,10 +799,12 @@ class group(SVGelement): a group is defined by an id and is used to contain elements g.addElement(SVGelement) """ - def __init__(self,id=None,**args): - SVGelement.__init__(self,'g',**args) - if id<>None: - self.attributes['id']=id + + def __init__(self, id=None, **args): + SVGelement.__init__(self, 'g', **args) + if id != None: + self.attributes['id'] = id + class symbol(SVGelement): """sy=symbol(id,viewbox,**args) @@ -737,20 +815,23 @@ class symbol(SVGelement): sy.addElement(SVGelement) """ - def __init__(self,id=None,viewBox=None,**args): - SVGelement.__init__(self,'symbol',**args) - if id<>None: - self.attributes['id']=id - if viewBox<>None: - self.attributes['viewBox']=_viewboxlist(viewBox) + def __init__(self, id=None, viewBox=None, **args): + SVGelement.__init__(self, 'symbol', **args) + if id != None: + self.attributes['id'] = id + if viewBox != None: + self.attributes['viewBox'] = _viewboxlist(viewBox) + class defs(SVGelement): """d=defs(**args) container for defining elements """ - def __init__(self,**args): - SVGelement.__init__(self,'defs',**args) + + def __init__(self, **args): + SVGelement.__init__(self, 'defs', **args) + class switch(SVGelement): """sw=switch(**args) @@ -759,8 +840,9 @@ class switch(SVGelement): requiredFeatures, requiredExtensions and systemLanguage. Refer to the SVG specification for details. """ - def __init__(self,**args): - SVGelement.__init__(self,'switch',**args) + + def __init__(self, **args): + SVGelement.__init__(self, 'switch', **args) class use(SVGelement): @@ -768,17 +850,18 @@ class use(SVGelement): references a symbol by linking to its id and its position, height and width """ - def __init__(self,link,x=None,y=None,width=None,height=None,**args): - SVGelement.__init__(self,'use',{'xlink:href':link},**args) - if x<>None: - self.attributes['x']=x - if y<>None: - self.attributes['y']=y - if width<>None: - self.attributes['width']=width - if height<>None: - self.attributes['height']=height + def __init__(self, link, x=None, y=None, width=None, height=None, **args): + SVGelement.__init__(self, 'use', {'xlink:href': link}, **args) + if x != None: + self.attributes['x'] = x + if y != None: + self.attributes['y'] = y + + if width != None: + self.attributes['width'] = width + if height != None: + self.attributes['height'] = height class link(SVGelement): @@ -787,17 +870,21 @@ class link(SVGelement): a link is defined by a hyperlink. add elements which have to be linked a.addElement(SVGelement) """ - def __init__(self,link='',**args): - SVGelement.__init__(self,'a',{'xlink:href':link},**args) + + def __init__(self, link='', **args): + SVGelement.__init__(self, 'a', {'xlink:href': link}, **args) + class view(SVGelement): """v=view(id,**args) a view can be used to create a view with different attributes""" - def __init__(self,id=None,**args): - SVGelement.__init__(self,'view',**args) - if id<>None: - self.attributes['id']=id + + def __init__(self, id=None, **args): + SVGelement.__init__(self, 'view', **args) + if id != None: + self.attributes['id'] = id + class script(SVGelement): """sc=script(type,type,cdata,**args) @@ -805,78 +892,94 @@ class script(SVGelement): adds a script element which contains CDATA to the SVG drawing """ - def __init__(self,type,cdata=None,**args): - SVGelement.__init__(self,'script',{'type':type},cdata=cdata,**args) + + def __init__(self, type, cdata=None, **args): + SVGelement.__init__( + self, 'script', {'type': type}, cdata=cdata, **args) + class animate(SVGelement): """an=animate(attribute,from,to,during,**args) animates an attribute. """ - def __init__(self,attribute,fr=None,to=None,dur=None,**args): - SVGelement.__init__(self,'animate',{'attributeName':attribute},**args) - if fr<>None: - self.attributes['from']=fr - if to<>None: - self.attributes['to']=to - if dur<>None: - self.attributes['dur']=dur + + def __init__(self, attribute, fr=None, to=None, dur=None, **args): + SVGelement.__init__( + self, 'animate', {'attributeName': attribute}, **args) + if fr != None: + self.attributes['from'] = fr + if to != None: + self.attributes['to'] = to + if dur != None: + self.attributes['dur'] = dur + class animateMotion(SVGelement): """an=animateMotion(pathdata,dur,**args) animates a SVGelement over the given path in dur seconds """ - def __init__(self,pathdata,dur,**args): - SVGelement.__init__(self,'animateMotion',**args) - if pathdata<>None: - self.attributes['path']=str(pathdata) - if dur<>None: - self.attributes['dur']=dur + + def __init__(self, pathdata, dur, **args): + SVGelement.__init__(self, 'animateMotion', **args) + if pathdata != None: + self.attributes['path'] = str(pathdata) + if dur != None: + self.attributes['dur'] = dur + class animateTransform(SVGelement): """antr=animateTransform(type,from,to,dur,**args) transform an element from and to a value. """ - def __init__(self,type=None,fr=None,to=None,dur=None,**args): - SVGelement.__init__(self,'animateTransform',{'attributeName':'transform'},**args) - #As far as I know the attributeName is always transform - if type<>None: - self.attributes['type']=type - if fr<>None: - self.attributes['from']=fr - if to<>None: - self.attributes['to']=to - if dur<>None: - self.attributes['dur']=dur + + def __init__(self, type=None, fr=None, to=None, dur=None, **args): + SVGelement.__init__(self, 'animateTransform', { + 'attributeName': 'transform'}, **args) + # As far as I know the attributeName is always transform + if type != None: + self.attributes['type'] = type + if fr != None: + self.attributes['from'] = fr + if to != None: + self.attributes['to'] = to + if dur != None: + self.attributes['dur'] = dur + + class animateColor(SVGelement): """ac=animateColor(attribute,type,from,to,dur,**args) Animates the color of a element """ - def __init__(self,attribute,type=None,fr=None,to=None,dur=None,**args): - SVGelement.__init__(self,'animateColor',{'attributeName':attribute},**args) - if type<>None: - self.attributes['type']=type - if fr<>None: - self.attributes['from']=fr - if to<>None: - self.attributes['to']=to - if dur<>None: - self.attributes['dur']=dur + + def __init__(self, attribute, type=None, fr=None, to=None, dur=None, **args): + SVGelement.__init__(self, 'animateColor', { + 'attributeName': attribute}, **args) + if type != None: + self.attributes['type'] = type + if fr != None: + self.attributes['from'] = fr + if to != None: + self.attributes['to'] = to + if dur != None: + self.attributes['dur'] = dur + + class set(SVGelement): """st=set(attribute,to,during,**args) sets an attribute to a value for a """ - def __init__(self,attribute,to=None,dur=None,**args): - SVGelement.__init__(self,'set',{'attributeName':attribute},**args) - if to<>None: - self.attributes['to']=to - if dur<>None: - self.attributes['dur']=dur + def __init__(self, attribute, to=None, dur=None, **args): + SVGelement.__init__(self, 'set', {'attributeName': attribute}, **args) + if to != None: + self.attributes['to'] = to + if dur != None: + self.attributes['dur'] = dur class svg(SVGelement): @@ -894,15 +997,17 @@ class svg(SVGelement): d.setSVG(s) d.toXml() """ - def __init__(self,viewBox=None, width=None, height=None,**args): - SVGelement.__init__(self,'svg',**args) - if viewBox<>None: - self.attributes['viewBox']=_viewboxlist(viewBox) - if width<>None: - self.attributes['width']=width - if height<>None: - self.attributes['height']=height - self.namespace="http://www.w3.org/2000/svg" + + def __init__(self, viewBox=None, width=None, height=None, **args): + SVGelement.__init__(self, 'svg', **args) + if viewBox != None: + self.attributes['viewBox'] = _viewboxlist(viewBox) + if width != None: + self.attributes['width'] = width + if height != None: + self.attributes['height'] = height + self.namespace = "http://www.w3.org/2000/svg" + class drawing: """d=drawing() @@ -916,29 +1021,32 @@ class drawing: """ def __init__(self, entity={}): - self.svg=None + self.svg = None self.entity = entity - def setSVG(self,svg): - self.svg=svg - #Voeg een element toe aan de grafiek toe. - if use_dom_implementation==0: - def toXml(self, filename='',compress=False): - import cStringIO - xml=cStringIO.StringIO() + + def setSVG(self, svg): + self.svg = svg + # Voeg een element toe aan de grafiek toe. + if use_dom_implementation == 0: + def toXml(self, filename='', compress=False): + import io + xml = io.StringIO() xml.write("<?xml version='1.0' encoding='UTF-8'?>\n") - xml.write("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\"") + xml.write( + "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\"") if self.entity: xml.write(" [\n") - for item in self.entity.keys(): - xml.write("<!ENTITY %s \"%s\">\n" % (item, self.entity[item])) + for item in list(self.entity.keys()): + xml.write("<!ENTITY %s \"%s\">\n" % + (item, self.entity[item])) xml.write("]") xml.write(">\n") - self.svg.toXml(0,xml) + self.svg.toXml(0, xml) if not filename: if compress: import gzip - f=cStringIO.StringIO() - zf=gzip.GzipFile(fileobj=f,mode='wb') + f = io.StringIO() + zf = gzip.GzipFile(fileobj=f, mode='wb') zf.write(xml.getvalue()) zf.close() f.seek(0) @@ -946,57 +1054,62 @@ class drawing: else: return xml.getvalue() else: - if filename[-4:]=='svgz': + if filename[-4:] == 'svgz': import gzip - f=gzip.GzipFile(filename=filename,mode="wb", compresslevel=9) + f = gzip.GzipFile(filename=filename, + mode="wb", compresslevel=9) f.write(xml.getvalue()) f.close() else: - f=file(filename,'w') + f = file(filename, 'w') f.write(xml.getvalue()) f.close() else: - def toXml(self,filename='',compress=False): + def toXml(self, filename='', compress=False): """drawing.toXml() ---->to the screen drawing.toXml(filename)---->to the file writes a svg drawing to the screen or to a file compresses if filename ends with svgz or if compress is true """ - doctype = implementation.createDocumentType('svg',"-//W3C//DTD SVG 1.0//EN""",'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd ') + doctype = implementation.createDocumentType( + 'svg', "-//W3C//DTD SVG 1.0//EN""", 'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd ') global root - #root is defined global so it can be used by the appender. Its also possible to use it as an arugument but - #that is a bit messy. - root=implementation.createDocument(None,None,doctype) - #Create the xml document. + # root is defined global so it can be used by the appender. Its also possible to use it as an arugument but + # that is a bit messy. + root = implementation.createDocument(None, None, doctype) + # Create the xml document. global appender - def appender(element,elementroot): + + def appender(element, elementroot): """This recursive function appends elements to an element and sets the attributes and type. It stops when alle elements have been appended""" if element.namespace: - e=root.createElementNS(element.namespace,element.type) + e = root.createElementNS(element.namespace, element.type) else: - e=root.createElement(element.type) + e = root.createElement(element.type) if element.text: - textnode=root.createTextNode(element.text) + textnode = root.createTextNode(element.text) e.appendChild(textnode) - for attribute in element.attributes.keys(): #in element.attributes is supported from python 2.2 - e.setAttribute(attribute,str(element.attributes[attribute])) + # in element.attributes is supported from python 2.2 + for attribute in list(element.attributes.keys()): + e.setAttribute(attribute, str( + element.attributes[attribute])) if element.elements: for el in element.elements: - e=appender(el,e) + e = appender(el, e) elementroot.appendChild(e) return elementroot - root=appender(self.svg,root) + root = appender(self.svg, root) if not filename: - import cStringIO - xml=cStringIO.StringIO() - PrettyPrint(root,xml) + import io + xml = io.StringIO() + PrettyPrint(root, xml) if compress: import gzip - f=cStringIO.StringIO() - zf=gzip.GzipFile(fileobj=f,mode='wb') + f = io.StringIO() + zf = gzip.GzipFile(fileobj=f, mode='wb') zf.write(xml.getvalue()) zf.close() f.seek(0) @@ -1005,64 +1118,68 @@ class drawing: return xml.getvalue() else: try: - if filename[-4:]=='svgz': + if filename[-4:] == 'svgz': import gzip - import cStringIO - xml=cStringIO.StringIO() - PrettyPrint(root,xml) - f=gzip.GzipFile(filename=filename,mode='wb',compresslevel=9) + import io + xml = io.StringIO() + PrettyPrint(root, xml) + f = gzip.GzipFile(filename=filename, + mode='wb', compresslevel=9) f.write(xml.getvalue()) f.close() else: - f=open(filename,'w') - PrettyPrint(root,f) + f = open(filename, 'w') + PrettyPrint(root, f) f.close() except: - print "Cannot write SVG file: " + filename + print(("Cannot write SVG file: " + filename)) + def validate(self): try: import xml.parsers.xmlproc.xmlval except: - raise exceptions.ImportError,'PyXml is required for validating SVG' - svg=self.toXml() - xv=xml.parsers.xmlproc.xmlval.XMLValidator() + raise exceptions.ImportError( + 'PyXml is required for validating SVG') + svg = self.toXml() + xv = xml.parsers.xmlproc.xmlval.XMLValidator() try: xv.feed(svg) except: raise Exception("SVG is not well formed, see messages above") else: - print "SVG well formed" -if __name__=='__main__': + print("SVG well formed") - d=drawing() - s=svg((0,0,100,100)) - r=rect(-100,-100,300,300,'cyan') +if __name__ == '__main__': + + d = drawing() + s = svg((0, 0, 100, 100)) + r = rect(-100, -100, 300, 300, 'cyan') s.addElement(r) - t=title('SVGdraw Demo') + t = title('SVGdraw Demo') s.addElement(t) - g=group('animations') - e=ellipse(0,0,5,2) + g = group('animations') + e = ellipse(0, 0, 5, 2) g.addElement(e) - c=circle(0,0,1,'red') + c = circle(0, 0, 1, 'red') g.addElement(c) - pd=pathdata(0,-10) + pd = pathdata(0, -10) for i in range(6): - pd.relsmbezier(10,5,0,10) - pd.relsmbezier(-10,5,0,10) - an=animateMotion(pd,10) - an.attributes['rotate']='auto-reverse' - an.attributes['repeatCount']="indefinite" + pd.relsmbezier(10, 5, 0, 10) + pd.relsmbezier(-10, 5, 0, 10) + an = animateMotion(pd, 10) + an.attributes['rotate'] = 'auto-reverse' + an.attributes['repeatCount'] = "indefinite" g.addElement(an) s.addElement(g) - for i in range(20,120,20): - u=use('#animations',i,0) + for i in range(20, 120, 20): + u = use('#animations', i, 0) s.addElement(u) - for i in range(0,120,20): - for j in range(5,105,10): - c=circle(i,j,1,'red','black',.5) + for i in range(0, 120, 20): + for j in range(5, 105, 10): + c = circle(i, j, 1, 'red', 'black', .5) s.addElement(c) d.setSVG(s) - print d.toXml() + print((d.toXml())) diff --git a/wqflask/utility/temp_data.py b/wqflask/utility/temp_data.py index 5bf700c9..07c5a318 100644 --- a/wqflask/utility/temp_data.py +++ b/wqflask/utility/temp_data.py @@ -1,9 +1,9 @@ -from __future__ import print_function, division, absolute_import from redis import Redis import simplejson as json -class TempData(object): + +class TempData: def __init__(self, temp_uuid): self.temp_uuid = temp_uuid @@ -12,7 +12,7 @@ class TempData(object): def store(self, field, value): self.redis.hset(self.key, field, value) - self.redis.expire(self.key, 60*15) # Expire in 15 minutes + self.redis.expire(self.key, 60 * 15) # Expire in 15 minutes def get_all(self): return self.redis.hgetall(self.key) @@ -20,6 +20,6 @@ class TempData(object): if __name__ == "__main__": redis = Redis() - for key in redis.keys(): + for key in list(redis.keys()): for field in redis.hkeys(key): print("{}.{}={}".format(key, field, redis.hget(key, field))) diff --git a/wqflask/utility/tools.py b/wqflask/utility/tools.py index 89d88516..0efe8ca9 100644 --- a/wqflask/utility/tools.py +++ b/wqflask/utility/tools.py @@ -9,16 +9,18 @@ from wqflask import app # Use the standard logger here to avoid a circular dependency import logging -logger = logging.getLogger(__name__ ) +logger = logging.getLogger(__name__) OVERRIDES = {} + def app_set(command_id, value): """Set application wide value""" - app.config.setdefault(command_id,value) + app.config.setdefault(command_id, value) return value -def get_setting(command_id,guess=None): + +def get_setting(command_id, guess=None): """Resolve a setting from the environment or the global settings in app.config, with valid_path is a function checking whether the path points to an expected directory and returns the full path to @@ -45,7 +47,7 @@ def get_setting(command_id,guess=None): def value(command): if command: # sys.stderr.write("Found "+command+"\n") - app_set(command_id,command) + app_set(command_id, command) return command else: return None @@ -54,7 +56,7 @@ def get_setting(command_id,guess=None): # print("Looking for "+command_id+"\n") command = value(os.environ.get(command_id)) if command is None or command == "": - command = OVERRIDES.get(command_id) # currently not in use + command = OVERRIDES.get(command_id) # currently not in use if command is None: # ---- Check whether setting exists in app command = value(app.config.get(command_id)) @@ -62,16 +64,19 @@ def get_setting(command_id,guess=None): command = value(guess) if command is None or command == "": # print command - raise Exception(command_id+' setting unknown or faulty (update default_settings.py?).') + raise Exception( + command_id + ' setting unknown or faulty (update default_settings.py?).') # print("Set "+command_id+"="+str(command)) return command + def get_setting_bool(id): v = get_setting(id) - if v not in [0,False,'False','FALSE',None]: - return True + if v not in [0, False, 'False', 'FALSE', None]: + return True return False + def get_setting_int(id): v = get_setting(id) if isinstance(v, str): @@ -80,69 +85,83 @@ def get_setting_int(id): return 0 return v + def valid_bin(bin): if os.path.islink(bin) or valid_file(bin): return bin return None + def valid_file(fn): if os.path.isfile(fn): return fn return None + def valid_path(dir): if os.path.isdir(dir): return dir return None + def js_path(module=None): """ Find the JS module in the two paths """ - try_gn = get_setting("JS_GN_PATH")+"/"+module + try_gn = get_setting("JS_GN_PATH") + "/" + module if valid_path(try_gn): return try_gn - try_guix = get_setting("JS_GUIX_PATH")+"/"+module + try_guix = get_setting("JS_GUIX_PATH") + "/" + module if valid_path(try_guix): return try_guix - raise "No JS path found for "+module+" (if not in Guix check JS_GN_PATH)" + raise "No JS path found for " + module + \ + " (if not in Guix check JS_GN_PATH)" + def reaper_command(guess=None): - return get_setting("REAPER_COMMAND",guess) + return get_setting("REAPER_COMMAND", guess) + def gemma_command(guess=None): - return assert_bin(get_setting("GEMMA_COMMAND",guess)) + return assert_bin(get_setting("GEMMA_COMMAND", guess)) + def gemma_wrapper_command(guess=None): - return assert_bin(get_setting("GEMMA_WRAPPER_COMMAND",guess)) + return assert_bin(get_setting("GEMMA_WRAPPER_COMMAND", guess)) + def plink_command(guess=None): - return assert_bin(get_setting("PLINK_COMMAND",guess)) + return assert_bin(get_setting("PLINK_COMMAND", guess)) + def flat_file_exists(subdir): base = get_setting("GENENETWORK_FILES") - return valid_path(base+"/"+subdir) + return valid_path(base + "/" + subdir) + def flat_files(subdir=None): base = get_setting("GENENETWORK_FILES") if subdir: - return assert_dir(base+"/"+subdir) + return assert_dir(base + "/" + subdir) return assert_dir(base) + def assert_bin(fn): if not valid_bin(fn): - raise Exception("ERROR: can not find binary "+fn) + raise Exception("ERROR: can not find binary " + fn) return fn + def assert_dir(dir): if not valid_path(dir): - raise Exception("ERROR: can not find directory "+dir) + raise Exception("ERROR: can not find directory " + dir) return dir + def assert_writable_dir(dir): try: fn = dir + "/test.txt" - fh = open( fn, 'w' ) + fh = open(fn, 'w') fh.write("I am writing this text to the file\n") fh.close() os.remove(fn) @@ -150,16 +169,19 @@ def assert_writable_dir(dir): raise Exception('Unable to write test.txt to directory ' + dir) return dir + def assert_file(fn): if not valid_file(fn): - raise Exception('Unable to find file '+fn) + raise Exception('Unable to find file ' + fn) return fn + def mk_dir(dir): if not valid_path(dir): os.makedirs(dir) return assert_dir(dir) + def locate(name, subdir=None): """ Locate a static flat file in the GENENETWORK_FILES environment. @@ -168,19 +190,22 @@ def locate(name, subdir=None): """ base = get_setting("GENENETWORK_FILES") if subdir: - base = base+"/"+subdir + base = base + "/" + subdir if valid_path(base): lookfor = base + "/" + name if valid_file(lookfor): - logger.info("Found: file "+lookfor+"\n") + logger.info("Found: file " + lookfor + "\n") return lookfor else: - raise Exception("Can not locate "+lookfor) - if subdir: sys.stderr.write(subdir) - raise Exception("Can not locate "+name+" in "+base) + raise Exception("Can not locate " + lookfor) + if subdir: + sys.stderr.write(subdir) + raise Exception("Can not locate " + name + " in " + base) + def locate_phewas(name, subdir=None): - return locate(name,'/phewas/'+subdir) + return locate(name, '/phewas/' + subdir) + def locate_ignore_error(name, subdir=None): """ @@ -191,75 +216,80 @@ def locate_ignore_error(name, subdir=None): """ base = get_setting("GENENETWORK_FILES") if subdir: - base = base+"/"+subdir + base = base + "/" + subdir if valid_path(base): lookfor = base + "/" + name if valid_file(lookfor): - logger.debug("Found: file "+name+"\n") + logger.debug("Found: file " + name + "\n") return lookfor - logger.info("WARNING: file "+name+" not found\n") + logger.info("WARNING: file " + name + " not found\n") return None + def tempdir(): """ Get UNIX TMPDIR by default """ - return valid_path(get_setting("TMPDIR","/tmp")) + return valid_path(get_setting("TMPDIR", "/tmp")) + -BLUE = '\033[94m' +BLUE = '\033[94m' GREEN = '\033[92m' -BOLD = '\033[1m' -ENDC = '\033[0m' +BOLD = '\033[1m' +ENDC = '\033[0m' + def show_settings(): from utility.tools import LOG_LEVEL - print("Set global log level to "+BLUE+LOG_LEVEL+ENDC) + print(("Set global log level to " + BLUE + LOG_LEVEL + ENDC)) log_level = getattr(logging, LOG_LEVEL.upper()) logging.basicConfig(level=log_level) logger.info(OVERRIDES) - logger.info(BLUE+"Mr. Mojo Risin 2"+ENDC) - print "runserver.py: ****** Webserver configuration - k,v pairs from app.config ******" - keylist = app.config.keys() + logger.info(BLUE + "Mr. Mojo Risin 2" + ENDC) + keylist = list(app.config.keys()) + print("runserver.py: ****** Webserver configuration - k,v pairs from app.config ******") keylist.sort() for k in keylist: try: - print("%s: %s%s%s%s" % (k,BLUE,BOLD,get_setting(k),ENDC)) + print(("%s: %s%s%s%s" % (k, BLUE, BOLD, get_setting(k), ENDC))) except: - print("%s: %s%s%s%s" % (k,GREEN,BOLD,app.config[k],ENDC)) + print(("%s: %s%s%s%s" % (k, GREEN, BOLD, app.config[k], ENDC))) # Cached values -GN_VERSION = get_setting('GN_VERSION') -HOME = get_setting('HOME') -SERVER_PORT = get_setting('SERVER_PORT') -WEBSERVER_MODE = get_setting('WEBSERVER_MODE') -GN2_BASE_URL = get_setting('GN2_BASE_URL') -GN2_BRANCH_URL = get_setting('GN2_BRANCH_URL') -GN_SERVER_URL = get_setting('GN_SERVER_URL') -SERVER_PORT = get_setting_int('SERVER_PORT') -SQL_URI = get_setting('SQL_URI') -LOG_LEVEL = get_setting('LOG_LEVEL') -LOG_LEVEL_DEBUG = get_setting_int('LOG_LEVEL_DEBUG') -LOG_SQL = get_setting_bool('LOG_SQL') -LOG_SQL_ALCHEMY = get_setting_bool('LOG_SQL_ALCHEMY') -LOG_BENCH = get_setting_bool('LOG_BENCH') -LOG_FORMAT = "%(message)s" # not yet in use -USE_REDIS = get_setting_bool('USE_REDIS') -USE_GN_SERVER = get_setting_bool('USE_GN_SERVER') - -GENENETWORK_FILES = get_setting('GENENETWORK_FILES') -JS_GUIX_PATH = get_setting('JS_GUIX_PATH') +GN_VERSION = get_setting('GN_VERSION') +HOME = get_setting('HOME') +SERVER_PORT = get_setting('SERVER_PORT') +WEBSERVER_MODE = get_setting('WEBSERVER_MODE') +GN2_BASE_URL = get_setting('GN2_BASE_URL') +GN2_BRANCH_URL = get_setting('GN2_BRANCH_URL') +GN_SERVER_URL = get_setting('GN_SERVER_URL') +GN_PROXY_URL = get_setting('GN_PROXY_URL') +GN3_LOCAL_URL = get_setting('GN3_LOCAL_URL') +SERVER_PORT = get_setting_int('SERVER_PORT') +SQL_URI = get_setting('SQL_URI') +LOG_LEVEL = get_setting('LOG_LEVEL') +LOG_LEVEL_DEBUG = get_setting_int('LOG_LEVEL_DEBUG') +LOG_SQL = get_setting_bool('LOG_SQL') +LOG_SQL_ALCHEMY = get_setting_bool('LOG_SQL_ALCHEMY') +LOG_BENCH = get_setting_bool('LOG_BENCH') +LOG_FORMAT = "%(message)s" # not yet in use +USE_REDIS = get_setting_bool('USE_REDIS') +USE_GN_SERVER = get_setting_bool('USE_GN_SERVER') + +GENENETWORK_FILES = get_setting('GENENETWORK_FILES') +JS_GUIX_PATH = get_setting('JS_GUIX_PATH') assert_dir(JS_GUIX_PATH) -JS_GN_PATH = get_setting('JS_GN_PATH') +JS_GN_PATH = get_setting('JS_GN_PATH') # assert_dir(JS_GN_PATH) GITHUB_CLIENT_ID = get_setting('GITHUB_CLIENT_ID') GITHUB_CLIENT_SECRET = get_setting('GITHUB_CLIENT_SECRET') if GITHUB_CLIENT_ID != 'UNKNOWN' and GITHUB_CLIENT_SECRET: GITHUB_AUTH_URL = "https://github.com/login/oauth/authorize?client_id=" + \ - GITHUB_CLIENT_ID+"&client_secret="+GITHUB_CLIENT_SECRET + GITHUB_CLIENT_ID + "&client_secret=" + GITHUB_CLIENT_SECRET GITHUB_API_URL = get_setting('GITHUB_API_URL') ORCID_CLIENT_ID = get_setting('ORCID_CLIENT_ID') @@ -267,11 +297,12 @@ ORCID_CLIENT_SECRET = get_setting('ORCID_CLIENT_SECRET') ORCID_AUTH_URL = None if ORCID_CLIENT_ID != 'UNKNOWN' and ORCID_CLIENT_SECRET: ORCID_AUTH_URL = "https://orcid.org/oauth/authorize?response_type=code&scope=/authenticate&show_login=true&client_id=" + \ - ORCID_CLIENT_ID+"&client_secret="+ORCID_CLIENT_SECRET + ORCID_CLIENT_ID + "&client_secret=" + ORCID_CLIENT_SECRET + \ + "&redirect_uri=" + GN2_BRANCH_URL + "n/login/orcid_oauth2" ORCID_TOKEN_URL = get_setting('ORCID_TOKEN_URL') -# ELASTICSEARCH_HOST = get_setting('ELASTICSEARCH_HOST') -# ELASTICSEARCH_PORT = get_setting('ELASTICSEARCH_PORT') +ELASTICSEARCH_HOST = get_setting('ELASTICSEARCH_HOST') +ELASTICSEARCH_PORT = get_setting('ELASTICSEARCH_PORT') # import utility.elasticsearch_tools as es # es.test_elasticsearch_connection() @@ -279,28 +310,29 @@ SMTP_CONNECT = get_setting('SMTP_CONNECT') SMTP_USERNAME = get_setting('SMTP_USERNAME') SMTP_PASSWORD = get_setting('SMTP_PASSWORD') -REAPER_COMMAND = app_set("REAPER_COMMAND",reaper_command()) -GEMMA_COMMAND = app_set("GEMMA_COMMAND",gemma_command()) +REAPER_COMMAND = app_set("REAPER_COMMAND", reaper_command()) +GEMMA_COMMAND = app_set("GEMMA_COMMAND", gemma_command()) assert(GEMMA_COMMAND is not None) -PLINK_COMMAND = app_set("PLINK_COMMAND",plink_command()) +PLINK_COMMAND = app_set("PLINK_COMMAND", plink_command()) GEMMA_WRAPPER_COMMAND = gemma_wrapper_command() -TEMPDIR = tempdir() # defaults to UNIX TMPDIR +TEMPDIR = tempdir() # defaults to UNIX TMPDIR assert_dir(TEMPDIR) # ---- Handle specific JS modules JS_GUIX_PATH = get_setting("JS_GUIX_PATH") assert_dir(JS_GUIX_PATH) -assert_dir(JS_GUIX_PATH+'/cytoscape-panzoom') +assert_dir(JS_GUIX_PATH + '/cytoscape-panzoom') CSS_PATH = JS_GUIX_PATH # The CSS is bundled together with the JS # assert_dir(JS_PATH) -JS_TWITTER_POST_FETCHER_PATH = get_setting("JS_TWITTER_POST_FETCHER_PATH",js_path("javascript-twitter-post-fetcher")) +JS_TWITTER_POST_FETCHER_PATH = get_setting( + "JS_TWITTER_POST_FETCHER_PATH", js_path("javascript-twitter-post-fetcher")) assert_dir(JS_TWITTER_POST_FETCHER_PATH) -assert_file(JS_TWITTER_POST_FETCHER_PATH+"/js/twitterFetcher_min.js") +assert_file(JS_TWITTER_POST_FETCHER_PATH + "/js/twitterFetcher_min.js") -JS_CYTOSCAPE_PATH = get_setting("JS_CYTOSCAPE_PATH",js_path("cytoscape")) +JS_CYTOSCAPE_PATH = get_setting("JS_CYTOSCAPE_PATH", js_path("cytoscape")) assert_dir(JS_CYTOSCAPE_PATH) -assert_file(JS_CYTOSCAPE_PATH+'/cytoscape.min.js') +assert_file(JS_CYTOSCAPE_PATH + '/cytoscape.min.js') # assert_file(PHEWAS_FILES+"/auwerx/PheWAS_pval_EMMA_norm.RData") diff --git a/wqflask/utility/type_checking.py b/wqflask/utility/type_checking.py index f15b17e2..00f14ba9 100644 --- a/wqflask/utility/type_checking.py +++ b/wqflask/utility/type_checking.py @@ -7,6 +7,7 @@ def is_float(value): except: return False + def is_int(value): try: int(value) @@ -14,6 +15,7 @@ def is_int(value): except: return False + def is_str(value): if value is None: return False @@ -23,20 +25,23 @@ def is_str(value): except: return False -def get_float(vars,name,default=None): - if name in vars: - if is_float(vars[name]): - return float(vars[name]) + +def get_float(vars_obj, name, default=None): + if name in vars_obj: + if is_float(vars_obj[name]): + return float(vars_obj[name]) return default -def get_int(vars,name,default=None): - if name in vars: - if is_int(vars[name]): - return float(vars[name]) + +def get_int(vars_obj, name, default=None): + if name in vars_obj: + if is_int(vars_obj[name]): + return float(vars_obj[name]) return default -def get_string(vars,name,default=None): - if name in vars: - if not vars[name] is None: - return str(vars[name]) + +def get_string(vars_obj, name, default=None): + if name in vars_obj: + if not vars_obj[name] is None: + return str(vars_obj[name]) return default diff --git a/wqflask/utility/webqtlUtil.py b/wqflask/utility/webqtlUtil.py index 53661ae4..0cb71567 100644 --- a/wqflask/utility/webqtlUtil.py +++ b/wqflask/utility/webqtlUtil.py @@ -33,44 +33,46 @@ from math import * from base import webqtlConfig # NL, 07/27/2010. moved from webqtlForm.py -#Dict of Parents and F1 information, In the order of [F1, Mat, Pat] -ParInfo ={ -'BXH':['BHF1', 'HBF1', 'C57BL/6J', 'C3H/HeJ'], -'AKXD':['AKF1', 'KAF1', 'AKR/J', 'DBA/2J'], -'BXD':['B6D2F1', 'D2B6F1', 'C57BL/6J', 'DBA/2J'], -'C57BL-6JxC57BL-6NJF2':['', '', 'C57BL/6J', 'C57BL/6NJ'], -'BXD300':['B6D2F1', 'D2B6F1', 'C57BL/6J', 'DBA/2J'], -'B6BTBRF2':['B6BTBRF1', 'BTBRB6F1', 'C57BL/6J', 'BTBRT<+>tf/J'], -'BHHBF2':['B6HF2','HB6F2','C57BL/6J','C3H/HeJ'], -'BHF2':['B6HF2','HB6F2','C57BL/6J','C3H/HeJ'], -'B6D2F2':['B6D2F1', 'D2B6F1', 'C57BL/6J', 'DBA/2J'], -'BDF2-1999':['B6D2F2', 'D2B6F2', 'C57BL/6J', 'DBA/2J'], -'BDF2-2005':['B6D2F1', 'D2B6F1', 'C57BL/6J', 'DBA/2J'], -'CTB6F2':['CTB6F2','B6CTF2','C57BL/6J','Castaneous'], -'CXB':['CBF1', 'BCF1', 'C57BL/6ByJ', 'BALB/cByJ'], -'AXBXA':['ABF1', 'BAF1', 'C57BL/6J', 'A/J'], -'AXB':['ABF1', 'BAF1', 'C57BL/6J', 'A/J'], -'BXA':['BAF1', 'ABF1', 'C57BL/6J', 'A/J'], -'LXS':['LSF1', 'SLF1', 'ISS', 'ILS'], -'HXBBXH':['SHR_BNF1', 'BN_SHRF1', 'BN-Lx/Cub', 'SHR/OlaIpcv'], -'BayXSha':['BayXShaF1', 'ShaXBayF1', 'Bay-0','Shahdara'], -'ColXBur':['ColXBurF1', 'BurXColF1', 'Col-0','Bur-0'], -'ColXCvi':['ColXCviF1', 'CviXColF1', 'Col-0','Cvi'], -'SXM':['SMF1', 'MSF1', 'Steptoe','Morex'], -'HRDP':['SHR_BNF1', 'BN_SHRF1', 'BN-Lx/Cub', 'SHR/OlaIpcv'] +# Dict of Parents and F1 information, In the order of [F1, Mat, Pat] +ParInfo = { + 'BXH': ['BHF1', 'HBF1', 'C57BL/6J', 'C3H/HeJ'], + 'AKXD': ['AKF1', 'KAF1', 'AKR/J', 'DBA/2J'], + 'BXD': ['B6D2F1', 'D2B6F1', 'C57BL/6J', 'DBA/2J'], + 'C57BL-6JxC57BL-6NJF2': ['', '', 'C57BL/6J', 'C57BL/6NJ'], + 'BXD300': ['B6D2F1', 'D2B6F1', 'C57BL/6J', 'DBA/2J'], + 'B6BTBRF2': ['B6BTBRF1', 'BTBRB6F1', 'C57BL/6J', 'BTBRT<+>tf/J'], + 'BHHBF2': ['B6HF2', 'HB6F2', 'C57BL/6J', 'C3H/HeJ'], + 'BHF2': ['B6HF2', 'HB6F2', 'C57BL/6J', 'C3H/HeJ'], + 'B6D2F2': ['B6D2F1', 'D2B6F1', 'C57BL/6J', 'DBA/2J'], + 'BDF2-1999': ['B6D2F2', 'D2B6F2', 'C57BL/6J', 'DBA/2J'], + 'BDF2-2005': ['B6D2F1', 'D2B6F1', 'C57BL/6J', 'DBA/2J'], + 'CTB6F2': ['CTB6F2', 'B6CTF2', 'C57BL/6J', 'Castaneous'], + 'CXB': ['CBF1', 'BCF1', 'C57BL/6ByJ', 'BALB/cByJ'], + 'AXBXA': ['ABF1', 'BAF1', 'C57BL/6J', 'A/J'], + 'AXB': ['ABF1', 'BAF1', 'C57BL/6J', 'A/J'], + 'BXA': ['BAF1', 'ABF1', 'C57BL/6J', 'A/J'], + 'LXS': ['LSF1', 'SLF1', 'ISS', 'ILS'], + 'HXBBXH': ['SHR_BNF1', 'BN_SHRF1', 'BN-Lx/Cub', 'SHR/OlaIpcv'], + 'BayXSha': ['BayXShaF1', 'ShaXBayF1', 'Bay-0', 'Shahdara'], + 'ColXBur': ['ColXBurF1', 'BurXColF1', 'Col-0', 'Bur-0'], + 'ColXCvi': ['ColXCviF1', 'CviXColF1', 'Col-0', 'Cvi'], + 'SXM': ['SMF1', 'MSF1', 'Steptoe', 'Morex'], + 'HRDP': ['SHR_BNF1', 'BN_SHRF1', 'BN-Lx/Cub', 'SHR/OlaIpcv'] } ######################################### # Accessory Functions ######################################### -def genRandStr(prefix = "", length=8, chars=string.letters+string.digits): + +def genRandStr(prefix="", length=8, chars=string.ascii_letters + string.digits): from random import choice _str = prefix[:] for i in range(length): _str += choice(chars) return _str + def ListNotNull(lst): '''Obsolete - Use built in function any (or all or whatever) @@ -83,15 +85,17 @@ def ListNotNull(lst): return 1 return None -def readLineCSV(line): ### dcrowell July 2008 + +def readLineCSV(line): # dcrowell July 2008 """Parses a CSV string of text and returns a list containing each element as a string. Used by correlationPage""" returnList = line.split('","') - returnList[-1]=returnList[-1][:-2] - returnList[0]=returnList[0][1:] + returnList[-1] = returnList[-1][:-2] + returnList[0] = returnList[0][1:] return returnList -def cmpEigenValue(A,B): + +def cmpEigenValue(A, B): try: if A[0] > B[0]: return -1 @@ -102,12 +106,13 @@ def cmpEigenValue(A,B): except: return 0 + def hasAccessToConfidentialPhenotypeTrait(privilege, userName, authorized_users): access_to_confidential_phenotype_trait = 0 if webqtlConfig.USERDICT[privilege] > webqtlConfig.USERDICT['user']: access_to_confidential_phenotype_trait = 1 else: - AuthorizedUsersList=map(string.strip, string.split(authorized_users, ',')) - if AuthorizedUsersList.__contains__(userName): + AuthorizedUsersList = [x.strip() for x in authorized_users.split(',')] + if userName in AuthorizedUsersList: access_to_confidential_phenotype_trait = 1 - return access_to_confidential_phenotype_trait
\ No newline at end of file + return access_to_confidential_phenotype_trait |