From 1da3cee0c7fdd1dbfeaf4847aa9533c08b511063 Mon Sep 17 00:00:00 2001 From: Morten Johansen Date: Tue, 9 Aug 2016 18:15:22 +0200 Subject: [PATCH 001/123] Morj: Removing some HyperBrowser code from ProTo. Clean up ProTo controllers. SG: Removed some more HB code from ProTo, including common.js and CommonFunctions, and cleaned up. (cherry picked from commit 6f219a5220ea54b89b8239f07ad440e9aaa4d3b0) --- .../scripts/mvc/dataset/dataset-li-edit.js | 4 +- lib/galaxy/tools/__init__.py | 4 +- .../webapps/galaxy/controllers/proto.py | 63 +-- .../webapps/galaxy/controllers/tool_runner.py | 21 +- lib/proto/BaseToolController.py | 26 +- lib/proto/CommonFunctions.py | 398 ++------------- ...serToolTypes.py => galaxy_tool_classes.py} | 39 +- lib/proto/generictool.py | 197 ++++---- lib/proto/hyper_gui.py | 238 --------- lib/proto/tools/ExploreToolsTool.xml | 2 +- lib/proto/tools/GeneralGuiTool.py | 287 ++--------- lib/proto/tools/GenerateToolsTool.xml | 2 +- lib/proto/tools/InstallToolsTool.xml | 2 +- lib/proto/tools/ManageTools.py | 9 +- templates/proto/common.js | 474 +----------------- templates/proto/functions.mako | 234 +-------- templates/proto/generictool.mako | 104 ++-- 17 files changed, 356 insertions(+), 1748 deletions(-) rename lib/proto/{HyperBrowserToolTypes.py => galaxy_tool_classes.py} (84%) diff --git a/client/galaxy/scripts/mvc/dataset/dataset-li-edit.js b/client/galaxy/scripts/mvc/dataset/dataset-li-edit.js index 853e0563da21..f64d4283209f 100644 --- a/client/galaxy/scripts/mvc/dataset/dataset-li-edit.js +++ b/client/galaxy/scripts/mvc/dataset/dataset-li-edit.js @@ -172,8 +172,8 @@ var DatasetListItemEdit = _super.extend( require([ 'mvc/tool/tool-form' ], function( ToolForm ){ var form = new ToolForm.View({ 'job_id' : creating_job }); form.deferred.execute( function(){ - //console.log(form.options); - if (form.options.model_class.startsWith('HyperBrowser')) + console.log(form.options); + if (form.options.model_class.startsWith('Proto')) galaxy_main.location = rerun_url; else Galaxy.app.display( form ); diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py index ec88606a9e8e..de877d25b161 100755 --- a/lib/galaxy/tools/__init__.py +++ b/lib/galaxy/tools/__init__.py @@ -2847,8 +2847,8 @@ def allow_user_access( self, user, attempting_access=True ): tool_types[ tool_class.tool_type ] = tool_class # add ProTo tool types -from proto.HyperBrowserToolTypes import hb_tool_types -tool_types.update(hb_tool_types) +from proto.galaxy_tool_classes import proto_tool_types +tool_types.update(proto_tool_types) # ---- Utility classes to be factored out ----------------------------------- diff --git a/lib/galaxy/webapps/galaxy/controllers/proto.py b/lib/galaxy/webapps/galaxy/controllers/proto.py index 80b96ba1bcd7..dc8f4ca001db 100644 --- a/lib/galaxy/webapps/galaxy/controllers/proto.py +++ b/lib/galaxy/webapps/galaxy/controllers/proto.py @@ -16,19 +16,19 @@ import sys, os -from galaxy.web.base.controller import * -import logging, sets, time +from galaxy.web.base.controller import web, error, BaseUIController +import logging log = logging.getLogger( __name__ ) import traceback -from multiprocessing import Process, Pipe, Array, Queue +from multiprocessing import Process, Pipe, Queue from importlib import import_module + class ProtoController( BaseUIController ): - @staticmethod - def __index_pipe(response, trans, tool): + def run_fork(self, response, trans, mako): # logging locks and/or atexit handlers may be cause of deadlocks in a fork from thread # attempt to fix by shutting down and reloading logging module and clear exit handlers # logging.shutdown() @@ -48,30 +48,33 @@ def __index_pipe(response, trans, tool): html = '' #response.send_bytes('ping') try: -# from gold.application.GalaxyInterface import GalaxyInterface -# template_mako = '/hyperbrowser/' + tool + '.mako' - template_mako = '/proto/' + tool + '.mako' - toolController = None - try: - #toolModule = __import__('proto.' + tool, globals(), locals(), ['getController']) - toolModule = import_module('proto.' + tool) - toolController = toolModule.getController(trans) - except Exception, e: - print e - exc_info = sys.exc_info() - pass - - #html = trans.fill_template(template_mako, trans=trans, hyper=GalaxyInterface, control=toolController) - html = trans.fill_template(template_mako, trans=trans, control=toolController) + html, exc_info = self.run_tool(mako, trans) except Exception, e: html = '
\n'
             if exc_info:
-                html += str(e) + ':\n' + ''.join(traceback.format_exception(exc_info[0],exc_info[1],exc_info[2])) + '\n\n'
+               html += str(e) + ':\n' + exc_info + '\n\n'
+#               html += str(e) + ':\n' + ''.join(traceback.format_exception(exc_info[0],exc_info[1],exc_info[2])) + '\n\n'
             html += str(e) + ':\n' + traceback.format_exc() + '\n
' response.send_bytes(html) response.close() + def run_tool(self, mako, trans): + toolController = None + exc_info = None + try: + toolModule = import_module('proto.' + mako) + toolController = toolModule.getController(trans) + except Exception, e: + # print e + exc_info = sys.exc_info() + if mako.startswith('/'): + template_mako = mako + '.mako' + html = trans.fill_template(template_mako, trans=trans, control=toolController) + else: + template_mako = '/proto/' + mako + '.mako' + html = trans.fill_template(template_mako, trans=trans, control=toolController) + return html, exc_info @web.expose def index(self, trans, mako = 'generictool', **kwd): @@ -80,29 +83,27 @@ def index(self, trans, mako = 'generictool', **kwd): if isinstance(mako, list): mako = mako[0] - - #trans.sa_session.flush() - # trans.sa_session.close() - done = False - while not done: + retry = 3 + while retry > 0: + retry -= 1 trans.sa_session.flush() my_end, your_end = Pipe() - proc = Process(target=self.__index_pipe, args=(your_end,trans,str(mako))) + proc = Process(target=self.run_fork, args=(your_end,trans,str(mako))) proc.start() html = '' if proc.is_alive(): - if my_end.poll(10): + if my_end.poll(15): #ping = my_end.recv_bytes() html = my_end.recv_bytes() my_end.close() - done = True + break else: - log.warn('Fork timed out after 10 sec. Retrying...') + log.warn('Fork timed out after 15 sec. Retrying...') else: log.warn('Fork died on startup.') - done = True + break proc.join(1) if proc.is_alive(): diff --git a/lib/galaxy/webapps/galaxy/controllers/tool_runner.py b/lib/galaxy/webapps/galaxy/controllers/tool_runner.py index 1f9acb29e7c0..2305cf6c9a50 100644 --- a/lib/galaxy/webapps/galaxy/controllers/tool_runner.py +++ b/lib/galaxy/webapps/galaxy/controllers/tool_runner.py @@ -11,6 +11,9 @@ from galaxy.web import error, url_for from galaxy.web.base.controller import BaseUIController +# import ProTo tool types +from galaxy.tools import proto_tool_types + log = logging.getLogger( __name__ ) @@ -75,8 +78,9 @@ def index( self, trans, tool_id=None, from_noframe=None, **kwd ): if len( params ) > 0: trans.log_event( 'Tool params: %s' % ( str( params ) ), tool_id=tool_id ) - # ProTo: if no params then this is a direct request/link to open the tool interface, redirect hyperbrowser-tools to the hyper-controller - if tool.tool_type.startswith('hyperbrowser') and len(params) == 0: + # ProTo: if no params then this is a direct request/link to open the tool interface, redirect hyperbrowser-tools + # to the hyper-controller + if len(params) == 0 and proto_tool_types.has_key(tool.tool_type): if tool.inputs.has_key('mako'): return trans.response.send_redirect(url_for(controller=tool.action, tool_id=tool_id, mako=tool.inputs['mako'].get_initial_value(None, None))) @@ -118,15 +122,16 @@ def rerun( self, trans, id=None, job_id=None, **kwd ): tool_id = job.tool_id tool = self.__get_tool(tool_id) - if tool.tool_type.startswith('hyperbrowser'): + if proto_tool_types.has_key(tool.tool_type): # Get the job's parameters - try: - params_objects = job.get_param_values(trans.app, ignore_errors=True) - except: - raise Exception('Failed to get job params') + #try: + # params_objects = job.get_param_values(trans.app, ignore_errors=True) + #except: + # raise Exception('Failed to get job params') return trans.response.send_redirect( - url_for(controller='proto', action='index', mako=params_objects.get('mako', 'generictool'), + url_for(controller=tool.action, action='index', + mako=tool.inputs['mako'].get_initial_value(None, None), rerun_hda_id=id)) return trans.response.send_redirect( url_for( controller="root", job_id=job_id ) ) diff --git a/lib/proto/BaseToolController.py b/lib/proto/BaseToolController.py index 649b5e8cf83d..ca83ea300c2a 100644 --- a/lib/proto/BaseToolController.py +++ b/lib/proto/BaseToolController.py @@ -18,7 +18,7 @@ import sys #from gold.application.GalaxyInterface import GalaxyInterface from collections import OrderedDict -from hyper_gui import load_input_parameters, SelectElement, \ +from proto.hyper_gui import load_input_parameters, SelectElement, \ GalaxyWrapper, getDataFilePath from config import Config from urllib import unquote @@ -30,6 +30,10 @@ def __init__(self, trans = None, job = None): elif job: self.openJobParams(job) + def _init(self): + if hasattr(super(BaseToolController, self), '_init'): + super(BaseToolController, self)._init() + def openTransaction(self, trans): self.transaction = trans self.galaxy = GalaxyWrapper(trans) @@ -71,7 +75,6 @@ def _getGalaxyGenomes(self): return [(gb[1], gb[0], False) for gb in self.transaction.app.genome_builds.get_genome_build_names()] def _getAllGenomes(self): - #return [('----- Select -----', '', False)] + GalaxyInterface.getAllGenomes(self.galaxy.getUserName()) return [('----- Select -----', '', False)] + self._getGalaxyGenomes() def getGenomeElement(self, id='dbkey', genomeList = None): @@ -87,26 +90,11 @@ def getGenome(self, id='dbkey'): return self.params.get('dbkey') elif self.params.has_key(id): self.params['dbkey'] = self.params.get(id) -# return self.params.get('dbkey', self._getAllGenomes()[0][1]) - return self.params.get('dbkey') + return self.params.get('dbkey', self._getAllGenomes()[0][1]) +# return self.params.get('dbkey') def getDictOfAllGenomes(self): return OrderedDict([(x[0],False) for x in self._getAllGenomes()[1:]]) - - def getTrackElement(self, id, label, history=False, ucsc=False, tracks=None): - datasets = [] - if history: - try: - datasets = self.galaxy.getHistory(GalaxyInterface.getSupportedGalaxyFileFormats()) - except Exception, e: - print e - element = TrackWrapper(id, GalaxyInterface, [], self.galaxy, datasets, self.getGenome(), ucscTracks=ucsc) - if tracks is not None: - element.tracks = tracks - else: - element.fetchTracks() - element.legend = label - return element def getDataFilePath(self, id): if hasattr(self, 'galaxy'): diff --git a/lib/proto/CommonFunctions.py b/lib/proto/CommonFunctions.py index 88bea3315d44..dca6e507b14a 100644 --- a/lib/proto/CommonFunctions.py +++ b/lib/proto/CommonFunctions.py @@ -15,12 +15,46 @@ # along with The Genomic HyperBrowser. If not, see . import os +import shelve import sys import functools import re import urllib import contextlib +from proto.config.Config import GALAXY_BASE_DIR + +""" +Note on datasetInfo and datasetId (used in several functions): + +DatasetInfo is an especially coded list of strings, used mainly to process +files from galaxy history, but can also be used otherwise. Structure is: +['galaxy', fileEnding, datasetFn, name]. The first element is used for +assertion. The second element contains the file format (as galaxy force +the ending '.dat'). datasetFn is the dataset file name, typically ending +with 'XXX/dataset_YYYY.dat', where XXX and YYYY are numbers which may be +extracted and used as a datasetId in the form [XXX, YYYY]. The last element +is the name of the history element, mostly used for presentation purposes. +""" + + +def getToolPrototype(toolId): + tool_shelve = None + try: + tool_shelve = shelve.open( + GALAXY_BASE_DIR + '/database/proto-tool-cache.shelve', 'r') + module_name, class_name = tool_shelve[str(toolId)] + module = __import__(module_name, fromlist=[class_name]) + # print module, class_name, toolId + prototype = getattr(module, class_name)(toolId) + # print "Loaded proto tool:", class_name + #except KeyError: + # prototype = None + finally: + if tool_shelve: + tool_shelve.close() + return prototype + def ensurePathExists(fn): "Assumes that fn consists of a basepath (folder) and a filename, and ensures that the folder exists." @@ -32,28 +66,6 @@ def ensurePathExists(fn): #os.umask(oldMask) -def reloadModules(): - for module in [val for key,val in sys.modules.iteritems() \ - if key.startswith('gold') or key.startswith('quick') or key.startswith('test')]: - try: - reload(module) - except: - print module - - -def wrapClass(origClass, keywords={}): - #for key in keywords.keys(): - # if re.match('^[0-9]+$',keywords[key]) is not None: - # keywords[key] = int(keywords[key]) - # elif re.match('^[0-9]?[.][0-9]?$',keywords[key]) is not None and keywords[key] != '.': - # keywords[key] = float(keywords[key]) - - args = [] - wrapped = functools.partial(origClass, *args, **keywords) - functools.update_wrapper(wrapped, origClass) - return wrapped - - def extractIdFromGalaxyFn(fn): ''' Extracts the Galaxy history ID from a history file path, e.g.: @@ -156,12 +168,11 @@ def getGalaxyFnFromAnyDatasetId(id, galaxyFilePath=None): def getGalaxyFilesDir(galaxyFn): return galaxyFn[:-4] + '_files' - ''' - id is the relative file hierarchy, encoded as a list of strings - ''' - def getGalaxyFilesFilename(galaxyFn, id): + """ + id is the relative file hierarchy, encoded as a list of strings + """ return os.path.sep.join([getGalaxyFilesDir(galaxyFn)] + id) @@ -191,7 +202,7 @@ def extractFileSuffixFromDatasetInfo(datasetInfo, fileSuffixFilterList=None): suffix = datasetInfo[1] - if fileSuffixFilterList and not suffix.lower() in fileSuffixFilterList(): + if fileSuffixFilterList and not suffix.lower() in fileSuffixFilterList: raise Exception('File type "' + suffix + '" is not supported.') return suffix @@ -205,216 +216,9 @@ def extractNameFromDatasetInfo(datasetInfo): return unquote(datasetInfo[-1]) -# def getUniqueRunSpecificId(id=[]): -# return ['run_specific'] + id -# -# def getUniqueWebPath(id=[]): -# from proto.config.Config import STATIC_PATH -# return os.sep.join([STATIC_PATH] + getUniqueRunSpecificId(id)) - - -def getLoadToGalaxyHistoryURL(fn, genome='hg18', galaxyDataType='bed', urlPrefix=None): - if urlPrefix is None: - from proto.config.Config import URL_PREFIX - urlPrefix = URL_PREFIX - - import base64 - - assert galaxyDataType in ['bed', 'bedgraph', 'gtrack', 'gsuite'] - - return urlPrefix + '/tool_runner?tool_id=file_import_%s&dbkey=%s&runtool_btn=yes&input=' % (galaxyDataType, genome) \ - + base64.urlsafe_b64encode(fn) + ('&datatype='+galaxyDataType if galaxyDataType is not None else '') - -# def getRelativeUrlFromWebPath(webPath): -# from proto.config.Config import GALAXY_BASE_DIR, URL_PREFIX -# if webPath.startswith(GALAXY_BASE_DIR): -# return URL_PREFIX + webPath[len(GALAXY_BASE_DIR):] - - -def isFlatList(list): - for l in list: - if type(l) == type([]): - return False - return True - - -def flattenList(list): - ''' - recursively flattens a nested list (does not handle dicts and sets..) e.g. - [1, 2, 3, 4, 5] == flattenList([[], [1,2],[3,4,5]]) - ''' - if isFlatList(list): - return list - else: - return flattenList( reduce(lambda x,y: x+y, list ) ) - - -def listStartsWith(a, b): - return len(a) > len(b) and a[:len(b)] == b - - -def isNan(a): - import numpy - - try: - return numpy.isnan(a) - except (TypeError, NotImplementedError): - return False - - -def isListType(x): - import numpy - return type(x) == list or type(x) == tuple or isinstance(x, numpy.ndarray) or isinstance(x, dict) - - -def ifDictConvertToList(d): - return [(x, d[x]) for x in sorted(d.keys())] if isinstance(d, dict) else d - - -def smartRecursiveAssertList(x, y, assertEqualFunc, assertAlmostEqualFunc): - import numpy - - if isListType(x): - if isinstance(x, numpy.ndarray): - try: - if not assertEqualFunc(x.shape, y.shape): - return False - except Exception, e: - raise AssertionError(str(e) + ' on shape of lists: ' + str(x) + ' and ' + str(y)) - - try: - if not assertEqualFunc(x.dtype, y.dtype): - return False - except Exception, e: - raise AssertionError(str(e) + ' on datatypes of lists: ' + str(x) + ' and ' + str(y)) - else: - try: - if not assertEqualFunc(len(x), len(y)): - return False - except Exception, e: - raise AssertionError(str(e) + ' on length of lists: ' + str(x) + ' and ' + str(y)) - - for el1,el2 in zip(*[ifDictConvertToList(x) for x in [x, y]]): - if not smartRecursiveAssertList(el1, el2, assertEqualFunc, assertAlmostEqualFunc): - return False - return True - - else: - try: - return assertAlmostEqualFunc(x, y) - except TypeError: - return assertEqualFunc(x, y) - - -def bothIsNan(a, b): - import numpy - - try: - if not any(isListType(x) for x in [a,b]): - return numpy.isnan(a) and numpy.isnan(b) - except (TypeError, NotImplementedError): - pass - return False - - -def smartEquals(a, b): - if bothIsNan(a, b): - return True - return a == b - - -def smartRecursiveEquals(a, b): - return smartRecursiveAssertList(a, b, smartEquals, smartEquals) - - -def reorderTrackNameListFromTopDownToBottomUp(trackNameSource): - prevTns = [] - source = trackNameSource.__iter__() - trackName = source.next() - - try: - while True: - if len(prevTns) == 0 or listStartsWith(trackName, prevTns[0]): - prevTns.insert(0, trackName) - trackName = source.next() - continue - yield prevTns.pop(0) - - except StopIteration: - while len(prevTns) > 0: - yield prevTns.pop(0) - - -R_ALREADY_SILENCED = False -R_ALREADY_SILENCED_OUTPUT = False - - -def silenceRWarnings(): - global R_ALREADY_SILENCED - if not R_ALREADY_SILENCED: - from proto.RSetup import r - r('sink(file("/dev/null", open="wt"), type="message")') - R_ALREADY_SILENCED = True - - -def silenceROutput(): - global R_ALREADY_SILENCED_OUTPUT - if not R_ALREADY_SILENCED_OUTPUT: - from proto.RSetup import r - r('sink(file("/dev/null", open="wt"), type="output")') - R_ALREADY_SILENCED_OUTPUT = True - - -def createHyperBrowserURL(genome, trackName1, trackName2=None, track1file=None, track2file=None, \ - demoID=None, analcat=None, analysis=None, \ - configDict=None, trackIntensity=None, method=None, region=None, \ - binsize=None, chrs=None, chrArms=None, chrBands=None, genes=None): - urlParams = [] - urlParams.append( ('dbkey', genome) ) - urlParams.append( ('track1', ':'.join(trackName1)) ) - if trackName2: - urlParams.append( ('track2', ':'.join(trackName2)) ) - if track1file: - urlParams.append( ('track1file', track1file) ) - if track2file: - urlParams.append( ('track2file', track2file) ) - if demoID: - urlParams.append( ('demoID', demoID) ) - if analcat: - urlParams.append( ('analcat', analcat) ) - if analysis: - urlParams.append( ('analysis', analysis) ) - if configDict: - for key, value in configDict.iteritems(): - urlParams.append( ('config_%s' % key, value) ) - if trackIntensity: - urlParams.append( ('trackIntensity', trackIntensity) ) - if method: - urlParams.append( ('method', method) ) - if region: - urlParams.append( ('region', region) ) - if binsize: - urlParams.append( ('binsize', binsize) ) - if chrs: - urlParams.append( ('__chrs__', chrs) ) - if chrArms: - urlParams.append( ('__chrArms__', chrArms) ) - if chrBands: - urlParams.append( ('__chrBands__', chrBands) ) - if genes: - urlParams.append( ('genes', genes) ) - #genes not __genes__? - #encode? - - from proto.config.Config import URL_PREFIX - return URL_PREFIX + '/hyper?' + '&'.join([urllib.quote(key) + '=' + \ - urllib.quote(value) for key,value in urlParams]) - - def createToolURL(toolId, **kwArgs): - from proto.config.Config import URL_PREFIX - return URL_PREFIX + '/hyper?mako=generictool&tool_id=' + toolId + \ - ''.join(['&' + urllib.quote(key) + '=' + urllib.quote(value) for key,value in kwArgs.iteritems()]) + from proto.tools.GeneralGuiTool import GeneralGuiTool + return GeneralGuiTool.createGenericGuiToolURL(toolId, tool_choices=kwArgs) def createGalaxyToolURL(toolId, **kwArgs): @@ -423,119 +227,13 @@ def createGalaxyToolURL(toolId, **kwArgs): ''.join(['&' + urllib.quote(key) + '=' + urllib.quote(value) for key,value in kwArgs.iteritems()]) +def getLoadToGalaxyHistoryURL(fn, genome='hg18', galaxyDataType='bed', urlPrefix=None): + if urlPrefix is None: + from proto.config.Config import URL_PREFIX + urlPrefix = URL_PREFIX -def numAsPaddedBinary(comb, length): - return '0'*(length-len(bin(comb)[2:]))+bin(comb)[2:] - - -@contextlib.contextmanager -def changedWorkingDir(new_dir): - orig_dir = os.getcwd() - os.chdir(new_dir) - try: - yield - finally: - os.chdir(orig_dir) - - -def convertTNstrToTNListFormat(tnStr, doUnquoting=False): - tnList = re.split(':|\^|\|', tnStr) - if doUnquoting: - tnList = [urllib.unquote(x) for x in tnList] - return tnList - - -#used by echo -def format_arg_value(arg_val): - """ Return a string representing a (name, value) pair. - - >>> format_arg_value(('x', (1, 2, 3))) - 'x=(1, 2, 3)' - """ - arg, val = arg_val - return "%s=%r" % (arg, val) - - -def echo(fn, write=sys.stdout.write): - """ Echo calls to a function. - - Returns a decorated version of the input function which "echoes" calls - made to it by writing out the function's name and the arguments it was - called with. - """ - import functools - # Unpack function's arg count, arg names, arg defaults - code = fn.func_code - argcount = code.co_argcount - argnames = code.co_varnames[:argcount] - fn_defaults = fn.func_defaults or list() - argdefs = dict(zip(argnames[-len(fn_defaults):], fn_defaults)) - - @functools.wraps(fn) - def wrapped(*v, **k): - # Collect function arguments by chaining together positional, - # defaulted, extra positional and keyword arguments. - positional = map(format_arg_value, zip(argnames, v)) - defaulted = [format_arg_value((a, argdefs[a])) - for a in argnames[len(v):] if a not in k] - nameless = map(repr, v[argcount:]) - keyword = map(format_arg_value, k.items()) - args = positional + defaulted + nameless + keyword - write("%s(%s)\n" % (name(fn), ", ".join(args))) - return fn(*v, **k) - return wrapped - - -def getGeSource(track, genome=None): - - from quick.application.ExternalTrackManager import ExternalTrackManager - from gold.origdata.BedGenomeElementSource import BedGenomeElementSource, BedCategoryGenomeElementSource - from gold.origdata.GtrackGenomeElementSource import GtrackGenomeElementSource - from gold.origdata.TrackGenomeElementSource import FullTrackGenomeElementSource - - if type(track) == str: - track = track.split(':') - - try: - fileType = ExternalTrackManager.extractFileSuffixFromGalaxyTN(track) - fn = ExternalTrackManager.extractFnFromGalaxyTN(track) - if fileType == 'category.bed': - return BedCategoryGenomeElementSource(fn) - elif fileType == 'gtrack': - return GtrackGenomeElementSource(fn) - else: - return BedGenomeElementSource(fn) - except: - return FullTrackGenomeElementSource(genome, track, allowOverlaps=False) - - -# generate powerset for set -# powerset([a,b,c]) = [(), (a), (b), (c), (a,b), (a,c), (a,b), (a,b,c)) -def powerset(iterable): - from itertools import chain, combinations - s = list(iterable) - return chain.from_iterable(combinations(s, r) for r in range(len(s)+1)) - - -# Generate all supersets of a set represented by a binary string -# e.g. allSupersets('010') = ['110','011','111'] -def allSupersets(binaryString): - length = len(binaryString) - binaryList = list(binaryString) - zeroIndex = [i for i,val in enumerate(binaryList) if val == '0'] - for comb in powerset(zeroIndex): - if comb: - yield ''.join([binaryList[i] if i not in comb else '1' for i in range(length)]) - - -def getUniqueFileName(origFn): - import os - - i = 0 - newOrigFn = origFn - - while os.path.exists(newOrigFn): - newOrigFn = origFn + '.%s' % i - i += 1 + import base64 - return newOrigFn + assert galaxyDataType is not None + return urlPrefix + '/tool_runner?tool_id=file_import&dbkey=%s&runtool_btn=yes&input=' % (genome,) \ + + base64.urlsafe_b64encode(fn) + ('&format=' + galaxyDataType if galaxyDataType is not None else '') diff --git a/lib/proto/HyperBrowserToolTypes.py b/lib/proto/galaxy_tool_classes.py similarity index 84% rename from lib/proto/HyperBrowserToolTypes.py rename to lib/proto/galaxy_tool_classes.py index 7c6913ce22af..142d37f0466f 100644 --- a/lib/proto/HyperBrowserToolTypes.py +++ b/lib/proto/galaxy_tool_classes.py @@ -3,14 +3,14 @@ from galaxy.tools import Tool, DataSourceTool from galaxy.tools.parser.output_actions import ToolOutputActionGroup from galaxy.tools.parser.output_objects import ToolOutput -from galaxy.util.odict import odict from galaxy.tools.parser.output_collection_def import DEFAULT_DATASET_COLLECTOR_DESCRIPTION +from galaxy.util.odict import odict log = logging.getLogger( __name__ ) -### HyperBrowser Tools -class HyperBrowserTool( DataSourceTool ): - tool_type = 'hyperbrowser' +### Proto Tools +class ProtoTool(DataSourceTool): + tool_type = 'proto' def parse_inputs( self, root ): Tool.parse_inputs( self, root ) @@ -33,6 +33,8 @@ def exec_before_job( self, app, inp_data, out_data, param_dict): out_data2[name] = data param_dict['file_path'] = os.path.abspath(os.path.join(app.config.root, app.config.file_path)) + # Galaxy removes the tool_id from params, add it back for ProTo + param_dict['tool_id'] = self.id DataSourceTool.exec_before_job(self, app, inp_data, out_data2, param_dict) def exec_after_process(self, app, inp_data, out_data, param_dict, job = None): @@ -41,11 +43,13 @@ def exec_after_process(self, app, inp_data, out_data, param_dict, job = None): for name, data in out_data.items(): data.info = urllib.unquote(job_info) self.sa_session.flush() - #app.model.context.flush() -class HyperBrowserGenericTool( HyperBrowserTool ): - tool_type = 'hyperbrowser_generic' +class ProtoGenericTool(ProtoTool): + tool_type = 'proto_generic' + proto_mako = 'generictool' + proto_action = '/proto' + proto_command = '$GALAXY_ROOT_DIR/lib/proto/protoToolExecute.py $output' def parse( self, tool_source, guid=None ): root = tool_source.root @@ -60,18 +64,20 @@ def parse( self, tool_source, guid=None ): if root.find('inputs') is None: inputs = ElementTree.Element('inputs') - inputs.append(ElementTree.Element('param', name='mako', type='hidden', value='generictool')) + inputs.append(ElementTree.Element('param', name='mako', type='hidden', value=self.proto_mako)) inputs.append(ElementTree.Element('param', name='tool_id', type='hidden', value=root.get('id'))) + inputs.append(ElementTree.Element('param', name='tool_name', type='hidden', value=root.get('id'))) root.append(inputs) if root.find('outputs') is None: outputs = ElementTree.Element('outputs') outputs.append(ElementTree.Element('data', format='html', name='output')) root.append(outputs) - HyperBrowserTool.parse(self, tool_source, guid) - self.command = '$GALAXY_ROOT_DIR/lib/proto/protoToolExecute.py $output' + super(ProtoGenericTool, self).parse(tool_source, guid) + #self.command = '$GALAXY_ROOT_DIR/lib/proto/protoToolExecute.py $output' + self.command = self.proto_command self.interpreter = 'python' self.options['sanitize'] = False - self.action = '/proto' + self.action = self.proto_action self.check_values = False self.method = 'post' @@ -113,13 +119,6 @@ def execute( self, trans, incoming={}, set_output_hid=True, history=None, **kwar return self.tool_action.execute( self, trans, incoming=incoming, set_output_hid=set_output_hid, history=history, **kwargs ) -class HyperBrowserMultiGenericTool( HyperBrowserGenericTool ): - tool_type = 'hyperbrowser_multi_generic' - - def parse( self, root, guid=None ): - HyperBrowserGenericTool.parse(self, root, guid) - self.command = 'multiGenericTool.py $output' - - -hb_tool_types = {'hyperbrowser': HyperBrowserTool, 'hyperbrowser_generic': HyperBrowserGenericTool} +proto_tool_types = {'proto': ProtoTool, + 'proto_generic': ProtoGenericTool} diff --git a/lib/proto/generictool.py b/lib/proto/generictool.py index d86442a8e0e4..a14d6636ae19 100644 --- a/lib/proto/generictool.py +++ b/lib/proto/generictool.py @@ -20,17 +20,14 @@ import cPickle as pickle from zlib import compress, decompress from base64 import urlsafe_b64decode, urlsafe_b64encode -from collections import namedtuple, OrderedDict +from collections import namedtuple, OrderedDict, defaultdict from urllib import quote, unquote -#from gold.application.GalaxyInterface import GalaxyInterface -#from quick.webtools.GeneralGuiToolsFactory import GeneralGuiToolsFactory -#from quick.util.StaticFile import StaticImage from proto.tools.GeneralGuiTool import HistElement from proto.HtmlCore import HtmlCore from proto.config.Config import URL_PREFIX, GALAXY_BASE_DIR -#from gold.application.LogSetup import usageAndErrorLogging -#from gold.util.CommonFunctions import getClassName from BaseToolController import BaseToolController +from proto.CommonFunctions import getToolPrototype +from proto.StaticFile import StaticImage def getClassName(obj): return obj.__class__.__name__ @@ -53,21 +50,8 @@ def __init__(self, trans, job): self.subClassId = unquote(self.params.get('sub_class_id', '')) - self.prototype = None - tool_shelve = None - try: - tool_shelve = shelve.open(GALAXY_BASE_DIR + '/database/proto-tool-cache.shelve', 'r') - module_name, class_name = tool_shelve[str(self.toolId)] - module = __import__(module_name, fromlist=[class_name]) - self.prototype = getattr(module, class_name)(self.toolId) - #print "Loaded proto tool:", class_name - except KeyError as exc: - #print exc, 'trying GeneralGuiToolsFactory' - self.prototype = GeneralGuiToolsFactory.getWebTool(self.toolId) - finally: - if tool_shelve: - tool_shelve.close() + self.prototype = getToolPrototype(self.toolId) self._monkeyPatchAttr('userName', self.params.get('userEmail')) @@ -106,13 +90,16 @@ def __init__(self, trans, job): self.inputOrder = self._getIdxList(self.prototype.getInputBoxOrder()) self.resetBoxes = self._getIdxList(self.prototype.getResetBoxes()) - self.trackElements = {} self.extra_output = [] + self._init() self._initCache() if trans: self.action() + + self.inputGroup = self._getInputGroup(self.prototype.getInputBoxGroups(self.choices)) + if hasattr(self.prototype, 'getExtraHistElements'): extra_output = self.prototype.getExtraHistElements(self.choices) if extra_output: @@ -122,7 +109,19 @@ def __init__(self, trans, job): else: self.extra_output.append(e) + def _init(self): + super(GenericToolController, self)._init() + def _getInputGroup(self, inputBoxGroups): + startGroupInfo = defaultdict(list) + endIdxs = list() + if inputBoxGroups: + for group in inputBoxGroups: + idxBegin = self._getIdxForBoxId(group[1]) + idxEnd = self._getIdxForBoxId(group[2]) + startGroupInfo[idxBegin].append(group[0]) + endIdxs.append(idxEnd) + return startGroupInfo, endIdxs def _getInputBoxNames(self): names = self.prototype.getInputBoxNames() @@ -156,6 +155,17 @@ def _getIdxList(self, inputList): raise IndexError('List index out of range: %d >= %d' % (idx, len(self.inputIds))) return idxList + def _getIdxForBoxId(self, i): + if isinstance(i, str): + try: + idx = self.inputIds.index(i) + except ValueError: + if i.startswith('box'): + idx = int(i[3:]) - 1 + else: + idx = i - 1 + return idx + def _getOptionsBox(self, i, val = None): id = self.inputIds[i] id = id[0].upper() + id[1:] @@ -227,7 +237,7 @@ def getOptionsBox(self, id, i, val): #print 'from cache:',id self.input_changed = (val != self.cachedParams[id]) except Exception as e: - print 'cache load fail', e + print 'cache load failed:', e, id opts, info = self._getOptionsBox(i, val) self.input_changed = True @@ -290,19 +300,7 @@ def action(self): elif opts == '__track__': self.inputTypes += ['__track__'] - try: - #assert False - cachedTracks = self.getCacheData(id) - track = self.getTrackElement(id, name, tracks=cachedTracks) - except: - print 'track cache fail' - track = self.getTrackElement(id, name) - self.putCacheData(id, track.tracks) - self.trackElements[id] = track - tn = track.definition(False) - GalaxyInterface.cleanUpTrackName(tn) - val = ':'.join(tn) - #val = track.asString() + val = self.getInputValueForTrack(id, name) elif opts == '__password__': self.inputTypes += ['__password__'] @@ -341,12 +339,7 @@ def action(self): elif opts[0] == '__track__': self.inputTypes += ['__track__'] - track = self.getTrackElement(id, name, True if 'history' in opts else False, True if 'ucsc' in opts else False) - self.trackElements[id] = track - tn = track.definition(False) - GalaxyInterface.cleanUpTrackName(tn) - val = ':'.join(tn) - #val = track.asString() + val = self.getInputValueForTrack(id, name) elif opts[0] == '__hidden__': self.inputTypes += opts[:1] @@ -418,6 +411,39 @@ def action(self): def _action(self): pass + + def decodeChoice(self, opts, id, choice): + if opts == '__genome__': + id = 'dbkey' + choice = str(self.params[id]) if self.params.has_key(id) else '' + + # if isinstance(opts, tuple): + # if opts[0] == '__hidden__': + # choice = unquote(choice) + + if opts == '__genomes__' or (isinstance(opts, tuple) and opts[0] == '__multihistory__'): + values = {} + for key in self.params.keys(): + if key.startswith(id + '|'): + values[key.split('|')[1]] = self.params[key] + choice = OrderedDict(sorted(values.items(), \ + key=lambda t: int(t[0]) if opts[0] == '__multihistory__' else t[0])) + + if isinstance(opts, dict): + values = type(opts)() + for k, v in opts.items(): + if self.params.has_key(id + '|' + k): + values[k] = self.params[id + '|' + k] + else: + values[k] = False + choice = values + + if isinstance(opts, bool): + choice = True if choice == "True" else False + + return choice + + def execute(self): outputFormat = self.params['datatype'] if self.params.has_key('datatype') else 'html' if outputFormat in ['html','customhtml','hbfunction']: @@ -429,43 +455,47 @@ def execute(self): choice = self.params[id] if self.params.has_key(id) else '' opts = self.getOptionsBox(id, i, choice) - if opts == '__genome__': - id = 'dbkey' - choice = self.params[id] if self.params.has_key(id) else '' - -# if isinstance(opts, tuple): -# if opts[0] == '__hidden__': -# choice = unquote(choice) - - if opts == '__track__' or (isinstance(opts, tuple) and opts[0] == '__track__'): - tn = choice.split(':') - GalaxyInterface.cleanUpTrackName(tn) - choice = ':'.join(tn) - - if opts == '__genomes__' or (isinstance(opts, tuple) and opts[0] == '__multihistory__'): - values = {} - for key in self.params.keys(): - if key.startswith(id + '|'): - values[key.split('|')[1]] = self.params[key] - choice = OrderedDict(sorted(values.items(), \ - key=lambda t: int(t[0]) if opts[0] == '__multihistory__' else t[0])) - - if isinstance(opts, dict): - values = type(opts)() - for k,v in opts.items(): - if self.params.has_key(id + '|' + k): - values[k] = self.params[id + '|' + k] - else: - values[k] = False - choice = values - if isinstance(opts, bool): - choice = True if choice == "True" else False + choice = self.decodeChoice(opts, id, choice) + +# if opts == '__genome__': +# id = 'dbkey' +# choice = str(self.params[id]) if self.params.has_key(id) else '' +# +# # if isinstance(opts, tuple): +# # if opts[0] == '__hidden__': +# # choice = unquote(choice) +# +# if opts == '__track__' or (isinstance(opts, tuple) and opts[0] == '__track__'): +# #tn = choice.split(':') +# #GalaxyInterface.cleanUpTrackName(tn) +# #choice = ':'.join(tn) +# choice = self.decodeChoiceForTrack(choice) +# +# if opts == '__genomes__' or (isinstance(opts, tuple) and opts[0] == '__multihistory__'): +# values = {} +# for key in self.params.keys(): +# if key.startswith(id + '|'): +# values[key.split('|')[1]] = self.params[key] +# choice = OrderedDict(sorted(values.items(), \ +# key=lambda t: int(t[0]) if opts[0] == '__multihistory__' else t[0])) +# +# if isinstance(opts, dict): +# values = type(opts)() +# for k,v in opts.items(): +# if self.params.has_key(id + '|' + k): +# values[k] = self.params[id + '|' + k] +# else: +# values[k] = False +# choice = values +# +# if isinstance(opts, bool): +# choice = True if choice == "True" else False self.inputValues.append(choice) - if self.params.has_key('Track_state'): - self.inputValues.append(unquote(self.params['Track_state'])) + # if self.params.has_key('Track_state'): + # self.inputValues.append(unquote(self.params['Track_state'])) ChoiceTuple = namedtuple('ChoiceTuple', self.inputIds) choices = ChoiceTuple._make(self.inputValues) @@ -482,7 +512,7 @@ def execute(self): - +

Toggle debug

             ''' % {'prefix': URL_PREFIX}
         #    print '
Corresponding batch run line:\n', '$Tool[%s](%s)
' % (self.toolId, batchargs) @@ -514,12 +544,13 @@ def execute(self): print '''
- + ''' + def _executeTool(self, toolClassName, choices, galaxyFn, username): self._monkeyPatchAttr('extraGalaxyFn', self.extraGalaxyFn) self._monkeyPatchAttr('runParams', self.json_params) @@ -625,12 +656,6 @@ def validate(self): def isValid(self): return True if self.errorMessage is None else False - def getBatchLine(self): - if len(self.subClasses) == 0 and self.prototype.isBatchTool(): - self.batchline = '$Tool[%s](%s)' % (self.toolId, '|'.join([repr(c) for c in self.choices])) - return self.batchline - return None - def hasErrorMessage(self): return False if self.errorMessage in [None, ''] else True @@ -639,6 +664,10 @@ def hasErrorMessage(self): # return self.prototype.validateAndReturnErrors(self.inputValues) + def getInputValueForTrack(self, id, name): + return None + + def getController(transaction = None, job = None): #from gold.util.Profiler import Profiler #prof = Profiler() @@ -646,6 +675,4 @@ def getController(transaction = None, job = None): control = GenericToolController(transaction, job) #prof.stop() #prof.printStats() - return control - #return GenericToolController(transaction, job) diff --git a/lib/proto/hyper_gui.py b/lib/proto/hyper_gui.py index a92b251999f6..9c673af9af63 100644 --- a/lib/proto/hyper_gui.py +++ b/lib/proto/hyper_gui.py @@ -219,228 +219,6 @@ def setSessionParam(self, param, value): self.trans.sa_session.flush() -trackListCache = {} - -class TrackWrapper: - has_subtrack = False - def __init__(self, name, api, preTracks, galaxy, datasets, genome='', ucscTracks = True): - params = galaxy.params - self.api = api - self.galaxy = galaxy - self.datasets = datasets - self.nameMain = name - self.nameFile = self.nameMain + 'file' - self.nameState = self.nameMain + '_state' -# self.nameRecent = self.nameMain + '_recent' - self.state = params.get(self.nameState) - if self.state != None: - self.state = unquote(self.state) - -# if params.has_key(name) and (not params.has_key(self.nameLevel(0)) or params.get(self.nameRecent)): - if params.has_key(self.nameMain) and (not params.has_key(self.nameLevel(0))): - parts = params[self.nameMain].split(':') - if len(parts) == 4 and parts[0] == 'galaxy': - params[self.nameFile] = params[self.nameMain] -# dataset_id = int(parts[2].split('/dataset_')[-1].split('.dat')[0]) if not parts[2].isdigit() else int(parts[2]) -# params[self.nameFile] = ','.join([parts[0],str(dataset_id),parts[1],parts[3]]) - self.setValueLevel(0, parts[0]) - else: - for i in range(len(parts)): - self.setValueLevel(i, parts[i]) - - #if self.valueLevel(0) == '__recent_tracks' and params.get(self.nameRecent): - # #raise Exception(params[self.nameRecent]) - # parts = params[self.nameRecent].split(':') - # for i in range(len(parts)): - # self.setValueLevel(i, parts[i]) - - - self.preTracks = [] - self.extraTracks = [] -# self.recentTracks = [] - - if len(datasets) > 0: - self.extraTracks.append(('-- From history (bed, wig, ...) --', 'galaxy', False)) - self.extraTracks += preTracks - -# if self.galaxy.hasSessionParam('recent_tracks'): -# self.extraTracks.append(('-- Recently selected tracks --', '__recent_tracks', False)) -# self.recentTracks = self.galaxy.getSessionParam('recent_tracks') - - self.main = self.valueLevel(0) - self.file = params.get(self.nameFile) - if len(self.datasets) > 0: - if not self.file: - self.file = self.galaxy.makeHistoryOption(self.datasets[0])[1] - else: - self._checkHistoryFile() - - self.tracks = [] - self.ucscTracks = ucscTracks - self.genome = genome - self.numLevels = len(self.asList()) - self.valid = True - - - def _checkHistoryFile(self): - did = self.galaxy.getHistoryOptionId(self.file) - for dataset in self.datasets: - if dataset.dataset_id == did: - self.file = self.galaxy.makeHistoryOption(dataset)[1] - break - - def optionsFromHistory(self, sel = None, exts = None): - html = '' - for dataset in self.datasets: - if exts == None or dataset.extension in exts: - option = self.galaxy.makeHistoryOption(dataset, sel) - html += option[0] - return html - - def getState(self, q = True): - return quote(self.state) if self.state and q else self.state if self.state else '' - - def fetchTracks(self): - for i in range(0, self.numLevels + 1): - self.getTracksForLevel(i) - self.numLevels = len(self.tracks) - - def hasSubtrack(self): - #sub = self.valueLevel(self.numLevels - 1) - #print self.has_subtrack, sub, len(self.tracks), self.numLevels, self.valueLevel(self.numLevels - 1) - ldef = len(self.definition()) - if len(self.tracks) > ldef: - if len(self.tracks[ldef]) > 0: - return True - return False - - def nameLevel(self, level): - return self.nameMain + '_' + str(level) - - def valueLevel(self, level): - val = self.galaxy.params.get(self.nameLevel(level)) - if val == '-': - return None - return val - - def setValueLevel(self, level, val): - self.galaxy.params[self.nameLevel(level)] = val - - def asList(self): - vals = [] - for i in range(0, 10): - val = self.valueLevel(i) - if val != None and val != '-': - vals.append(val) - else: - break - return vals - - def asString(self): - vals = self.definition(False, False) - return ':'.join(vals) - - def selected(self): - #self.hasSubtrack() - #print len(self.tracks[len(self.definition())]), self.hasSubtrack() -# if self.valueLevel(0) == '__recent_tracks': -# return False - if (len(self.definition()) >= 1 and not self.hasSubtrack()) or self.valueLevel(0) == '': - #if self.galaxy.hasSessionParam('recent_tracks'): - # recent = self.galaxy.getSessionParam('recent_tracks') - # if not isinstance(recent, list): - # recent = [] - #else: - # recent = [] - #if not self.asString() in recent: - # recent.append(self.asString()) - #if len(recent) > 5: - # recent = recent[len(recent)-5:] - #self.galaxy.setSessionParam('recent_tracks', recent) - self.valid = self.api.trackValid(self.genome, self.definition()) - if self.valid == True: - return True - return False - - def definition(self, unquotehistoryelementname=True, use_path=True): - #if self._definition: - # return self._definition - arr = [self.main] - if self.main == 'galaxy' and self.file: - f = self.file.split(':') - if not use_path: - path = self.galaxy.encode_id(f[2]) if len(f[2]) < 16 and f[2].isdigit() else f[2] - else: - path = self.galaxy.getDataFilePath(f[2]) - #print path - arr.append(str(f[1])) - arr.append(str(path)) - if unquotehistoryelementname: - arr.append(str(unquote(f[3]))) - else: - arr.append(str(f[3])) - elif self.valueLevel(0) == '': - arr = [] - else: - arr = self.asList() - return arr - - def getTracksForLevel(self, level): - if not self.genome: - return [] - if level < len(self.tracks): - return self.tracks[level] - self.has_subtrack = False - tracks = None - if level == 0: - tracks = self.mainTracks() - val = self.valueLevel(0) - if val != None: - ok = False - for t in tracks: - if val == t[1]: - ok = True - if not ok: - self.setValueLevel(0, None) - else: - trk = [] - for i in range(0, level): - val = self.valueLevel(i) - if val != None and val != 'galaxy': - trk.append(val) - else: - trk = [] - break - if len(trk) > 0 and val not in ['', '-']: - try: - tracks, self.state = self.api.getSubTrackNames(self.genome, trk, False, self.galaxy.getUserName(), self.state) - except OSError, e: - #print e, level, i - self.setValueLevel(i, None) - self.has_subtrack = False - if e.errno != 2: - raise e - if tracks and len(tracks) > 0: - self.has_subtrack = True - #tracks = None - #if tracks and len(tracks) == 1 and not tracks[0][1]: - # tracks = None - if tracks == None: - self.setValueLevel(level, None) - else: - self.tracks.append(tracks) - return tracks - - def mainTracks(self): - tracks = self.api.getMainTrackNames(self.genome, self.preTracks, self.extraTracks, self.galaxy.getUserName(), self.ucscTracks) -# for i in range(len(tracks)): -# if tracks[i][1] == self.main: -# tracks[i] = (tracks[i][0], tracks[i][1], True) - return tracks - - #return self.api.getMainTrackNames(self.preTracks, [('-- From history (bed-file) --', 'bed', False), ('-- From history (wig-file) --', 'wig', False)]) - - def selected(opt, sel): return ' selected="selected" ' if opt == sel else '' @@ -516,19 +294,3 @@ def getScript(self): self.script += "$('#%s').change(function (){%s})" % (self.attrs['id'], self.onChange) self.script += '}); \n\n' return self.script - - -class TrackSelectElement(SelectElement): - def __init__(self, track, level): - SelectElement.__init__(self) - self.id = track.nameLevel(level) - self.attrs['id'] = self.id - self.attrs['name'] = self.id - self.options = [('----- Select ----- ', '-', False)] - self.selectedOption = track.valueLevel(level) - self.onChange = "try{$('#" + track.nameLevel(level + 1) + "').val('')} catch(e){} " + self.onChange - opts = track.getTracksForLevel(level) - if opts: - self.options.extend(opts) - - diff --git a/lib/proto/tools/ExploreToolsTool.xml b/lib/proto/tools/ExploreToolsTool.xml index 8d23b2894c68..005e9a3a502b 100644 --- a/lib/proto/tools/ExploreToolsTool.xml +++ b/lib/proto/tools/ExploreToolsTool.xml @@ -1 +1 @@ - + diff --git a/lib/proto/tools/GeneralGuiTool.py b/lib/proto/tools/GeneralGuiTool.py index 5d7e40984a28..9eb91e63fbe8 100644 --- a/lib/proto/tools/GeneralGuiTool.py +++ b/lib/proto/tools/GeneralGuiTool.py @@ -1,11 +1,18 @@ import os from collections import namedtuple +from urllib import quote + + +class HistElement(object): + def __init__(self, name, format, label=None, hidden=False): + self.name = name + self.format = format + self.label = label + self.hidden = hidden + + +BoxGroup = namedtuple('BoxGroup', ['label', 'first', 'last']) -#from proto.config.Config import DATA_FILES_PATH -#from gold.application.LogSetup import logMessage -#from gold.util.CustomExceptions import Warning -#from quick.util.CommonFunctions import getUniqueWebPath, getRelativeUrlFromWebPath, extractIdFromGalaxyFn -#from quick.application.SignatureDevianceLogging import takes,returns class GeneralGuiTool(object): def __init__(self, toolId=None): @@ -36,11 +43,6 @@ def isRedirectTool(choices=None): def isHistoryTool(): return True - @classmethod - def isBatchTool(cls): - return False -# return cls.isHistoryTool() - @staticmethod def isDynamic(): return True @@ -69,41 +71,6 @@ def getToolIllustration(): def getFullExampleURL(): return None - @classmethod - def doTestsOnTool(cls, galaxyFn, title, label): - from quick.application.GalaxyInterface import GalaxyInterface - from collections import OrderedDict - import sys - - if hasattr(cls, 'getTests'): - galaxy_ext = None - testRunList = cls.getTests() - for indx, tRun in enumerate(testRunList): - choices = tRun.split('(',1)[1].rsplit(')',1)[0].split('|') - choices = [eval(v) for v in choices] - if not galaxy_ext: - galaxy_ext = cls.getOutputFormat(choices) - output_filename = cls.makeHistElement(galaxyExt=galaxy_ext, title=title+str(indx), label=label+str(indx)) - sys.stdout = open(output_filename, "w", 0) - cls.execute(choices, output_filename) - sys.stdout = open(galaxyFn, "a", 0) - else: - print open(galaxyFn, "a").write('No tests specified for %s' % cls.__name__) - - - @classmethod - def getTests(cls): - import shelve - SHELVE_FN = DATA_FILES_PATH + os.sep + 'tests' + os.sep + '%s.shelve'%cls.toolId - if os.path.isfile(SHELVE_FN): - - testDict = shelve.open(SHELVE_FN) - resDict = dict() - for k, v in testDict.items(): - resDict[k] = cls.convertHttpParamsStr(v) - return resDict - return None - @staticmethod def isDebugMode(): return False @@ -114,12 +81,6 @@ def getOutputFormat(choices=None): @staticmethod def validateAndReturnErrors(choices): - ''' - Should validate the selected input parameters. If the parameters are not valid, - an error text explaining the problem should be returned. The GUI then shows this text - to the user (if not empty) and greys out the execute button (even if the text is empty). - If all parameters are valid, the method should return None, which enables the execute button. - ''' return None # Convenience methods @@ -145,192 +106,6 @@ def getOptionBoxNames(cls): return [i[0] for i in labels] #return [labels[i][0] for i in inputOrder] - @classmethod - def formatTests(cls, choicesFormType, testRunList): - labels = cls.getOptionBoxNames() - if len(labels) != len(choicesFormType): - logMessage('labels and choicesFormType are different:(labels=%i, choicesFormType=%i)' % (len(labels), len(choicesFormType))) - return (testRunList, zip(labels, choicesFormType)) - - #@classmethod - #def _getPathAndUrlForFile(cls, galaxyFn, relFn): - # ''' - # Gets a disk path and a URL for storing a run-specific file. - # galaxyFn is connected to the resulting history item in Galaxy, - # and is used to determine a unique disk path for this specific run. - # relFn is a relative file name (i.e. only name, not full path) that one - # wants a full disk path for, as well as a URL referring to the file. - # ''' - # fullFn = cls._getDiskPathForFiles(galaxyFn) + os.sep + relFn - # url = cls._getBaseUrlForFiles(fullFn) - # return fullFn, url - # - #@staticmethod - #def _getDiskPathForFiles(galaxyFn): - # galaxyId = extractIdFromGalaxyFn(galaxyFn) - # return getUniqueWebPath(galaxyId) - # - #@staticmethod - #def _getBaseUrlForFiles(diskPath): - # return getRelativeUrlFromWebPath(diskPath) - - @staticmethod - def _getGenomeChoice(choices, genomeChoiceIndex): - if genomeChoiceIndex is None: - genome = None - else: - if type(genomeChoiceIndex) == int: - genome = choices[genomeChoiceIndex] - else: - genome = getattr(choices, genomeChoiceIndex) - - if genome in [None, '']: - return genome, 'Please select a genome build' - - return genome, None - - @staticmethod - def _getTrackChoice(choices, trackChoiceIndex): - if type(trackChoiceIndex) == int: - trackChoice = choices[trackChoiceIndex] - else: - trackChoice = getattr(choices, trackChoiceIndex) - - if trackChoice is None: - return trackChoice, 'Please select a track' - - trackName = trackChoice.split(':') - return trackName, None - - @staticmethod - def _checkTrack(choices, trackChoiceIndex=1, genomeChoiceIndex=0, filetype=None, validateFirstLine=True): - genome, errorStr = GeneralGuiTool._getGenomeChoice(choices, genomeChoiceIndex) - if errorStr: - return errorStr - - trackName, errorStr = GeneralGuiTool._getTrackChoice(choices, trackChoiceIndex) - if errorStr: - return errorStr - - from quick.application.ExternalTrackManager import ExternalTrackManager - if ExternalTrackManager.isGalaxyTrack(trackName): - errorStr = GeneralGuiTool._checkHistoryTrack(choices, trackChoiceIndex, genome, filetype, validateFirstLine) - if errorStr: - return errorStr - else: - if not GeneralGuiTool._isValidTrack(choices, trackChoiceIndex, genomeChoiceIndex): - return 'Please select a valid track' - - @staticmethod - def _isValidTrack(choices, tnChoiceIndex=1, genomeChoiceIndex=0): - from quick.application.GalaxyInterface import GalaxyInterface - from quick.application.ProcTrackOptions import ProcTrackOptions - - genome, errorStr = GeneralGuiTool._getGenomeChoice(choices, genomeChoiceIndex) - if errorStr or genome is None: - return False - - trackName, errorStr = GeneralGuiTool._getTrackChoice(choices, tnChoiceIndex) - if errorStr: - return False - - return ProcTrackOptions.isValidTrack(genome, trackName, True) or \ - GalaxyInterface.isNmerTrackName(genome, trackName) - - @staticmethod - def _checkHistoryTrack(choices, historyChoiceIndex, genome, filetype=None, validateFirstLine=True): - fileStr = filetype + ' file' if filetype else 'file' - - trackName, errorStr = GeneralGuiTool._getTrackChoice(choices, historyChoiceIndex) - if errorStr: - return 'Please select a ' + fileStr + ' from history.' - - if validateFirstLine: - return GeneralGuiTool._validateFirstLine(trackName, genome, fileStr) - - @staticmethod - def _validateFirstLine(galaxyTN, genome=None, fileStr='file'): - try: - from quick.application.ExternalTrackManager import ExternalTrackManager - from gold.origdata.GenomeElementSource import GenomeElementSource - - suffix = ExternalTrackManager.extractFileSuffixFromGalaxyTN(galaxyTN) - fn = ExternalTrackManager.extractFnFromGalaxyTN(galaxyTN) - - GenomeElementSource(fn, genome, suffix=suffix).parseFirstDataLine() - - except Exception, e: - return fileStr.capitalize() + ' invalid: ' + str(e) - - @staticmethod - def _getBasicTrackFormat(choices, tnChoiceIndex=1, genomeChoiceIndex=0): - genome = GeneralGuiTool._getGenomeChoice(choices, genomeChoiceIndex)[0] - tn = GeneralGuiTool._getTrackChoice(choices, tnChoiceIndex)[0] - - from quick.application.GalaxyInterface import GalaxyInterface - from gold.description.TrackInfo import TrackInfo - from quick.application.ExternalTrackManager import ExternalTrackManager - from gold.track.TrackFormat import TrackFormat - - if ExternalTrackManager.isGalaxyTrack(tn): - geSource = ExternalTrackManager.getGESourceFromGalaxyOrVirtualTN(tn, genome) - try: - tf = GeneralGuiTool._convertToBasicTrackFormat(TrackFormat.createInstanceFromGeSource(geSource).getFormatName()) - except Warning: - return genome, tn, '' - else: - if GalaxyInterface.isNmerTrackName(genome, tn): - tfName = 'Points' - else: - tfName = TrackInfo(genome, tn).trackFormatName - tf = GeneralGuiTool._convertToBasicTrackFormat(tfName) - return genome, tn, tf - - @staticmethod - def _getValueTypeName(choices, tnChoiceIndex=1, genomeChoiceIndex=0): - genome = GeneralGuiTool._getGenomeChoice(choices, genomeChoiceIndex)[0] - tn = GeneralGuiTool._getTrackChoice(choices, tnChoiceIndex)[0] - - from quick.application.GalaxyInterface import GalaxyInterface - from gold.description.TrackInfo import TrackInfo - from quick.application.ExternalTrackManager import ExternalTrackManager - from gold.track.TrackFormat import TrackFormat - - if ExternalTrackManager.isGalaxyTrack(tn): - geSource = ExternalTrackManager.getGESourceFromGalaxyOrVirtualTN(tn, genome) - valTypeName = TrackFormat.createInstanceFromGeSource(geSource).getValTypeName() - else: - if GalaxyInterface.isNmerTrackName(genome, tn): - valTypeName = '' - else: - valTypeName = TrackInfo(genome, tn).markType - return valTypeName.lower() - - #@staticmethod - #def _getBasicTrackFormatFromHistory(choices, tnChoiceIndex=1): - # from quick.application.ExternalTrackManager import ExternalTrackManager - # from gold.track.TrackFormat import TrackFormat - # genome = choices[0] - # tn = choices[tnChoiceIndex].split(':') - # geSource = ExternalTrackManager.getGESourceFromGalaxyOrVirtualTN(tn, genome) - # tf = GeneralGuiTool._convertToBasicTrackFormat(TrackFormat.createInstanceFromGeSource(geSource).getFormatName()) - # - # - # return genome, tn, tf - - - @staticmethod - def _convertToBasicTrackFormat(tfName): - tfName = tfName.lower() - - if tfName.startswith('linked '): - tfName = tfName[7:] - - tfName = tfName.replace('unmarked ','') - tfName = tfName.replace('marked','valued') - - return tfName - @classmethod def getNamedTuple(cls): names = cls.getInputBoxNames() @@ -351,7 +126,7 @@ def getNamedTuple(cls): @staticmethod def _exampleText(text): - from gold.result.HtmlCore import HtmlCore + from proto.HtmlCore import HtmlCore core = HtmlCore() core.styleInfoBegin(styleClass='debug', linesep=False) core.append(text.replace('\t','\\t')) @@ -360,7 +135,7 @@ def _exampleText(text): @classmethod def makeHistElement(cls, galaxyExt='html', title='new Dataset', label='Newly created dataset',): - import simplejson, glob + import json, glob #print 'im in makeHistElement' json_params = cls.runParams datasetId = json_params['output_data'][0]['dataset_id'] # dataset_id fra output_data @@ -374,7 +149,7 @@ def makeHistElement(cls, galaxyExt='html', title='new Dataset', label='Newly cr #print 'numFiles', numFiles outputFilename = os.path.join(newFilePath , 'primary_%i_%s_visible_%s' % ( hdaId, title, galaxyExt ) ) #print 'outputFilename', outputFilename - metadata_parameter_file.write( "%s\n" % simplejson.dumps( dict( type = 'dataset', #new_primary_ + metadata_parameter_file.write( "%s\n" % json.dumps( dict( type = 'dataset', #new_primary_ dataset_id = datasetId,#base_ ext = galaxyExt, #filename = outputFilename, @@ -383,6 +158,26 @@ def makeHistElement(cls, galaxyExt='html', title='new Dataset', label='Newly cr metadata_parameter_file.close() return outputFilename + @classmethod + def createGenericGuiToolURL(cls, tool_id, sub_class_name=None, tool_choices=None): + from proto.CommonFunctions import getToolPrototype + tool = getToolPrototype(tool_id) + base_url = '?mako=generictool&tool_id=' + tool_id + '&' + if sub_class_name and isinstance(tool, MultiGeneralGuiTool): + for subClass in tool.getSubToolClasses(): + if sub_class_name == subClass.__name__: + tool = subClass() + base_url += 'sub_class_id=' + quote(tool.getToolSelectionName()) + '&' + + #keys = tool.getNamedTuple()._fields + if not tool_choices: + args = [] + elif isinstance(tool_choices, dict): + args = [ '%s=%s' % (k,quote(v)) for k,v in tool_choices.items()] + elif isinstance(tool_choices, list): + args = [ '%s=%s' % ('box%d'%(i+1,), quote(tool_choices[i])) for i in range(0, len(tool_choices)) ] + + return base_url + '&'.join(args) class MultiGeneralGuiTool(GeneralGuiTool): @@ -409,15 +204,3 @@ def getInputBoxNames(): @staticmethod def useSubToolPrefix(): return False - - -class HistElement(object): - def __init__(self, name, format, label=None, hidden=False): - self.name = name - self.format = format - self.label = label - self.hidden = hidden - - -BoxGroup = namedtuple('BoxGroup', ['label','first','last']) - diff --git a/lib/proto/tools/GenerateToolsTool.xml b/lib/proto/tools/GenerateToolsTool.xml index 6b8cf557b6e4..c7b5ffc9bd19 100644 --- a/lib/proto/tools/GenerateToolsTool.xml +++ b/lib/proto/tools/GenerateToolsTool.xml @@ -1,4 +1,4 @@ + tool_type="proto_generic" proto_tool_module="proto.tools.ManageTools" proto_tool_class="GenerateToolsTool"> diff --git a/lib/proto/tools/InstallToolsTool.xml b/lib/proto/tools/InstallToolsTool.xml index 3e03dff5c809..d7725fbef785 100644 --- a/lib/proto/tools/InstallToolsTool.xml +++ b/lib/proto/tools/InstallToolsTool.xml @@ -1,4 +1,4 @@ + tool_type="proto_generic" proto_tool_module="proto.tools.ManageTools" proto_tool_class="InstallToolsTool"> diff --git a/lib/proto/tools/ManageTools.py b/lib/proto/tools/ManageTools.py index 5d3443de710a..1fb79c2e909b 100644 --- a/lib/proto/tools/ManageTools.py +++ b/lib/proto/tools/ManageTools.py @@ -20,7 +20,7 @@ class GalaxyToolConfig: tool_xml_template = ''' + tool_type="proto_generic" proto_tool_module="%s" proto_tool_class="%s"> %s \n''' @@ -62,7 +62,7 @@ def getProtoToolList(except_class_names=[]): tools = {} tool_classes = [] pys = [] - for d in os.walk(PROTO_TOOL_DIR): + for d in os.walk(PROTO_TOOL_DIR, followlinks=True): if d[0].find('.svn') == -1: pys += [os.path.join(d[0], f) for f in d[2] if f.endswith('.py') and not any(f.startswith(x) for x in ['.', '#'])] @@ -74,8 +74,8 @@ def getProtoToolList(except_class_names=[]): if m: class_name = m.group(1) if class_name not in except_class_names: - module_name = os.path.splitext(os.path.relpath(fn, SOURCE_CODE_BASE_DIR))[0].replace(os.path.sep, '.') - #print module_name + module_name = os.path.splitext(os.path.relpath(os.path.abspath(fn), SOURCE_CODE_BASE_DIR))[0].replace(os.path.sep, '.') + # print module_name try: module = import_module(module_name) prototype_cls = getattr(module, class_name) @@ -87,6 +87,7 @@ def getProtoToolList(except_class_names=[]): else: toolSelectionName = '.'.join(toolModule) + # print (fn, m.group(2), prototype_cls, module_name) tools[toolSelectionName] = (fn, m.group(2), prototype_cls, module_name) tool_classes.append(prototype_cls) except Exception as e: diff --git a/templates/proto/common.js b/templates/proto/common.js index 3f37601d1988..2d024ee55c31 100644 --- a/templates/proto/common.js +++ b/templates/proto/common.js @@ -1,448 +1,26 @@ -// Copyright (C) 2009, Geir Kjetil Sandve, Sveinung Gundersen and Morten Johansen -// This file is part of The Genomic HyperBrowser. -// -// The Genomic HyperBrowser is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The Genomic HyperBrowser is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with The Genomic HyperBrowser. If not, see . - - function checkForException(data) { - if (data.exception != undefined) { - if (data.exception) { - alert(data.exception); - return true; - } - } - return false; - } - - var invalid = false; - var do_submit = false; - - function setJobInfo(data) { - var job_info = $('#job_info'); - if (job_info && data.job_info) { - job_info.val(escape(data.job_info)); - $('#job_name').val(escape(data.job_name)); - } - } - - function validate(f) { - //alert('validate'); - //updateRunDescription(f); - invalid = disableSubmit(); - //invalid = true; - $('#validating').show(); - $.ajax({ - type:'post', - dataType: 'json', - url: '?mako=ajax&ajax=validate', - data: $(f).serialize(), - success: function (data) { - checkForException(data); - if (data.valid != 'OK') { - invalid = true; - //$('#start').attr('disabled', true); - $('#status').html(data.valid); - $('#status').show(); - //alert('validate:'+data); - if (do_submit) { - do_submit = false; - } - } else { - updateRunDescription(f); - setJobInfo(data) - invalid = false; - $(f).attr('action', '${h.url_for("/tool_runner")}') - $('#start').attr('disabled', false); - $('#status').hide(); - $('#status').empty(); - if (do_submit) { - f.submit(); - } - } - restoreSubmit(invalid); - $('#validating').hide(); - }, - error: function (XMLHttpRequest, textStatus, errorThrown) { - //alert(textStatus + ': ' + errorThrown); - } - }); - } - - function setConfigChoices(f) { - var href = '?mako=ajax&ajax=config'; - //alert(href); - $.ajax({ - type: 'post', - url: href, - data: $(f).serialize(), - dataType: 'json', - success: function (data) { - //alert(data.stats); - checkForException(data); - - $('#stats').val(data.stats); - $('#_stats_text').html(data.stats_text); - } - }); - updateRunDescription(f); - } - - function getInfo(form, link, about) { - var elem = $('#info_'+about); - if (!$(link).hasClass('hideInfo')) { - $('#show_info_'+about).val('1'); - var url = '?mako=ajax&ajax=trackinfo&about=' + escape($('#'+about).val()); - - $.ajax({ - type: 'post', - dataType: 'json', - url: url, - data: $(form).serialize(), - success: function (data) { - checkForException(data); - //alert(data); - elem.show(); - //elem.html(data); - $(link).addClass('hideInfo'); - }, - error: function (XMLHttpRequest, textStatus, errorThrown) { - //alert(textStatus + ': ' + errorThrown); - } - }); - - } else { - $('#show_info_'+about).val('0'); - elem.hide(); - $(link).removeClass('hideInfo'); - } - } - - function getGenomeInfo(form, link, genome) { - var elem = $('#genome_info'); - if (!$(link).hasClass('hideInfo')) { - $('#show_genome_info').val('1'); - var url = '?mako=ajax&ajax=genomeinfo&about=' + escape(genome); - $.ajax({ - type: 'post', - dataType: 'json', - url: url, - data: $(form).serialize(), - success: function (data) { - checkForException(data); - //alert(data); - elem.show(); - elem.html(data); - $(link).addClass('hideInfo'); - }, - error: function (XMLHttpRequest, textStatus, errorThrown) { - //alert(textStatus + ': ' + errorThrown); - } - }); - } else { - $('#show_genome_info').val('0'); - elem.hide(); - $(link).removeClass('hideInfo'); - } - } - - function getHelp(about) { - var elem = $('#help_'+about); - elem.empty(); - if (elem.is(':visible')) { - elem.hide(); - return; - } - var form = $('form'); - var url = '?mako=ajax&ajax=help&about=' + escape(about); - $.ajax({ - type: 'post', - dataType: 'json', - url: url, - data: form.serialize(), - success: function (data) { - checkForException(data); - elem.html(data); - elem.show(); - //$(link).addClass('hideInfo'); - }, - error: function (XMLHttpRequest, textStatus, errorThrown) { - //alert(textStatus + ': ' + errorThrown); - } - }); - } - - function hideHelp(about) { - var elem = $('#help_'+about); - elem.hide(); - } - - function getRunDescription(form, link, elem) { - if (!$(link).hasClass('hideInfo')) { - $('#showrundescription').val('1'); - var url = '?mako=ajax&ajax=rundescription&'; - $('#gettingrundescription').show(); - $.ajax({ - type: 'post', - dataType: 'json', - url: url, - data: $(form).serialize(), - success: function (data) { - checkForException(data); - //alert(data); - $(elem).show(); - $(elem).html(data); - $(link).addClass('hideInfo'); - $('#gettingrundescription').hide(); - }, - error: function (XMLHttpRequest, textStatus, errorThrown) { - //alert(textStatus + ': ' + errorThrown); - } - }); - } else { - $('#showrundescription').val('0'); - $(elem).hide(); - $(link).removeClass('hideInfo'); - } - } - - function disableSubmit() { - var old = $('#start').attr('disabled'); - if (!old) { - $('#start').attr('disabled', true); - } - return old; - } - - function restoreSubmit(old) { - $('#start').attr('disabled', old); - } - - function updateRunDescription(form) { - if ($('#showrundescription').val() != '1') return; - var elem = $('#rundescription'); - var url = '?mako=ajax&ajax=rundescription'; - //var submit = disableSubmit(); - $('#gettingrundescription').show(); - $.ajax({ - type: 'post', - dataType: 'json', - url: url, - data: $(form).serialize(), - success: function (data) { - checkForException(data); - //alert(data); - //$(elem).show(); - $(elem).html(data); - //$(link).addClass('hideInfo'); - //restoreSubmit(invalid); - $('#gettingrundescription').hide(); - }, - error: function (XMLHttpRequest, textStatus, errorThrown) { - //alert(textStatus + ': ' + errorThrown); - } - }); - } - - - function methodOnChange(self,event,updRunDescr) { - var value = $(self).val(); - $('#pnl__brs__').hide(); - $('#pnl__chrs__').hide(); - $('#pnl__chrArms__').hide(); - $('#pnl__chrBands__').hide(); - $('#pnl__genes__').hide(); - $('#pnl__encode__').hide(); - switch (value) { - case 'auto': - $('#pnlRegion').show(); - $('#pnlBinSize').show(); - $('#pnlUserRegion').hide(); - break; - case 'binfile': - $('#pnlRegion').hide(); - $('#pnlBinSize').hide(); - $('#pnlUserRegion').show(); - break; - case '__brs__': - case '__chrs__': - case '__chrArms__': - case '__chrBands__': - case '__genes__': - case '__encode__': - $('#pnlRegion').hide(); - $('#pnlBinSize').hide(); - $('#pnlUserRegion').hide(); - $('#pnl'+value).show(); - break; - default: - $('#pnlRegion').hide(); - $('#pnlBinSize').hide(); - $('#pnlUserRegion').hide(); - } - if (updRunDescr) { - validate(self.form); - //updateRunDescription(self.form); - } - } - - function optionOnClick(ev) { - var opt = this; - var id = '#' + opt.rel; - $(id).val(opt.rev); - $(id+'_text').val($(opt).text()); - $(id+'_display').text($(opt).text()); - $(id+'_options').hide(); - var onchange = $(id).attr('on_select'); - if (onchange) { - //alert(onchange); - eval(onchange); - } - with(document.forms['form']){action='';submit()} - return false; - } - - function optionsToggle(id) { - var opts = $('#'+id+'_options'); - opts.toggle(); -/* - $('select').each(function (i) { - var sel = $(this); - if (sel.offset().top >= opts.offset().top) { - sel.toggleClass('invisible'); - } - }); -*/ - } - - function checkNmerReload(event, element) { - if (!element.value || element.value == '') { - //element.value = ''; - //alert('Invalid nmer pattern'); - return; - } - if (event.type == 'blur' || event.keyCode == 13) { - //if (! /[acgt]+/i.test(element.value)) { - // alert('Invalid nmer pattern'); - // return; - //} - if (element.oldValue != element.value) { - element.oldValue = element.value; - element.form.action = '?'; - element.form.submit(); - } - } - element.oldValue = element.value; - } - - function refreshOnChange(event, element) { - if (!element.value || element.value == '') { - return; - } - disablePage(); - if (event.type == 'focus') { - //element.submitState = disableSubmit(); - element.oldValue = element.value; - //$(element.form).filter('input').attr('disabled', true); - } - if (event.type == 'blur') { - if (element.oldValue != element.value) { - element.oldValue = element.value; - element.form.action = '?'; - element.form.submit(); - } - //restoreSubmit(element.submitState); - } - } - - function reloadForm(form, element) { - disablePage(); - if (!form) form = document.forms[0]; - //if (form.focused_element && element) - // form.focused_element.value = element.id; - form.action = '?'; - if (element && element.name) { - form.action += '#'+element.name; - } - form.submit(); - } - - function resetAll() { - if ($('#stats')) $('#stats').val(''); - if ($('#_stats')) $('#_stats').val(''); - if ($('#track1')) $('#track1').val(''); - if ($('#track2')) $('#track2').val(''); - if ($('#track3')) $('#track3').val(''); - if ($('#track1_0')) $('#track1_0').val(''); - if ($('#track2_0')) $('#track2_0').val(''); - if ($('#track3_0')) $('#track3_0').val(''); - } - - function setTrackToRecent(trackname, recent, form) { - var rtrk = $(recent).val(); - $('#'+trackname).val(rtrk); - $('#'+trackname+'_0').val(rtrk.split(':')[0]); - form.action='?'; - form.submit() - } - - function disablePage() { - $('#__disabled').height($(document).height()).show(); - } - function enablePage() { - $('#__disabled').height(0).hide(); - } - - function onSubmit(self) { - disablePage(); - do_submit = true; - validate(document.forms['form']); - return false; - } - - function appendValueFromInputToInput(from, to) { - if (to.value) { - to.value += ':' + from.value; - } else { - to.value = from.value; - } - } - - function updateMultiChoice(element, name, key, history){ - var multi = document.getElementById(name); - var curVal = JSON.parse(multi.value); - curVal[key] = element.checked ? (history ? element.value : true) : (history ? null : false); - multi.value = JSON.stringify(curVal); - } - - - function main() { - //$('.option').attr('href', 'javascript:;') - $('.option').click(optionOnClick); - - //$('#method').change(methodOnChange); - $('#method').change(); - $('#form').submit(onSubmit); - -/* - if (window.location.hash) { - $(window.location.hash).focus(); - } - if ($('#focused_element')) { - var id = $('#focused_element').val(); - if (id) - $('#' + id).focus(); - } -*/ - } - - $(document).ready(main); +function reloadForm(form, element) { +disablePage(); + if (!form) form = document.forms[0]; + //if (form.focused_element && element) + // form.focused_element.value = element.id; + form.action = '?'; + if (element && element.name) { + form.action += '#'+element.name; + } + form.submit(); +} + +function disablePage() { + $('#__disabled').height($(document).height()).show(); +} + +function enablePage() { + $('#__disabled').height(0).hide(); +} + +function updateMultiChoice(element, name, key, history){ + var multi = document.getElementById(name); + var curVal = JSON.parse(multi.value); + curVal[key] = element.checked ? (history ? element.value : true) : (history ? null : false); + multi.value = JSON.stringify(curVal); +} diff --git a/templates/proto/functions.mako b/templates/proto/functions.mako index 1d1f2b60c801..9a524ed20b2d 100644 --- a/templates/proto/functions.mako +++ b/templates/proto/functions.mako @@ -105,159 +105,6 @@ import proto.hyper_gui as gui -<%def name="trackChooser(track, i, params, do_reset=True, readonly=False)"> - <% - galaxy = gui.GalaxyWrapper(trans) - genome = params.get('dbkey') - %> -
${track.legend} - - %if not readonly: - <% - typeElement = gui.TrackSelectElement(track, 0) - - # reset stats parameter when changing track - if do_reset: - typeElement.onChange = "\nif ($('#_stats')){$('#_stats').val('');$('#stats').val('');}\n" + typeElement.onChange - - #typeElement.onChange = "\nif ($('#%(id)s')){\n $('#%(id)s').val( $('%(id)s').val() + ':' + $(this).val() ); }\n" % {'id': track.nameMain} + typeElement.onChange - typeElement.onChange = "appendValueFromInputToInput(this, '#" + track.nameMain + "'); " + typeElement.onChange - %> - ${typeElement.getHTML()} ${typeElement.getScript()} - - %if track.valueLevel(0) == 'galaxy': - - - %else: - %for j in range(1, 10): - %if track.getTracksForLevel(j): - %if track.valueLevel(j - 1) == 'K-mers': -
|_ - -
- %else: - <% - levelElement = gui.TrackSelectElement(track, j) - #levelElement.onChange = "\nif ($('#%(id)s')){\n $('#%(id)s').val( $('%(id)s').val() + ':' + $(this).val() ); }\n" % {'id': track.nameMain} + levelElement.onChange - levelElement.onChange = "appendValueFromInputToInput(this, '#" + track.nameMain + "'); " + levelElement.onChange - %> -
|_ ${levelElement.getHTML()} ${levelElement.getScript()}
- %endif - %endif - %endfor - %endif - - %endif - - %if track.main and track.valueLevel(0) != 'galaxy' and track.selected(): - <% - sti_val = params.get('show_info_'+track.nameMain, '0') - if sti_val == '1': - sti_class = 'hideInfo' - sti_display = '' - else: - sti_class = 'showInfo' - sti_display = 'display:none' - - %> - Track information -
${hyper.getTrackInfo(genome, track.definition())}
- - - %endif - - - - - - %if i == 0: - ${self.help(track.nameMain, 'What is a genomic track?')} - %else: -
 
- %endif - - %if track.valid != True: -
${track.valid}
- %endif - -
- - - - - -<%def name="trackChooserTest(track, i, params, do_reset=True)"> - <% - galaxy = gui.GalaxyWrapper(trans) - genome = params.get('dbkey') - %> -
${track.legend} - - <% - typeElement = gui.TrackSelectElement(track, 0) - - # reset stats parameter when changing track - if do_reset: - typeElement.onChange = "if ($('#_stats')){$('#_stats').val('');$('#stats').val('');}" + typeElement.onChange - %> - ${typeElement.getHTML()} ${typeElement.getScript()} - - %if track.valueLevel(0) == 'galaxy': - - - %else: - %for j in range(1, 10): - %if track.getTracksForLevel(j): - %if track.valueLevel(j - 1) == 'K-mers': -
|_ - -
- %else: - <% - levelElement = gui.TrackSelectElement(track, j) - %> -
|_ ${levelElement.getHTML()} ${levelElement.getScript()}
- %endif - %endif - %endfor - %endif - - - %if track.main and track.valueLevel(0) != 'galaxy' and track.selected(): - <% - sti_val = params.get('show_info_'+track.nameMain, '0') - if sti_val == '1': - sti_class = 'hideInfo' - sti_display = '' - else: - sti_class = 'showInfo' - sti_display = 'display:none' - - %> - Track information -
${hyper.getTrackInfo(genome, track.definition())}
- - %endif - - - - ${self.help(track.nameMain, 'What is a genomic track?')} - - - -
- - - - - - - - <%def name="help(what, text)">
${text} @@ -266,56 +113,6 @@ import proto.hyper_gui as gui -<%def name="__trackChooser2(track, i, params)"> - <% - genome = params.get('dbkey') - %> -
${track.legend} - - <% - typeElement = gui.TrackSelectElement(track, 0) - # reset stats parameter when changing track - typeElement.onChange = "if ($('#_stats')){$('#_stats').val('');$('#stats').val('');}" + typeElement.onChange - %> - ${typeElement.getHTML()} ${typeElement.getScript()} - - %if track.valueLevel(0) == 'galaxy': - - - %else: - %for j in range(1, 10): - %if track.getTracksForLevel(j): - <% - levelElement = gui.TrackSelectElement(track, j) - #print levelElement.getHTML() - %> -
|_ ${levelElement.getHTML()} ${levelElement.getScript()}
- %endif - %endfor - %endif - %if track.main and track.valueLevel(0) != 'galaxy': - <% - sti_val = params.get('showtrackinfo'+str(i), '0') - if sti_val == '1': - sti_class = 'hideInfo' - sti_display = '' - else: - sti_class = 'showInfo' - sti_display = 'display:none' - %> - Track information -
${hyper.getTrackInfo(genome, track.definition())}
- - %endif - - - -
- - - <%def name="genomeChooser(control, genomeElement = None, genome = None, id='dbkey')"> <% if genomeElement == None: @@ -331,33 +128,16 @@ import proto.hyper_gui as gui %if id != 'dbkey': %endif - <% - sti_val = control.params.get('show_genome_info', '0') - if sti_val == '1': - sti_class = 'hideInfo' - sti_display = '' - else: - sti_class = 'showInfo' - sti_display = 'display:none' - genomeInfo = hyper.getGenomeInfo(genome) if hyper else None - %> - %if genomeInfo: - Genome info -
${genomeInfo}
- - %elif hyper and genome: - %if genome != '?': -
- ${genome} is not yet supported by HyperBrowser.
- %else: -
- %endif - Please select a genome build from the list above to continue. -
- %endif + ${self.genomeInfo(control.params, genome)} + + +<%def name="genomeInfo(params, genome)" /> + + <%def name="accessDenied()">
This functionality is only available to specific users.
Contact us if you need access.
+ diff --git a/templates/proto/generictool.mako b/templates/proto/generictool.mako index cb2269e78685..ec9e5cf6c09b 100644 --- a/templates/proto/generictool.mako +++ b/templates/proto/generictool.mako @@ -41,32 +41,17 @@ else: %if control.doRedirect(): %endif + ${self.includeScripts()} + ${h.js('proto/sorttable')} + + +<%def name="includeScripts()">\ - ${h.js('proto/sorttable')} -%if control.userHasFullAccess(): - -
- - - - - - - - - - - - - %if len(control.subClasses) > 0: - ${functions.select('sub_class_id', control.subClasses.keys(), control.subClassId, control.subToolSelectionTitle)} - %endif - - %for i in control.inputOrder: +<%def name="showOptionsBox(control, params, i)"> %if control.inputTypes[i] == 'select': ${functions.select(control.inputIds[i], control.options[i], control.displayValues[i], control.inputNames[i], info=control.inputInfo[i])} %elif control.inputTypes[i] == 'multi': @@ -83,8 +68,6 @@ else: ${functions.password(control.inputIds[i], control.displayValues[i], control.inputNames[i], reload=control.prototype.isDynamic(), info=control.inputInfo[i])} %elif control.inputTypes[i] == '__genome__': ${functions.genomeChooser(control, control.options[i], control.inputValues[i], control.inputIds[i])} - %elif control.inputTypes[i] == '__track__': - ${functions.trackChooser(control.trackElements[control.inputIds[i]], i, params, False)} %elif control.inputTypes[i] == '__history__': ${functions.history_select(control, control.inputIds[i], control.options[i], control.displayValues[i], control.inputNames[i], info=control.inputInfo[i])} %elif control.inputTypes[i] == '__toolhistory__': @@ -96,7 +79,42 @@ else: %elif control.inputTypes[i] == 'table': ${control.displayValues[i]} %endif - + + +%if control.userHasFullAccess(): + + + + + + + + + + + + + + + %if len(control.subClasses) > 0: + ${functions.select('sub_class_id', control.subClasses.keys(), control.subClassId, control.subToolSelectionTitle)} + %endif + + %for i in control.inputOrder: + %if i in control.inputGroup[0]: + %for label in control.inputGroup[0][i]: +
${label} + %endfor + %endif + + ${self.showOptionsBox(control, params, i)} + + %if i in control.inputGroup[1]: + %for j in range(0, control.inputGroup[1].count(i)): +
+ %endfor + %endif + %endfor

@@ -111,46 +129,14 @@ else:
${control.executeNoHistory()}
%endif - - %if control.prototype.isBatchTool() and control.getBatchLine() and control.isValid() and control.userIsOneOfUs(): -

- Corresponding batch command line: - -

-
- -

- Add run to test repository: - - - set test name:
- -

-

- - %endif + ${self.extraGuiContent(control)} %else: ${functions.accessDenied()} %endif +<%def name="extraGuiContent(control)"/> + <%def name="toolHelp()"> %if control.hasDemoURL(): From b4e63788bea42a54a3026170335a172ae447ea73 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 5 Jan 2018 15:15:04 +0100 Subject: [PATCH 002/123] Cleanup --- templates/proto/common.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/proto/common.js b/templates/proto/common.js index 2d024ee55c31..36a352ef3743 100644 --- a/templates/proto/common.js +++ b/templates/proto/common.js @@ -1,5 +1,5 @@ function reloadForm(form, element) { -disablePage(); + disablePage(); if (!form) form = document.forms[0]; //if (form.focused_element && element) // form.focused_element.value = element.id; From 889f66f8278ac039ecdd9d9c6f3e8182943ffbb8 Mon Sep 17 00:00:00 2001 From: Morten Johansen Date: Mon, 7 Nov 2016 14:01:46 +0100 Subject: [PATCH 003/123] Small ProTo edits and fixes (cherry picked from commit a706f9ba3b5f7dcbcf04228461726ac462aa3f75) --- client/galaxy/scripts/mvc/dataset/dataset-li-edit.js | 2 +- templates/proto/base.mako | 2 ++ templates/webapps/galaxy/dataset/display.mako | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/client/galaxy/scripts/mvc/dataset/dataset-li-edit.js b/client/galaxy/scripts/mvc/dataset/dataset-li-edit.js index f64d4283209f..936a1508889b 100644 --- a/client/galaxy/scripts/mvc/dataset/dataset-li-edit.js +++ b/client/galaxy/scripts/mvc/dataset/dataset-li-edit.js @@ -172,7 +172,7 @@ var DatasetListItemEdit = _super.extend( require([ 'mvc/tool/tool-form' ], function( ToolForm ){ var form = new ToolForm.View({ 'job_id' : creating_job }); form.deferred.execute( function(){ - console.log(form.options); + // console.log(form.options); if (form.options.model_class.startsWith('Proto')) galaxy_main.location = rerun_url; else diff --git a/templates/proto/base.mako b/templates/proto/base.mako index aac19dc20652..1aa761909229 100644 --- a/templates/proto/base.mako +++ b/templates/proto/base.mako @@ -153,6 +153,8 @@ ${self.head()} +<%def name="javascript_app()"> + <%def name="head()"> <%def name="action()"> <%def name="toolHelp()"> diff --git a/templates/webapps/galaxy/dataset/display.mako b/templates/webapps/galaxy/dataset/display.mako index 4feef6d97e54..60a465532b28 100644 --- a/templates/webapps/galaxy/dataset/display.mako +++ b/templates/webapps/galaxy/dataset/display.mako @@ -59,7 +59,7 @@ ## Renders dataset content. Function is used to render data in stand-along page and to provide content for embedded datasets as well. <%def name="render_item( data, data_to_render )"> ${ render_deleted_data_message( data ) } - %if data_to_render: + %if data_to_render and data.ext != 'html': %if truncated:
This dataset is large and only the first megabyte is shown below. | From faa9f977aca0c36bdfcbc6e48036339f7bee107e Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Mon, 7 Nov 2016 14:06:06 +0100 Subject: [PATCH 004/123] ProTo: Add common functions, plus small bugfix (cherry picked from commit 7e1553f74524487edf66b96af736ce501c7261a5) --- lib/proto/CommonFunctions.py | 55 +++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/lib/proto/CommonFunctions.py b/lib/proto/CommonFunctions.py index dca6e507b14a..e84425cf13bd 100644 --- a/lib/proto/CommonFunctions.py +++ b/lib/proto/CommonFunctions.py @@ -23,6 +23,7 @@ import contextlib from proto.config.Config import GALAXY_BASE_DIR +from proto.CommonConstants import THOUSANDS_SEPARATOR """ Note on datasetInfo and datasetId (used in several functions): @@ -147,7 +148,7 @@ def getEncodedDatasetIdFromPlainGalaxyId(plainId): return galaxySecureEncodeId(plainId) -def getEncodedDatasetIdFromGalaxyFn(cls, galaxyFn): +def getEncodedDatasetIdFromGalaxyFn(galaxyFn): plainId = extractIdFromGalaxyFn(galaxyFn)[1] return getEncodedDatasetIdFromPlainGalaxyId(plainId) @@ -237,3 +238,55 @@ def getLoadToGalaxyHistoryURL(fn, genome='hg18', galaxyDataType='bed', urlPrefix assert galaxyDataType is not None return urlPrefix + '/tool_runner?tool_id=file_import&dbkey=%s&runtool_btn=yes&input=' % (genome,) \ + base64.urlsafe_b64encode(fn) + ('&format=' + galaxyDataType if galaxyDataType is not None else '') + + +def strWithStdFormatting(val, separateThousands=True, floatFormatFlag='g'): + try: + assert val != int(val) + integral, fractional = (('%#.' + str(OUTPUT_PRECISION) + floatFormatFlag) % val).split('.') + except: + integral, fractional = str(val), None + + if not separateThousands: + return integral + ('.' + fractional if fractional is not None else '') + else: + try: + return ('-' if integral[0] == '-' else '') + \ + '{:,}'.format(abs(int(integral))).replace(',', THOUSANDS_SEPARATOR) + \ + ('.' + fractional if fractional is not None else '') + except: + return integral + + +def strWithNatLangFormatting(val, separateThousands=True): + return strWithStdFormatting(val, separateThousands=separateThousands, floatFormatFlag='f') + + +def sortDictOfLists(dictOfLists, sortColumnIndex, descending=True): + return OrderedDict(sorted( + list(dictOfLists.iteritems()), key=lambda t: (t[1][sortColumnIndex]), reverse=descending)) + + +def smartSortDictOfLists(dictOfLists, sortColumnIndex, descending=True): + """Sort numbers first than strings, take into account formatted floats""" + # convert = lambda text: int(text) if text.isdigit() else text + # alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)] + return OrderedDict(sorted( + list(dictOfLists.iteritems()), key=lambda t: forceNumericSortingKey(t[1][sortColumnIndex]), reverse=descending)) + + +def _strIsFloat(s): + try: + float(s) + return True + except: + return False + + +def forceNumericSortingKey(key): + sortKey1 = 0 + sortKey2 = key + if _strIsFloat(str(key).replace(THOUSANDS_SEPARATOR, '')): + sortKey1 = 1 + sortKey2 = float(str(key).replace(THOUSANDS_SEPARATOR, '')) + return [sortKey1, sortKey2] From ad7e735a4a57b4053d9dacdde319fd099ac1233f Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Mon, 7 Nov 2016 14:56:22 +0100 Subject: [PATCH 005/123] Added thousands separator in ProTo (cherry picked from commit 3b3bf0a0086f737fae88ff5eb328ce065fe5adbb) --- lib/proto/CommonConstants.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 lib/proto/CommonConstants.py diff --git a/lib/proto/CommonConstants.py b/lib/proto/CommonConstants.py new file mode 100644 index 000000000000..61a3d1c889f8 --- /dev/null +++ b/lib/proto/CommonConstants.py @@ -0,0 +1 @@ +THOUSANDS_SEPARATOR = ' ' From 1d4b103f0bd516049c680687c580f3ca57523166 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Wed, 16 Nov 2016 11:41:10 +0100 Subject: [PATCH 006/123] Refactored HtmlCore in ProTo (cherry picked from commit 305801d6cf6805112cb193790f8159c0cd1a8746) --- lib/proto/HtmlCore.py | 338 +++++++++++++++++++++++++----------------- 1 file changed, 206 insertions(+), 132 deletions(-) diff --git a/lib/proto/HtmlCore.py b/lib/proto/HtmlCore.py index 3e5fe9c956be..0c1b81e595d7 100644 --- a/lib/proto/HtmlCore.py +++ b/lib/proto/HtmlCore.py @@ -1,17 +1,32 @@ import os -from proto.config.Config import URL_PREFIX -from collections import OrderedDict -#from gold.util.CustomExceptions import ShouldNotOccurError +from config.Config import URL_PREFIX +from proto.CommonConstants import THOUSANDS_SEPARATOR import re +from collections import OrderedDict + class HtmlCore(object): def __init__(self): self._str = '' - def begin(self, extraJavaScriptFns=[], extraJavaScriptCode=None, extraCssFns=[]): + def begin(self, extraJavaScriptFns=[], extraJavaScriptCode=None, extraCssFns=[], redirectUrl=None, reloadTime=None): self._str = ''' - +''' + + if redirectUrl: + self._str += ''' +''' % redirectUrl + + if reloadTime: + self._str += ''' + +''' % (reloadTime * 1000) + + self._str += ''' ''' @@ -53,7 +68,12 @@ def bigHeader(self, title): def smallHeader(self, title): return self.highlight(title) - def end(self): + def end(self, stopReload=False): + if stopReload: + self._str += ''' +''' self._str += ''' ''' @@ -103,34 +123,189 @@ def emphasize(self, text): self._str += '' + text + '' return self - def tableHeader(self, headerRow, tagRow=None, firstRow=True, sortable=False, tableId=None): + def tableHeader(self, headerRow, tagRow=None, firstRow=True, sortable=False, + tableId=None, tableClass='colored bordered', headerClass='header', + style='table-layout:auto;word-wrap:break-word;', **kwargs): if firstRow: - tableId = 'id="%s" '%tableId if tableId else '' - sortable = ' sortable' if sortable else '' - self._str += '' \ - % (tableId, sortable) + os.linesep + tableIdStr = 'id="%s" ' % tableId if tableId else '' + tableClassList = tableClass.split() + if sortable: + tableClassList.append('sortable') + tableClassStr = 'class="' + ' '.join(tableClassList) + '" ' \ + if tableClassList else '' + self._str += '
' \ + % (tableIdStr, tableClassStr, style) + os.linesep if headerRow not in [None, []]: if tagRow is None: tagRow = [''] * len(headerRow) self._str += '' - for tag,el in zip(tagRow, headerRow): - self._str += '' + headerClassStr = ' class="' + ' '.join(headerClass.split()) + '"' \ + if headerClass else '' + for tag, el in zip(tagRow, headerRow): + self._str += '' + str(el) + '' self._str += '' + os.linesep return self - def tableLine(self, row, rowSpanList=None): - self._str += '' - for i,el in enumerate(row): - self._str += '' + str(el) + '' + def tableLine(self, row, rowSpanList=None, **kwargs): + self.tableRowBegin(**kwargs) + for i, el in enumerate(row): + rowSpan = rowSpanList[i] if rowSpanList else None + self.tableCell(str(el), rowSpan=rowSpan, **kwargs) + self.tableRowEnd(**kwargs) + return self + + def tableRowBegin(self, rowClass=None, **kwargs): + self._str += '' + + def tableFooter(self, **kwargs): self._str += '
' + str(el) + '
'+ os.linesep return self + def tableFromDictionary(self, dataDict, columnNames=None, sortable=True, + tableId=None, expandable=False, visibleRows=6, + presorted=None, **kwargs): + """Render a table from data in dataDict. Each key in dataDict is a row title, + each value is a list of values, each corresponding to the column given with columnNames. + + If presorted is set to a number and tableId != None and sortable == True, that column will be presorted (using a hacky solution using jquery. + """ + + # transform dicts with a single value to a dict of lists for easier + # sorting and html table generation + dataDictOfLists = OrderedDict() + for key, val in dataDict.iteritems(): + if isinstance(val, list): + dataDictOfLists[key] = val + elif isinstance(val, tuple): + dataDictOfLists[key] = list(val) + else: + dataDictOfLists[key] = [val] + + if presorted is not None and presorted > -1: + assert isinstance(presorted, int), 'presorted must be int' + from quick.util import CommonFunctions + dataDictOfLists = CommonFunctions.smartSortDictOfLists( + dataDictOfLists, sortColumnIndex=presorted) + + tableClass = 'colored bordered' + if expandable: + assert tableId, 'Table ID must be set for expandable tables.' + tableClass += ' expandable' + + self.tableHeader(headerRow=columnNames, sortable=sortable, + tableId=tableId, + tableClass=tableClass, **kwargs) + + for key, val in dataDictOfLists.iteritems(): + if isinstance(val, list): + self.tableLine([key] + val) + else: + self.tableLine([key] + [val]) + + self.tableFooter() + + # if tableId != None and sortable and presorted: + # # Javascript code for clicking on the column (so that it is sorted client side) + # # Hacky solution: Emulates a click on the header with a 500 ms delay so that sorttable.js is done first + # self._str += "" + + if expandable and len(dataDict) > visibleRows: + self.tableExpandButton(tableId, len(dataDict), + visibleRows=visibleRows) + + return self + + def tableFromDictOfDicts(self, dataDict, firstColName='', sortable=True, + tableId=None, expandable=False, visibleRows=6, + presorted=None, **kwargs): + """ + # Note: it is assumed that dataDict is a full matrix, i.e. each element in + # the dict is a dict of the same size. + """ + + assert isinstance(dataDict, OrderedDict) and \ + all(isinstance(x, OrderedDict) for x in dataDict.values()), \ + 'dataDict must be an OrderedDict of OrderedDicts' + + colNames = [] + convertedDataDict = OrderedDict() + + for key1, val1 in dataDict.iteritems(): + if not colNames: + colNames = [firstColName] + val1.keys() + convertedDataDict[key1] = val1.values() + + return self.tableFromDictionary(convertedDataDict, + columnNames=colNames, + sortable=sortable, tableId=tableId, + expandable=expandable, + visibleRows=visibleRows, + presorted=presorted, **kwargs) + + def tableExpandButton(self, tableId, totalRows, visibleRows=6): + self.script(''' +function expandTable(tableId) { + tblId = "#" + tableId; + $(tblId).find("tr").show(); + btnDivId = "#toggle_table_" + tableId; + $(btnDivId).find("input").toggle(); +} + +function collapseTable(tableId, visibleRows) { + tblId = "#" + tableId; + trScltr = tblId + " tr:nth-child(n + " + visibleRows + ")"; + $(trScltr).hide(); + btnDivId = "#toggle_table_" + tableId; + $(btnDivId).find("input").toggle(); +} + +$(document).ready(function(){ + hiddenRowsSlctr = "table.expandable tr:nth-child(n + %s)"; + $(hiddenRowsSlctr).hide(); +} +);''' % str(visibleRows + 2)) # '+2' for some reason (one of life's great mysteries) + + self._str += '''
+ + + ''' % ( + tableId, visibleRows, totalRows, tableId, totalRows, totalRows, + tableId, visibleRows + 1) + return self + def divider(self, withSpacing=False): self._str += '
' % ('style="margin-top: 20px; margin-bottom: 20px;"' if withSpacing else '') + os.linesep return self @@ -201,125 +376,14 @@ def script(self, script): return self def _getStyleClassOrIdItem(self, styleClass, styleId): + assert styleClass or styleId if styleClass: return '.%s' % styleClass elif styleId: return '#%s' % styleId - else: - raise Exception() - #raise ShouldNotOccurError() - - def tableFromDictionary(self, dataDict, columnNames=None, sortable=True, tableId=None, expandable=False, - visibleRows=6, presorted=None, addInstruction=None): - """Render a table from data in dataDict. Each key in dataDict is a row title, - each value is a list of values, each corresponding to the column given with columnNames. - - If presorted is set to a number and tableId != None and sortable == True, that column will be presorted (using a hacky solution using jquery. - """ - - # transfom dicts with a single value to a dict of lists for easier sorting and html table generation - dataDictOfLists = OrderedDict() - for key, val in dataDict.iteritems(): - if isinstance(val, list): - dataDictOfLists[key] = val - elif isinstance(val, tuple): - dataDictOfLists[key] = list(val) - else: - dataDictOfLists[key] = [val] - - if presorted is not None and presorted > -1: - assert isinstance(presorted, int), 'presorted must be int' - from quick.util import CommonFunctions - dataDictOfLists = CommonFunctions.smartSortDictOfLists(dataDictOfLists, sortColumnIndex=presorted) - - if expandable: - assert tableId is not None, 'Table ID must be set for expandable tables.' - self.tableHeaderWithClass(headerRow=columnNames, sortable=sortable, tableId=tableId, - tableClass='colored bordered expandable', addInstruction=addInstruction) - else: - self.tableHeader(headerRow=columnNames, sortable=sortable, tableId=tableId, addInstruction=addInstruction) - - # for key, val in dataDict.iteritems(): - for key, val in dataDictOfLists.iteritems(): - if isinstance(val, list): - self.tableLine([key] + val) - else: - self.tableLine([key] + [val]) - - self.tableFooter() - # if tableId != None and sortable and presorted: - # # Javascript code for clicking on the column (so that it is sorted client side) - # # Hacky solution: Emulates a click on the header with a 500 ms delay so that sorttable.js is done first - # self._str += "" - - if expandable and len(dataDict) > visibleRows: - self._tableExpandButton(tableId, len(dataDict), visibleRows=visibleRows) - - return self - - def tableHeaderWithClass(self, headerRow, tableClass=None, tagRow=None, firstRow=True, sortable=False, - addInstruction=False, tableId=None): - if firstRow: - if addInstruction == True: - if tableId == '': - tableId = 'tab0' - self._str += self.addInstruction(tableName=tableId) - tableId = 'id="%s" ' % tableId if tableId else '' - styleClass = 'class="%s' % tableClass if tableClass else '' - if sortable: - if styleClass: - styleClass += ' sortable"' - else: - styleClass = 'class="sortable' - if styleClass: - styleClass += '"' - self._str += '' \ - % (tableId, styleClass) + os.linesep - - if headerRow not in [None, []]: - if tagRow is None: - tagRow = [''] * len(headerRow) - self._str += '' - for tag, el in zip(tagRow, headerRow): - self._str += '' - self._str += '' + os.linesep - - return self - - def _tableExpandButton(self, tableId, totalRows, visibleRows=6): - - self.script(''' - - function expandTable(tableId) { - tblId = "#" + tableId; - $(tblId).find("tr").show(); - btnDivId = "#toggle_table_" + tableId; - $(btnDivId).find("input").toggle(); - } - - function collapseTable(tableId, visibleRows) { - tblId = "#" + tableId; - trScltr = tblId + " tr:nth-child(n + " + visibleRows + ")"; - $(trScltr).hide(); - btnDivId = "#toggle_table_" + tableId; - $(btnDivId).find("input").toggle(); - } - - $(document).ready(function(){ - hiddenRowsSlctr = "table.expandable tr:nth-child(n + %s)"; - $(hiddenRowsSlctr).hide(); - } - ); - - ''' % str(visibleRows + 2)) # '+2' for some reason (one of life's great mysteries) - - self._str += '''
- - - ''' % (tableId, visibleRows, totalRows, tableId, totalRows, totalRows, tableId, visibleRows + 1) - - def toggle(self, text, styleClass=None, styleId=None, withDivider=False, otherAnchor=None, withLine=True): + def toggle(self, text, styleClass=None, styleId=None, + withDivider=False, otherAnchor=None, withLine=True): item = self._getStyleClassOrIdItem(styleClass, styleId) classOrId = styleClass if styleClass else styleId @@ -341,6 +405,16 @@ def hideToggle(self, styleClass=None, styleId=None): ''' % item + def fieldsetBegin(self, title=None): + self._str += '
' + os.linesep + if title: + self._str += '%s' % title + os.linesep + return self + + def fieldsetEnd(self): + self._str += '
' + os.linesep + return self + def image(self, imgFn, style=None): self._str += '''''' % \ (' style="%s"' % style if style is not None else '', imgFn) From b500ddbadfcbacb934d6894493b795fa816741fd Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Wed, 16 Nov 2016 15:43:35 +0100 Subject: [PATCH 007/123] ProTo: Refactored HtmlCore. Added TextCore. Moved Advanced table creation into separate TableCoreMixin, so that it works for both HtmlCore and TextCore. Small fix in HtmlCore.html Removed HtmlCore.html duplicate in strange directory location. (cherry picked from commit 8247763b1b2795a99bf63de38359ff35bf89ccd3) --- lib/HtmlCore.html | 132 -------------------------------- lib/proto/HtmlCore.py | 121 ++--------------------------- lib/proto/TableCoreMixin.py | 116 ++++++++++++++++++++++++++++ lib/proto/TextCore.py | 103 +++++++++++++++++++++++++ static/proto/html/HtmlCore.html | 4 +- 5 files changed, 226 insertions(+), 250 deletions(-) delete mode 100644 lib/HtmlCore.html create mode 100644 lib/proto/TableCoreMixin.py create mode 100644 lib/proto/TextCore.py diff --git a/lib/HtmlCore.html b/lib/HtmlCore.html deleted file mode 100644 index 85255c341d3c..000000000000 --- a/lib/HtmlCore.html +++ /dev/null @@ -1,132 +0,0 @@ - - -Python: module HtmlCore - - - -
' + str(el) + '
- -
 
- 
HtmlCore
index
/Users/sveinugu/PycharmProjects/proto/lib/proto/HtmlCore.py
-

-

- - - - - -
 
-Modules
       
os
-
re
-

- - - - - -
 
-Classes
       
-
__builtin__.object -
-
-
HtmlCore -
-
-
-

- - - - - -
 
-class HtmlCore(__builtin__.object)
    Methods defined here:
-
__init__(self)
- -
__str__(self)
- -
anchor(self, text, url, args='')
- -
append(self, htmlStr)
- -
begin(self, extraJavaScriptFns=[], extraJavaScriptCode=None, extraCssFns=[])
- -
bigHeader(self, title)
- -
descriptionLine(self, label, descr, indent=False, emphasize=False)
- -
divBegin(self, divId=None, divClass=None, style=None)
- -
divEnd(self)
- -
divider(self, withSpacing=False)
- -
emphasize(self, text)
- -
end(self)
- -
formBegin(self, name=None, action=None, method=None)
- -
formEnd(self)
- -
format(self, val)
- -
header(self, title)
- -
hideToggle(self, styleClass=None, styleId=None)
- -
highlight(self, text)
- -
image(self, imgFn, style=None)
- -
indent(self, text)
- -
line(self, l)
- -
link(self, text, url, popup=False, args='', withLine=True)
- -
orderedList(self, strList)
- -
paragraph(self, p, indent=False)
- -
radioButton(self, value, name=None, event=None)
- -
script(self, script)
- -
smallHeader(self, title)
- -
styleInfoBegin(self, styleId='', styleClass='', style='', inline=False, linesep=True)
- -
styleInfoEnd(self, inline=False)
- -
tableFooter(self)
- -
tableHeader(self, headerRow, tagRow=None, firstRow=True, sortable=False, tableId=None)
- -
tableLine(self, row, rowSpanList=None)
- -
textWithHelp(self, baseText, helpText)
- -
toggle(self, text, styleClass=None, styleId=None, withDivider=False, otherAnchor=None, withLine=True)
- -
unorderedList(self, strList)
- -
-Data descriptors defined here:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-

- - - - - -
 
-Data
       URL_PREFIX = ''
- \ No newline at end of file diff --git a/lib/proto/HtmlCore.py b/lib/proto/HtmlCore.py index 0c1b81e595d7..0ceb12a1422a 100644 --- a/lib/proto/HtmlCore.py +++ b/lib/proto/HtmlCore.py @@ -1,11 +1,12 @@ import os +import re + from config.Config import URL_PREFIX from proto.CommonConstants import THOUSANDS_SEPARATOR -import re -from collections import OrderedDict +from proto.TableCoreMixin import TableCoreMixin -class HtmlCore(object): +class HtmlCore(TableCoreMixin): def __init__(self): self._str = '' @@ -127,7 +128,7 @@ def tableHeader(self, headerRow, tagRow=None, firstRow=True, sortable=False, tableId=None, tableClass='colored bordered', headerClass='header', style='table-layout:auto;word-wrap:break-word;', **kwargs): if firstRow: - tableIdStr = 'id="%s" ' % tableId if tableId else '' + tableIdStr = ('id="%s" ' % tableId) if tableId else '' tableClassList = tableClass.split() if sortable: tableClassList.append('sortable') @@ -194,118 +195,6 @@ def tableFooter(self, **kwargs): self._str += ''+ os.linesep return self - def tableFromDictionary(self, dataDict, columnNames=None, sortable=True, - tableId=None, expandable=False, visibleRows=6, - presorted=None, **kwargs): - """Render a table from data in dataDict. Each key in dataDict is a row title, - each value is a list of values, each corresponding to the column given with columnNames. - - If presorted is set to a number and tableId != None and sortable == True, that column will be presorted (using a hacky solution using jquery. - """ - - # transform dicts with a single value to a dict of lists for easier - # sorting and html table generation - dataDictOfLists = OrderedDict() - for key, val in dataDict.iteritems(): - if isinstance(val, list): - dataDictOfLists[key] = val - elif isinstance(val, tuple): - dataDictOfLists[key] = list(val) - else: - dataDictOfLists[key] = [val] - - if presorted is not None and presorted > -1: - assert isinstance(presorted, int), 'presorted must be int' - from quick.util import CommonFunctions - dataDictOfLists = CommonFunctions.smartSortDictOfLists( - dataDictOfLists, sortColumnIndex=presorted) - - tableClass = 'colored bordered' - if expandable: - assert tableId, 'Table ID must be set for expandable tables.' - tableClass += ' expandable' - - self.tableHeader(headerRow=columnNames, sortable=sortable, - tableId=tableId, - tableClass=tableClass, **kwargs) - - for key, val in dataDictOfLists.iteritems(): - if isinstance(val, list): - self.tableLine([key] + val) - else: - self.tableLine([key] + [val]) - - self.tableFooter() - - # if tableId != None and sortable and presorted: - # # Javascript code for clicking on the column (so that it is sorted client side) - # # Hacky solution: Emulates a click on the header with a 500 ms delay so that sorttable.js is done first - # self._str += "" - - if expandable and len(dataDict) > visibleRows: - self.tableExpandButton(tableId, len(dataDict), - visibleRows=visibleRows) - - return self - - def tableFromDictOfDicts(self, dataDict, firstColName='', sortable=True, - tableId=None, expandable=False, visibleRows=6, - presorted=None, **kwargs): - """ - # Note: it is assumed that dataDict is a full matrix, i.e. each element in - # the dict is a dict of the same size. - """ - - assert isinstance(dataDict, OrderedDict) and \ - all(isinstance(x, OrderedDict) for x in dataDict.values()), \ - 'dataDict must be an OrderedDict of OrderedDicts' - - colNames = [] - convertedDataDict = OrderedDict() - - for key1, val1 in dataDict.iteritems(): - if not colNames: - colNames = [firstColName] + val1.keys() - convertedDataDict[key1] = val1.values() - - return self.tableFromDictionary(convertedDataDict, - columnNames=colNames, - sortable=sortable, tableId=tableId, - expandable=expandable, - visibleRows=visibleRows, - presorted=presorted, **kwargs) - - def tableExpandButton(self, tableId, totalRows, visibleRows=6): - self.script(''' -function expandTable(tableId) { - tblId = "#" + tableId; - $(tblId).find("tr").show(); - btnDivId = "#toggle_table_" + tableId; - $(btnDivId).find("input").toggle(); -} - -function collapseTable(tableId, visibleRows) { - tblId = "#" + tableId; - trScltr = tblId + " tr:nth-child(n + " + visibleRows + ")"; - $(trScltr).hide(); - btnDivId = "#toggle_table_" + tableId; - $(btnDivId).find("input").toggle(); -} - -$(document).ready(function(){ - hiddenRowsSlctr = "table.expandable tr:nth-child(n + %s)"; - $(hiddenRowsSlctr).hide(); -} -);''' % str(visibleRows + 2)) # '+2' for some reason (one of life's great mysteries) - - self._str += '''

- - - ''' % ( - tableId, visibleRows, totalRows, tableId, totalRows, totalRows, - tableId, visibleRows + 1) - return self - def divider(self, withSpacing=False): self._str += '
' % ('style="margin-top: 20px; margin-bottom: 20px;"' if withSpacing else '') + os.linesep return self diff --git a/lib/proto/TableCoreMixin.py b/lib/proto/TableCoreMixin.py new file mode 100644 index 000000000000..65ff3d92c6cc --- /dev/null +++ b/lib/proto/TableCoreMixin.py @@ -0,0 +1,116 @@ +from collections import OrderedDict + + +class TableCoreMixin(object): + def tableFromDictionary(self, dataDict, columnNames=None, sortable=True, + tableId=None, expandable=False, visibleRows=6, + presorted=None, **kwargs): + """Render a table from data in dataDict. Each key in dataDict is a row title, + each value is a list of values, each corresponding to the column given with columnNames. + + If presorted is set to a number and tableId != None and sortable == True, that column will be presorted (using a hacky solution using jquery. + """ + + # transform dicts with a single value to a dict of lists for easier + # sorting and html table generation + dataDictOfLists = OrderedDict() + for key, val in dataDict.iteritems(): + if isinstance(val, list): + dataDictOfLists[key] = val + elif isinstance(val, tuple): + dataDictOfLists[key] = list(val) + else: + dataDictOfLists[key] = [val] + + if presorted is not None and presorted > -1: + assert isinstance(presorted, int), 'presorted must be int' + from quick.util import CommonFunctions + dataDictOfLists = CommonFunctions.smartSortDictOfLists( + dataDictOfLists, sortColumnIndex=presorted) + + tableClass = 'colored bordered' + if expandable: + assert tableId, 'Table ID must be set for expandable tables.' + tableClass += ' expandable' + + self.tableHeader(headerRow=columnNames, sortable=sortable, + tableId=tableId, + tableClass=tableClass, **kwargs) + + for key, val in dataDictOfLists.iteritems(): + if isinstance(val, list): + self.tableLine([key] + val) + else: + self.tableLine([key] + [val]) + + self.tableFooter() + + # if tableId != None and sortable and presorted: + # # Javascript code for clicking on the column (so that it is sorted client side) + # # Hacky solution: Emulates a click on the header with a 500 ms delay so that sorttable.js is done first + # self._str += "" + + if expandable and len(dataDict) > visibleRows: + self.tableExpandButton(tableId, len(dataDict), + visibleRows=visibleRows) + + return self + + def tableFromDictOfDicts(self, dataDict, firstColName='', sortable=True, + tableId=None, expandable=False, visibleRows=6, + presorted=None, **kwargs): + """ + # Note: it is assumed that dataDict is a full matrix, i.e. each element in + # the dict is a dict of the same size. + """ + + assert isinstance(dataDict, OrderedDict) and \ + all(isinstance(x, OrderedDict) for x in dataDict.values()), \ + 'dataDict must be an OrderedDict of OrderedDicts' + + colNames = [] + convertedDataDict = OrderedDict() + + for key1, val1 in dataDict.iteritems(): + if not colNames: + colNames = [firstColName] + val1.keys() + convertedDataDict[key1] = val1.values() + + self.tableFromDictionary(convertedDataDict, + columnNames=colNames, + sortable=sortable, tableId=tableId, + expandable=expandable, + visibleRows=visibleRows, + presorted=presorted, **kwargs) + + return self + + def tableExpandButton(self, tableId, totalRows, visibleRows=6): + self.script(''' +function expandTable(tableId) { + tblId = "#" + tableId; + $(tblId).find("tr").show(); + btnDivId = "#toggle_table_" + tableId; + $(btnDivId).find("input").toggle(); +} + +function collapseTable(tableId, visibleRows) { + tblId = "#" + tableId; + trScltr = tblId + " tr:nth-child(n + " + visibleRows + ")"; + $(trScltr).hide(); + btnDivId = "#toggle_table_" + tableId; + $(btnDivId).find("input").toggle(); +} + +$(document).ready(function(){ + hiddenRowsSlctr = "table.expandable tr:nth-child(n + %s)"; + $(hiddenRowsSlctr).hide(); +} +);''' % str(visibleRows + 2)) # '+2' for some reason (one of life's great mysteries) + + self._str += ''' +
+ + +''' % (tableId, visibleRows, totalRows, tableId, totalRows, totalRows, tableId, visibleRows + 1) + return self diff --git a/lib/proto/TextCore.py b/lib/proto/TextCore.py new file mode 100644 index 000000000000..8a80097b0d11 --- /dev/null +++ b/lib/proto/TextCore.py @@ -0,0 +1,103 @@ +import os +from proto.HtmlCore import HtmlCore +from proto.TableCoreMixin import TableCoreMixin + + +class TextCore(TableCoreMixin): + HTML_CORE_CLS = HtmlCore + + def __init__(self): + self._str = '' + + def __getattr__(self, item): + if getattr(self.HTML_CORE_CLS, item): + return self._default + else: + raise AttributeError('TextCore does not contain member "%s".' % item) + + def _default(self, *args, **kwargs): + return self + + def header(self, title): + self._str += '#' + title + os.linesep + os.linesep + return self + + def bigHeader(self, title): + self._str += '#' + title.upper() + os.linesep + os.linesep + return self + + def descriptionLine(self, label, descr, indent=False): + self._str += ('\t' if indent else '' ) + label + ': ' + descr + os.linesep + return self + + def line(self, l): + self._str += l + os.linesep + return self + + def format(self, val): + from gold.util.CommonFunctions import strWithStdFormatting + self._str += strWithStdFormatting(val, separateThousands=False) + return self + + def paragraph(self, p, indent=False): + self._str += ('\t' if indent else '' ) + p + os.linesep + os.linesep + return self + + def indent(self, text): + self._str += '\t' + text + return self + + def highlight(self, text): + self._str += text + return self + + def emphasize(self, text): + self._str += text + return self + + def tableHeader(self, headerRow, **kwargs): + self._str += '\t'.join([str(el).upper() for el in headerRow]) + self._str += os.linesep + return self + + def tableLine(self, row, rowSpanList=None, **kwargs): + self._str += '\t'.join(['' if rowSpanList is not None and rowSpanList[i]==0 \ + else str(el) for i,el in enumerate(row)]) + self._str += os.linesep + return self + + def tableFooter(self, **kwargs): + self._str += os.linesep + return self + + def divider(self, withSpacing=False): + self._str += (os.linesep if withSpacing else '') + os.linesep + \ + '---------------' + os.linesep + os.linesep + return self + + def textWithHelp(self, baseText, helpText): + self._str += baseText + return self + + def link(self, text, url, popup=False, args='', withLine=True): + self._str += text + ('(' + url +')' if url not in ['', '#'] else '') + return self + + def unorderedList(self, strList): + for s in strList: + self._str += '* %s' % s + os.linesep + self._str += os.linesep + return self + + def orderedList(self, strList): + for i, s in enumerate(strList): + self._str += '%i. %s' % (i, s) + os.linesep + self._str += os.linesep + return self + + def append(self, str): + self._str += str + return self + + def __str__(self): + return self._str diff --git a/static/proto/html/HtmlCore.html b/static/proto/html/HtmlCore.html index 85255c341d3c..d08742dd94cd 100644 --- a/static/proto/html/HtmlCore.html +++ b/static/proto/html/HtmlCore.html @@ -9,7 +9,7 @@  
 
HtmlCore
index
/Users/sveinugu/PycharmProjects/proto/lib/proto/HtmlCore.py
+>index
../../../lib/proto/HtmlCore.py

@@ -129,4 +129,4 @@
        URL_PREFIX = ''
- \ No newline at end of file + From 39cac199250989fc3e0876ca39ff8e67bb32724c Mon Sep 17 00:00:00 2001 From: Morten Johansen Date: Wed, 16 Nov 2016 17:42:39 +0100 Subject: [PATCH 008/123] ProTo: UniCode fix in dataset decoding (cherry picked from commit 0105df417fadb7699656f592bdf1894b7bc384c2) --- lib/proto/CommonFunctions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/proto/CommonFunctions.py b/lib/proto/CommonFunctions.py index e84425cf13bd..5915383195e8 100644 --- a/lib/proto/CommonFunctions.py +++ b/lib/proto/CommonFunctions.py @@ -141,7 +141,7 @@ def galaxySecureEncodeId(plainId): def galaxySecureDecodeId(encodedId): from proto.config.Config import GALAXY_SECURITY_HELPER_OBJ - return GALAXY_SECURITY_HELPER_OBJ.decode_id(encodedId) + return GALAXY_SECURITY_HELPER_OBJ.decode_id(str(encodedId)) def getEncodedDatasetIdFromPlainGalaxyId(plainId): From 9fea4980c2800689f7b513019975bf82ddf838a8 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Wed, 16 Nov 2016 17:56:19 +0100 Subject: [PATCH 009/123] ProTo: Fix missing reference (cherry picked from commit 8cc821579a688da68ecedb67c6003ce4569f23bb) --- lib/proto/CommonFunctions.py | 8 +++----- lib/proto/config/Config.py | 2 ++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/proto/CommonFunctions.py b/lib/proto/CommonFunctions.py index 5915383195e8..5dedefe001a5 100644 --- a/lib/proto/CommonFunctions.py +++ b/lib/proto/CommonFunctions.py @@ -15,15 +15,13 @@ # along with The Genomic HyperBrowser. If not, see . import os -import shelve -import sys -import functools import re +import shelve import urllib -import contextlib +from collections import OrderedDict -from proto.config.Config import GALAXY_BASE_DIR from proto.CommonConstants import THOUSANDS_SEPARATOR +from proto.config.Config import GALAXY_BASE_DIR, OUTPUT_PRECISION """ Note on datasetInfo and datasetId (used in several functions): diff --git a/lib/proto/config/Config.py b/lib/proto/config/Config.py index 5eb8b1cf31ff..5f23e545adee 100644 --- a/lib/proto/config/Config.py +++ b/lib/proto/config/Config.py @@ -4,6 +4,8 @@ GALAXY_BASE_DIR = os.path.abspath(os.path.dirname(__file__) + '/../../../.') +OUTPUT_PRECISION = 4 + def getUniverseConfigParser(): config = SafeConfigParser({'here': GALAXY_BASE_DIR}) From e5faee93b04eef38a563e5300c10a0582f22a83f Mon Sep 17 00:00:00 2001 From: Morten Johansen Date: Wed, 23 Nov 2016 13:42:51 +0100 Subject: [PATCH 010/123] ProTo: Moved dataset security code into separate file. Added output_precision config in galaxy.ini.sample. (cherry picked from commit 4bb0b07f3a5059ddf393f88df7b75495b87f3efb) --- config/galaxy.ini.sample | 5 +++ .../webapps/galaxy/controllers/proto.py | 2 + lib/proto/BaseToolController.py | 6 +-- lib/proto/CommonFunctions.py | 11 +---- lib/proto/config/Config.py | 45 +------------------ 5 files changed, 13 insertions(+), 56 deletions(-) diff --git a/config/galaxy.ini.sample b/config/galaxy.ini.sample index e18c0bb1d714..033625ea2a9a 100644 --- a/config/galaxy.ini.sample +++ b/config/galaxy.ini.sample @@ -1176,3 +1176,8 @@ use_interactive = True # privileges. All Galaxy Admins will have full ProTo privileges without having to # be in the restricted users list. #restricted_users = + +# The number of significant digits in printed numbers for tools that make use +# of the standard number formatting functionality of Galaxy ProTo +# (strWithStdFormatting). +#output_precision = 4 diff --git a/lib/galaxy/webapps/galaxy/controllers/proto.py b/lib/galaxy/webapps/galaxy/controllers/proto.py index dc8f4ca001db..9eb4083b87df 100644 --- a/lib/galaxy/webapps/galaxy/controllers/proto.py +++ b/lib/galaxy/webapps/galaxy/controllers/proto.py @@ -14,6 +14,8 @@ # You should have received a copy of the GNU General Public License # along with The Genomic HyperBrowser. If not, see . +from __future__ import absolute_import + import sys, os from galaxy.web.base.controller import web, error, BaseUIController diff --git a/lib/proto/BaseToolController.py b/lib/proto/BaseToolController.py index ca83ea300c2a..a9f18ed50239 100644 --- a/lib/proto/BaseToolController.py +++ b/lib/proto/BaseToolController.py @@ -16,12 +16,12 @@ # import sys -#from gold.application.GalaxyInterface import GalaxyInterface from collections import OrderedDict +from urllib import unquote from proto.hyper_gui import load_input_parameters, SelectElement, \ GalaxyWrapper, getDataFilePath -from config import Config -from urllib import unquote +from proto.config import Config + class BaseToolController(object): def __init__(self, trans = None, job = None): diff --git a/lib/proto/CommonFunctions.py b/lib/proto/CommonFunctions.py index 5dedefe001a5..5e27c3a75a19 100644 --- a/lib/proto/CommonFunctions.py +++ b/lib/proto/CommonFunctions.py @@ -22,6 +22,7 @@ from proto.CommonConstants import THOUSANDS_SEPARATOR from proto.config.Config import GALAXY_BASE_DIR, OUTPUT_PRECISION +from proto.config.Security import galaxySecureEncodeId, galaxySecureDecodeId """ Note on datasetInfo and datasetId (used in several functions): @@ -132,16 +133,6 @@ def getGalaxyFnFromDatasetId(num, galaxyFilePath=None): return os.path.join(galaxyFilePath, id1, 'dataset_%s.dat' % id2) -def galaxySecureEncodeId(plainId): - from proto.config.Config import GALAXY_SECURITY_HELPER_OBJ - return GALAXY_SECURITY_HELPER_OBJ.encode_id(plainId) - - -def galaxySecureDecodeId(encodedId): - from proto.config.Config import GALAXY_SECURITY_HELPER_OBJ - return GALAXY_SECURITY_HELPER_OBJ.decode_id(str(encodedId)) - - def getEncodedDatasetIdFromPlainGalaxyId(plainId): return galaxySecureEncodeId(plainId) diff --git a/lib/proto/config/Config.py b/lib/proto/config/Config.py index 5f23e545adee..7c3de6245938 100644 --- a/lib/proto/config/Config.py +++ b/lib/proto/config/Config.py @@ -1,30 +1,4 @@ -from ConfigParser import SafeConfigParser -import os - - -GALAXY_BASE_DIR = os.path.abspath(os.path.dirname(__file__) + '/../../../.') - -OUTPUT_PRECISION = 4 - - -def getUniverseConfigParser(): - config = SafeConfigParser({'here': GALAXY_BASE_DIR}) - configRelFn = os.environ.get('GALAXY_CONFIG_FILE') - if not configRelFn: - configRelFn = 'config/galaxy.ini' - configFn = GALAXY_BASE_DIR + '/' + configRelFn - if os.path.exists(configFn): - config.read(configFn) - else: - raise Exception('No Galaxy config file found at path: ' + configFn) - return config - - -def getFromConfig(config, key, default, section='app:main'): - try: - return config.get(section, key) - except: - return default +from proto.config.Security import GALAXY_SECURITY_HELPER_OBJ, GALAXY_BASE_DIR, getFromConfig, getUniverseConfigParser def getUrlPrefix(config): @@ -38,12 +12,12 @@ def getUrlPrefix(config): if not globals().get('URL_PREFIX'): URL_PREFIX = getUrlPrefix(config) - GALAXY_REL_TOOL_CONFIG_FILE = getFromConfig(config, 'tool_config_file', 'config/tool_conf.xml') ADMIN_USERS = [username.strip() for username in getFromConfig(config, 'admin_users', '').split(',')] RESTRICTED_USERS = [username.strip() for username in getFromConfig(config, 'restricted_users', '', 'galaxy_proto').split(',')] +OUTPUT_PRECISION = int(getFromConfig(config, 'output_precision', '4', 'galaxy_proto')) STATIC_REL_PATH = URL_PREFIX + '/static/proto' STATIC_PATH = GALAXY_BASE_DIR + '/' + STATIC_REL_PATH GALAXY_URL = URL_PREFIX @@ -52,18 +26,3 @@ def getUrlPrefix(config): def userHasFullAccess(galaxyUserName): return galaxyUserName in ADMIN_USERS + RESTRICTED_USERS if galaxyUserName not in [None, ''] else False - - -def galaxyGetSecurityHelper(config): - from galaxy.web.security import SecurityHelper - - id_secret = getFromConfig(config, 'proto_id_secret', - 'USING THE DEFAULT IS ALSO NOT SECURE!', - section='galaxy_proto') - return SecurityHelper(id_secret=id_secret) - - -try: - GALAXY_SECURITY_HELPER_OBJ = galaxyGetSecurityHelper(config) -except: - GALAXY_SECURITY_HELPER_OBJ = None From aa93c0cc171b13fac68929e796716a934a9eb1b0 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Wed, 23 Nov 2016 16:02:30 +0100 Subject: [PATCH 011/123] ProTo: Small refactorings (cherry picked from commit ab08a11f7e649d3f2002cd6b9e4bcf17bed74d0c) --- lib/proto/hyper_gui.py | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/lib/proto/hyper_gui.py b/lib/proto/hyper_gui.py index 9c673af9af63..377ff283a3b4 100644 --- a/lib/proto/hyper_gui.py +++ b/lib/proto/hyper_gui.py @@ -81,12 +81,12 @@ def __init__(self, trans): self.params[key] = params[key] def encode_id(self, id): - from proto.CommonFunctions import galaxySecureEncodeId + from proto.config.Security import galaxySecureEncodeId return galaxySecureEncodeId(id) # return self.trans.security.encode_id(id) def decode_id(self, id): - from proto.CommonFunctions import galaxySecureDecodeId + from proto.config.Security import galaxySecureDecodeId return galaxySecureDecodeId(id) # return self.trans.security.decode_id(id) @@ -114,11 +114,15 @@ def getValidRScripts(self): pass return datasets - def optionsFromHistory(self, exts, sel = None): + def optionsFromHistory(self, exts, sel=None, datasets=None): html = '' - for dataset in self.trans.get_history().active_datasets: - if exts == None or dataset.extension in exts: - option, val = self.makeHistoryOption(dataset, sel, ',') + + if not datasets: + datasets = self.trans.get_history().active_datasets + + for dataset in datasets: + if exts is None or dataset.extension in exts: + option = self.makeHistoryOption(dataset, sel)[0] html += option return html @@ -160,13 +164,26 @@ def makeHistoryOption(self, dataset, select=None, sep=':'): html = '\n' % (val, selected(dataset.dataset_id, sel_id), dataset.hid, name, dataset.dbkey) return (html, val) - def getHistoryOptionId(self, select): + def getHistoryOptionSecureIdAndExt(self, select): if select and select.startswith('galaxy'): sep = select[6] if sep == ',': - id_sel = select.split(',')[1] + splitted = select.split(',') + id_sel = splitted[1] + ext = splitted[2] else: - id_sel = select.split(':')[2] + splitted = select.split(':') + id_sel = splitted[2] + ext = splitted[1] + else: + id_sel = 0 + ext = '' + return id_sel, ext + + def getHistoryOptionId(self, select): + id_sel = self.getHistoryOptionSecureIdAndExt(select)[0] + if id_sel: + sep = select[6] try: sel_id = self.decode_id(id_sel) except: @@ -174,8 +191,6 @@ def getHistoryOptionId(self, select): sel_id = int(id_sel) elif id_sel and os.path.exists(id_sel): sel_id = int(re.findall(r'/dataset_([0-9]+)\.dat', id_sel)[0]) - else: - sel_id = 0 else: sel_id = 0 return sel_id From 83e912fd2f4663f6099b9aac18c9374d5c4912f2 Mon Sep 17 00:00:00 2001 From: morj Date: Thu, 24 Nov 2016 08:55:06 +0100 Subject: [PATCH 012/123] ProTo: Removed HB code from StaticFile (cherry picked from commit c7ebc4d578b4f2a8bcd79b4ef80ea35b625886f2) --- lib/proto/StaticFile.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/proto/StaticFile.py b/lib/proto/StaticFile.py index 7de3ac6114ff..1819ece7451e 100644 --- a/lib/proto/StaticFile.py +++ b/lib/proto/StaticFile.py @@ -1,6 +1,6 @@ from proto.config.Config import STATIC_PATH, STATIC_REL_PATH from proto.CommonFunctions import ensurePathExists, getLoadToGalaxyHistoryURL, \ - extractNameFromDatasetInfo + extractNameFromDatasetInfo, extractIdFromGalaxyFn, getGalaxyFilesFilename from proto.HtmlCore import HtmlCore import os @@ -132,7 +132,6 @@ def __init__(self, id): StaticFile.__init__(self, ['images']+id) -from proto.CommonFunctions import extractIdFromGalaxyFn, getGalaxyFilesFilename class GalaxyRunSpecificFile(StaticFile): ''' Handles file path and URL of static (web-accessible) files which are specific @@ -162,10 +161,6 @@ def getURL(self): def getId(self): return extractIdFromGalaxyFn(self._galaxyFn) + self._relativeId - def getExternalTrackName(self): - name = extractNameFromDatasetInfo(self._galaxyFn) - return createStdTrackName(self.getId(), name) - class PickleStaticFile(StaticFile): def storePickledObject(self, obj): From f85fa5169815c89c905726b041bc41d14cbb6ed5 Mon Sep 17 00:00:00 2001 From: Morten Johansen Date: Thu, 24 Nov 2016 09:28:13 +0100 Subject: [PATCH 013/123] ProTo: Forgot to add the Security module some commits ago (cherry picked from commit 55a156a901625b4901e394bac19f42745603b80a) --- lib/proto/config/Security.py | 54 ++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 lib/proto/config/Security.py diff --git a/lib/proto/config/Security.py b/lib/proto/config/Security.py new file mode 100644 index 000000000000..e80cbed0c95e --- /dev/null +++ b/lib/proto/config/Security.py @@ -0,0 +1,54 @@ +# NB: imported by Galaxy (managers.hdas). Should not import other ProTo modules. +# This module will only be loaded once, during startup of Galaxy, and not dynamically by ProTo tools. +# This means changes in the module will not take effect until Galaxy is restarted. + +import os +from ConfigParser import SafeConfigParser + +GALAXY_BASE_DIR = os.path.abspath(os.path.dirname(__file__) + '/../../../.') + + +def getUniverseConfigParser(): + config = SafeConfigParser({'here': GALAXY_BASE_DIR}) + configRelFn = os.environ.get('GALAXY_CONFIG_FILE') + if not configRelFn: + configRelFn = 'config/galaxy.ini' + configFn = GALAXY_BASE_DIR + '/' + configRelFn + if os.path.exists(configFn): + config.read(configFn) + else: + raise Exception('No Galaxy config file found at path: ' + configFn) + return config + + +config = getUniverseConfigParser() + + +def getFromConfig(config, key, default, section='app:main'): + try: + return config.get(section, key) + except: + return default + + +def galaxyGetSecurityHelper(config): + from galaxy.web.security import SecurityHelper + + id_secret = getFromConfig(config, 'proto_id_secret', + 'USING THE DEFAULT IS ALSO NOT SECURE!', + section='galaxy_proto') + return SecurityHelper(id_secret=id_secret) + + +try: + GALAXY_SECURITY_HELPER_OBJ = galaxyGetSecurityHelper(config) +except: + GALAXY_SECURITY_HELPER_OBJ = None + + +def galaxySecureEncodeId(plainId): + return GALAXY_SECURITY_HELPER_OBJ.encode_id(plainId) + + +def galaxySecureDecodeId(encodedId): + return GALAXY_SECURITY_HELPER_OBJ.decode_id(str(encodedId)) From ba4231dff50d56904638a0817b1f888988ed6341 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 24 Nov 2016 11:35:40 +0100 Subject: [PATCH 014/123] ProTo: Moved config parsing to GalaxyConfigParser. Made sure dynamic reading of galaxy.ini is supported (cherry picked from commit 82aabe52a6f1a04b5e33bb71aef4dcd346726824) --- lib/proto/config/Config.py | 21 +++++++------- lib/proto/config/GalaxyConfigParser.py | 31 ++++++++++++++++++++ lib/proto/config/Security.py | 39 +++++--------------------- 3 files changed, 49 insertions(+), 42 deletions(-) create mode 100644 lib/proto/config/GalaxyConfigParser.py diff --git a/lib/proto/config/Config.py b/lib/proto/config/Config.py index 7c3de6245938..be59a0ec46a2 100644 --- a/lib/proto/config/Config.py +++ b/lib/proto/config/Config.py @@ -1,28 +1,29 @@ -from proto.config.Security import GALAXY_SECURITY_HELPER_OBJ, GALAXY_BASE_DIR, getFromConfig, getUniverseConfigParser +from proto.config.GalaxyConfigParser import GALAXY_BASE_DIR, GalaxyConfigParser def getUrlPrefix(config): - prefix = getFromConfig(config, 'prefix', '', 'filter:proxy-prefix') - filterWith = getFromConfig(config, 'filter-with', '', 'app:main') + prefix = config.getWithDefault('prefix', '', 'filter:proxy-prefix') + filterWith = config.getWithDefault('filter-with', '', 'app:main') return prefix if filterWith == 'proxy-prefix' else '' -config = getUniverseConfigParser() +config = GalaxyConfigParser() if not globals().get('URL_PREFIX'): URL_PREFIX = getUrlPrefix(config) -GALAXY_REL_TOOL_CONFIG_FILE = getFromConfig(config, 'tool_config_file', 'config/tool_conf.xml') +GALAXY_REL_TOOL_CONFIG_FILE = config.getWithDefault('tool_config_file', 'config/tool_conf.xml') ADMIN_USERS = [username.strip() for username in - getFromConfig(config, 'admin_users', '').split(',')] + config.getWithDefault('admin_users', '').split(',')] RESTRICTED_USERS = [username.strip() for username in - getFromConfig(config, 'restricted_users', '', 'galaxy_proto').split(',')] -OUTPUT_PRECISION = int(getFromConfig(config, 'output_precision', '4', 'galaxy_proto')) + config.getWithDefault('restricted_users', '', 'galaxy_proto').split(',')] +OUTPUT_PRECISION = int(config.getWithDefault('output_precision', '4', 'galaxy_proto')) STATIC_REL_PATH = URL_PREFIX + '/static/proto' STATIC_PATH = GALAXY_BASE_DIR + '/' + STATIC_REL_PATH GALAXY_URL = URL_PREFIX -GALAXY_FILE_PATH = GALAXY_BASE_DIR + '/' + getFromConfig(config, 'file_path', 'database/files') +GALAXY_FILE_PATH = GALAXY_BASE_DIR + '/' + config.getWithDefault('file_path', 'database/files') def userHasFullAccess(galaxyUserName): - return galaxyUserName in ADMIN_USERS + RESTRICTED_USERS if galaxyUserName not in [None, ''] else False + return galaxyUserName in ADMIN_USERS + RESTRICTED_USERS \ + if galaxyUserName not in [None, ''] else False diff --git a/lib/proto/config/GalaxyConfigParser.py b/lib/proto/config/GalaxyConfigParser.py new file mode 100644 index 000000000000..eda328839b7b --- /dev/null +++ b/lib/proto/config/GalaxyConfigParser.py @@ -0,0 +1,31 @@ +# NB: imported by Galaxy (managers.hdas). Should not import other ProTo modules. +# This module will only be loaded once, during startup of Galaxy, and not dynamically by ProTo +# tools. This means changes in the module will not take effect until Galaxy is restarted. + +import os +from ConfigParser import SafeConfigParser + + +GALAXY_BASE_DIR = os.path.abspath(os.path.dirname(__file__) + '/../../../.') + + +class GalaxyConfigParser(SafeConfigParser): + def __init__(self): + SafeConfigParser.__init__(self, {'here': GALAXY_BASE_DIR}) + + configRelFn = os.environ.get('GALAXY_CONFIG_FILE') + + if not configRelFn: + for configRelFn in ['config/galaxy.ini', 'config/galaxy.ini.sample']: + configFn = GALAXY_BASE_DIR + '/' + configRelFn + if os.path.exists(configFn): + self.read(configFn) + break + else: + raise Exception('No Galaxy config file found at path: ' + configFn) + + def getWithDefault(self, key, default, section='app:main'): + try: + return self.get(section, key) + except: + return default diff --git a/lib/proto/config/Security.py b/lib/proto/config/Security.py index e80cbed0c95e..d76752239ba1 100644 --- a/lib/proto/config/Security.py +++ b/lib/proto/config/Security.py @@ -1,42 +1,17 @@ # NB: imported by Galaxy (managers.hdas). Should not import other ProTo modules. -# This module will only be loaded once, during startup of Galaxy, and not dynamically by ProTo tools. -# This means changes in the module will not take effect until Galaxy is restarted. +# This module will only be loaded once, during startup of Galaxy, and not dynamically by ProTo +# tools. This means changes in the module will not take effect until Galaxy is restarted. -import os -from ConfigParser import SafeConfigParser - -GALAXY_BASE_DIR = os.path.abspath(os.path.dirname(__file__) + '/../../../.') - - -def getUniverseConfigParser(): - config = SafeConfigParser({'here': GALAXY_BASE_DIR}) - configRelFn = os.environ.get('GALAXY_CONFIG_FILE') - if not configRelFn: - configRelFn = 'config/galaxy.ini' - configFn = GALAXY_BASE_DIR + '/' + configRelFn - if os.path.exists(configFn): - config.read(configFn) - else: - raise Exception('No Galaxy config file found at path: ' + configFn) - return config - - -config = getUniverseConfigParser() - - -def getFromConfig(config, key, default, section='app:main'): - try: - return config.get(section, key) - except: - return default +from proto.config.GalaxyConfigParser import GalaxyConfigParser def galaxyGetSecurityHelper(config): from galaxy.web.security import SecurityHelper - id_secret = getFromConfig(config, 'proto_id_secret', - 'USING THE DEFAULT IS ALSO NOT SECURE!', - section='galaxy_proto') + config = GalaxyConfigParser() + id_secret = config.getWithDefault('proto_id_secret', + 'USING THE DEFAULT IS ALSO NOT SECURE!', + section='galaxy_proto') return SecurityHelper(id_secret=id_secret) From 797751879141fe6b43497111969a7f08e3b59d68 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 24 Nov 2016 12:07:04 +0100 Subject: [PATCH 015/123] ProTo: Fixed bug in GalaxyConfigParser in startup. (cherry picked from commit dc5a57af79c17ab8e7fbc1c5d83d1a4f220febdb) --- lib/proto/config/GalaxyConfigParser.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/proto/config/GalaxyConfigParser.py b/lib/proto/config/GalaxyConfigParser.py index eda328839b7b..91d5bccb3d6c 100644 --- a/lib/proto/config/GalaxyConfigParser.py +++ b/lib/proto/config/GalaxyConfigParser.py @@ -13,16 +13,16 @@ class GalaxyConfigParser(SafeConfigParser): def __init__(self): SafeConfigParser.__init__(self, {'here': GALAXY_BASE_DIR}) - configRelFn = os.environ.get('GALAXY_CONFIG_FILE') - - if not configRelFn: - for configRelFn in ['config/galaxy.ini', 'config/galaxy.ini.sample']: + for configRelFn in [os.environ.get('GALAXY_CONFIG_FILE'), + 'config/galaxy.ini', + 'config/galaxy.ini.sample']: + if configRelFn: configFn = GALAXY_BASE_DIR + '/' + configRelFn if os.path.exists(configFn): self.read(configFn) break - else: - raise Exception('No Galaxy config file found at path: ' + configFn) + else: + raise Exception('No Galaxy config file found at path: ' + configFn) def getWithDefault(self, key, default, section='app:main'): try: From e2f0d3e67ef1a3e0b61c7f42742731974049791f Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 24 Nov 2016 12:22:02 +0100 Subject: [PATCH 016/123] ProTo: Small bug (cherry picked from commit ccab8f34d292e9c727350361bec852e8e4927db5) --- lib/proto/config/Security.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/proto/config/Security.py b/lib/proto/config/Security.py index d76752239ba1..66b71dcf912b 100644 --- a/lib/proto/config/Security.py +++ b/lib/proto/config/Security.py @@ -5,7 +5,7 @@ from proto.config.GalaxyConfigParser import GalaxyConfigParser -def galaxyGetSecurityHelper(config): +def galaxyGetSecurityHelper(): from galaxy.web.security import SecurityHelper config = GalaxyConfigParser() @@ -16,7 +16,7 @@ def galaxyGetSecurityHelper(config): try: - GALAXY_SECURITY_HELPER_OBJ = galaxyGetSecurityHelper(config) + GALAXY_SECURITY_HELPER_OBJ = galaxyGetSecurityHelper() except: GALAXY_SECURITY_HELPER_OBJ = None From c695d6c454ed7016542156ba276b1489b6c3fec1 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 24 Nov 2016 16:44:10 +0100 Subject: [PATCH 017/123] ProTo: Removed unneeded config (cherry picked from commit 313a140feacce0625c95c93e3753dbf64561b4d8) --- lib/proto/config/Config.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/proto/config/Config.py b/lib/proto/config/Config.py index be59a0ec46a2..2048e96d7fe0 100644 --- a/lib/proto/config/Config.py +++ b/lib/proto/config/Config.py @@ -20,7 +20,6 @@ def getUrlPrefix(config): OUTPUT_PRECISION = int(config.getWithDefault('output_precision', '4', 'galaxy_proto')) STATIC_REL_PATH = URL_PREFIX + '/static/proto' STATIC_PATH = GALAXY_BASE_DIR + '/' + STATIC_REL_PATH -GALAXY_URL = URL_PREFIX GALAXY_FILE_PATH = GALAXY_BASE_DIR + '/' + config.getWithDefault('file_path', 'database/files') From 3b1153f6c0af8312fa429cee17fe5d6c68e7db7f Mon Sep 17 00:00:00 2001 From: Morten Johansen Date: Thu, 24 Nov 2016 16:58:26 +0100 Subject: [PATCH 018/123] ProTo: Fix load file to history bug (cherry picked from commit 3017c96c64d25d763d297aff64f03fed14d08440) --- lib/proto/galaxy_tool_classes.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/proto/galaxy_tool_classes.py b/lib/proto/galaxy_tool_classes.py index 142d37f0466f..3dcb1fd2dc16 100644 --- a/lib/proto/galaxy_tool_classes.py +++ b/lib/proto/galaxy_tool_classes.py @@ -23,9 +23,10 @@ def exec_before_job( self, app, inp_data, out_data, param_dict): for name, data in out_data.items(): if name == 'output': - ext = param_dict.get('datatype') + ext = param_dict.get('datatype', param_dict.get('format')) if ext: - data = app.datatypes_registry.change_datatype(data, ext) + data = app.datatypes_registry.change_datatype(data, ext) + param_dict['datatype'] = ext job_name = param_dict.get('job_name') if job_name: if data.name == self.name: From e231c703e6d17a663a2dd394dbd2c1a43aa14d1b Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Tue, 29 Nov 2016 13:23:35 +0100 Subject: [PATCH 019/123] ProTo: Updated config parsing with type handling (cherry picked from commit ce1642a49a3ab23bd070fada47b0cea7e1e6fde3) --- lib/proto/config/GalaxyConfigParser.py | 27 ++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/proto/config/GalaxyConfigParser.py b/lib/proto/config/GalaxyConfigParser.py index 91d5bccb3d6c..dd46a71fb9f7 100644 --- a/lib/proto/config/GalaxyConfigParser.py +++ b/lib/proto/config/GalaxyConfigParser.py @@ -2,6 +2,7 @@ # This module will only be loaded once, during startup of Galaxy, and not dynamically by ProTo # tools. This means changes in the module will not take effect until Galaxy is restarted. +import ast import os from ConfigParser import SafeConfigParser @@ -13,19 +14,33 @@ class GalaxyConfigParser(SafeConfigParser): def __init__(self): SafeConfigParser.__init__(self, {'here': GALAXY_BASE_DIR}) + self._configFn = None + for configRelFn in [os.environ.get('GALAXY_CONFIG_FILE'), 'config/galaxy.ini', 'config/galaxy.ini.sample']: if configRelFn: - configFn = GALAXY_BASE_DIR + '/' + configRelFn - if os.path.exists(configFn): - self.read(configFn) + self._configFn = GALAXY_BASE_DIR + '/' + configRelFn + if os.path.exists(self._configFn): + self.read(self._configFn) break else: - raise Exception('No Galaxy config file found at path: ' + configFn) + raise Exception('No Galaxy config file found at path: ' + self._configFn) def getWithDefault(self, key, default, section='app:main'): try: - return self.get(section, key) + val = self.get(section, key) + if not isinstance(default, basestring): + val = ast.literal_eval(val) except: - return default + val = default + + if type(val) != type(default): + raise ValueError('Value for configuration "%s" in section "%s" of file "%s" ' % + (key, section, self._configFn) + + 'is incorrect. The value "%s" is of different type ' % val + + 'than the default value "%s": %s != %s' % + (default, type(val), type(default))) + + return val + From d90407fffaee6758259fefc41d9149fb851f57ea Mon Sep 17 00:00:00 2001 From: Morten Johansen Date: Tue, 29 Nov 2016 15:50:45 +0100 Subject: [PATCH 020/123] ProTo: Added extra database flushes to hopefully reduce database errors. (cherry picked from commit 1b2749a0971719ce2fefcd75c9f991a143db3cfd) --- lib/galaxy/webapps/galaxy/controllers/proto.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/galaxy/webapps/galaxy/controllers/proto.py b/lib/galaxy/webapps/galaxy/controllers/proto.py index 9eb4083b87df..9d7c169ed288 100644 --- a/lib/galaxy/webapps/galaxy/controllers/proto.py +++ b/lib/galaxy/webapps/galaxy/controllers/proto.py @@ -49,6 +49,7 @@ def run_fork(self, response, trans, mako): exc_info = None html = '' #response.send_bytes('ping') + try: html, exc_info = self.run_tool(mako, trans) except Exception, e: @@ -60,6 +61,7 @@ def run_fork(self, response, trans, mako): response.send_bytes(html) response.close() + trans.sa_session.flush() def run_tool(self, mako, trans): toolController = None @@ -112,6 +114,7 @@ def index(self, trans, mako = 'generictool', **kwd): proc.terminate() log.warn('Fork did not exit, terminated.') + trans.sa_session.flush() return html From 6ad23d115851ae6439e8788e331f1125b82e8d02 Mon Sep 17 00:00:00 2001 From: Morten Johansen Date: Tue, 29 Nov 2016 15:51:50 +0100 Subject: [PATCH 021/123] ProTo: Handle empty histories. Move some methods to HyperBrowser. Cleanup (cherry picked from commit b22bb0df196313b923064a6a2a3f891ae00040f8) --- lib/proto/hyper_gui.py | 61 +++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/lib/proto/hyper_gui.py b/lib/proto/hyper_gui.py index 377ff283a3b4..4291d9fbe50e 100644 --- a/lib/proto/hyper_gui.py +++ b/lib/proto/hyper_gui.py @@ -94,34 +94,26 @@ def getDataFilePath(self, id): from proto.CommonFunctions import getGalaxyFnFromAnyDatasetId return getGalaxyFnFromAnyDatasetId(id) - def getHistory(self, ext, dbkey = None): + def getHistory(self, exts = None, dbkey = None): datasets = [] - for dataset in self.trans.get_history().active_datasets: - if dataset.visible and dataset.state == 'ok': - if dataset.extension in ext and (dbkey == None or dataset.dbkey == dbkey): - datasets.append(dataset) + if self.trans.get_history(): + for dataset in self.trans.get_history().active_datasets: + if dataset.visible and dataset.state == 'ok': + if exts is None or (dataset.extension in exts and (dbkey is None or dataset.dbkey == dbkey)) \ + or any([isinstance(dataset.datatype, ext) for ext in exts if isinstance(ext, type)]): + datasets.append(dataset) return datasets - def getValidRScripts(self): - datasets = [] - try: - for dataset in self.getHistory(['R']): - rfilename = self.getDataFilePath(dataset.dataset_id) - qid = '[scriptFn:=' + rfilename.encode('hex_codec') + ':] -> CustomRStat' - #if hyper.isRScriptValid(tracks[0].definition(), tracks[1].definition(), qid): - datasets.append(dataset) - except AttributeError: - pass - return datasets def optionsFromHistory(self, exts, sel=None, datasets=None): html = '' if not datasets: - datasets = self.trans.get_history().active_datasets + #datasets = self.trans.get_history().active_datasets + datasets = self.getHistory(exts) for dataset in datasets: - if exts is None or dataset.extension in exts: + #if exts is None or dataset.extension in exts: option = self.makeHistoryOption(dataset, sel)[0] html += option return html @@ -129,24 +121,24 @@ def optionsFromHistory(self, exts, sel=None, datasets=None): def optionsFromHistoryFn(self, exts = None, tools = None, select = None): html = '\n' vals = [None] - for dataset in self.trans.get_history().active_datasets: + for dataset in self.getHistory(exts): if tools: job = getJobFromDataset(dataset) tool_id = job.tool_id if job else None - if dataset.visible and dataset.state == 'ok': - if exts == None or dataset.extension in exts or any([isinstance(dataset.datatype, ext) for ext in exts if isinstance(ext, type)]): - if tools == None or tool_id in tools: - option, val = self.makeHistoryOption(dataset, select) - vals.append(val) - html += option + #if dataset.visible and dataset.state == 'ok': + # if exts == None or dataset.extension in exts or any([isinstance(dataset.datatype, ext) for ext in exts if isinstance(ext, type)]): + if tools is None or tool_id in tools: + option, val = self.makeHistoryOption(dataset, select) + vals.append(val) + html += option return html, vals def itemsFromHistoryFn(self, exts = None): items = OrderedDict() - for dataset in self.trans.get_history().active_datasets: - if (exts == None or dataset.extension in exts) and dataset.state == 'ok': - option_tag, val = self.makeHistoryOption(dataset) - items[str(dataset.dataset_id)] = val + for dataset in self.getHistory(exts): + #if (exts == None or dataset.extension in exts) and dataset.state == 'ok': + option_tag, val = self.makeHistoryOption(dataset) + items[str(dataset.dataset_id)] = val return items def makeHistoryOption(self, dataset, select=None, sep=':'): @@ -247,17 +239,6 @@ def disabled(opt, sel): def _disabled(opt, sel): return ' disabled="disabled" ' if opt != sel else '' -def optionListFromDatasets(datasets, exts = None, sel = None): - assert False, 'fix or remove me' - list = [] - for dataset in datasets: - if exts == None or dataset.extension in exts: - val = 'galaxy,' + str(dataset.dataset_id) + ',' + dataset.extension - txt = '%d: %s [%s]' % (dataset.hid, dataset.name, val) - tup = (txt, val, False) - list.append(tup) - return list - def joinAttrs(attrs): str = '' From b793ae5bd3d511591ad5bea6705127ca9a499333 Mon Sep 17 00:00:00 2001 From: Morten Johansen Date: Wed, 7 Dec 2016 12:59:42 +0100 Subject: [PATCH 022/123] ProTo: Attempted fix of the database exceptions that appeared after forking. Attempt to fully load history/dataset objects to avoid "lazy" loading from database in forked process (cherry picked from commit e15023908830a7842cacd2dfd6b94d9e689b00f6) --- .../webapps/galaxy/controllers/proto.py | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/lib/galaxy/webapps/galaxy/controllers/proto.py b/lib/galaxy/webapps/galaxy/controllers/proto.py index 9d7c169ed288..304c080c4ad1 100644 --- a/lib/galaxy/webapps/galaxy/controllers/proto.py +++ b/lib/galaxy/webapps/galaxy/controllers/proto.py @@ -27,10 +27,22 @@ from multiprocessing import Process, Pipe, Queue from importlib import import_module +#from sqlalchemy import create_engine, event, exc +#from sqlalchemy.orm.session import Session, sessionmaker +#from sqlalchemy.orm.scoping import scoped_session + + class ProtoController( BaseUIController ): def run_fork(self, response, trans, mako): + # this can close active connections in parent threads, since db server endpoint closes + #trans.sa_session.get_bind().dispose() + + # don't know why this has no effect + #engine = create_engine(trans.app.config.database_connection, pool_size=1) + #trans.app.model.context = scoped_session(sessionmaker(bind=engine, autoflush=False, autocommit=True)) + # logging locks and/or atexit handlers may be cause of deadlocks in a fork from thread # attempt to fix by shutting down and reloading logging module and clear exit handlers # logging.shutdown() @@ -61,7 +73,9 @@ def run_fork(self, response, trans, mako): response.send_bytes(html) response.close() - trans.sa_session.flush() + + #trans.sa_session.flush() + #engine.dispose() def run_tool(self, mako, trans): toolController = None @@ -82,6 +96,7 @@ def run_tool(self, mako, trans): @web.expose def index(self, trans, mako = 'generictool', **kwd): + if kwd.has_key('rerun_hda_id'): self._import_job_params(trans, kwd['rerun_hda_id']) @@ -91,10 +106,22 @@ def index(self, trans, mako = 'generictool', **kwd): retry = 3 while retry > 0: retry -= 1 - trans.sa_session.flush() my_end, your_end = Pipe() proc = Process(target=self.run_fork, args=(your_end,trans,str(mako))) + + #trans.sa_session.flush() + + # this avoids database exceptions in fork, but defies the point of having a + # connection pool + #trans.sa_session.get_bind().dispose() + + # attempt to fully load history/dataset objects to avoid "lazy" loading from + # database in forked process + if trans.get_history(): + for hda in trans.get_history().active_datasets: + _ = hda.visible, hda.state, hda.dbkey, hda.extension, hda.datatype + proc.start() html = '' if proc.is_alive(): @@ -114,7 +141,6 @@ def index(self, trans, mako = 'generictool', **kwd): proc.terminate() log.warn('Fork did not exit, terminated.') - trans.sa_session.flush() return html From eeb75418804c4d76b4468f31fb86189664cf6e5e Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Tue, 6 Dec 2016 23:16:58 +0100 Subject: [PATCH 023/123] ProTo: Fixing UniCode support, removing all type checks explicitly towards 'str'. (cherry picked from commit c7e2f16db4172d2dd45d47ce29751eb48ba9fcb7) --- lib/proto/OptionsBoxes.py | 4 ++-- lib/proto/RSetup.py | 2 +- lib/proto/generictool.py | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/proto/OptionsBoxes.py b/lib/proto/OptionsBoxes.py index a7b619ffbe20..88f2505b5180 100644 --- a/lib/proto/OptionsBoxes.py +++ b/lib/proto/OptionsBoxes.py @@ -28,10 +28,10 @@ def __init__(self, text, rows=1, readonly=False): @classmethod def isTypeOf(cls, opts): - if isinstance(opts, str) or isinstance(opts, unicode): + if isinstance(opts, basestring): return True if isinstance(opts, tuple): - if len(opts) in [2, 3] and (isinstance(opts[0], str) or isinstance(opts[0], unicode)): + if len(opts) in [2, 3] and (isinstance(opts[0], basestring)): return True return False diff --git a/lib/proto/RSetup.py b/lib/proto/RSetup.py index 181e7ac96ba4..a71b84c62c9d 100644 --- a/lib/proto/RSetup.py +++ b/lib/proto/RSetup.py @@ -12,7 +12,7 @@ numpy2ri.activate() def replaceNone(obj): - if isinstance(obj, str): + if isinstance(obj, basestring): return obj if isinstance(obj, collections.Iterable): diff --git a/lib/proto/generictool.py b/lib/proto/generictool.py index a14d6636ae19..913a921ee425 100644 --- a/lib/proto/generictool.py +++ b/lib/proto/generictool.py @@ -141,7 +141,7 @@ def _getIdxList(self, inputList): idxList = range(len(self.inputIds)) else: for i in inputList: - if isinstance(i, str): + if isinstance(i, basestring): try: idx = self.inputIds.index(i) except ValueError: @@ -156,7 +156,7 @@ def _getIdxList(self, inputList): return idxList def _getIdxForBoxId(self, i): - if isinstance(i, str): + if isinstance(i, basestring): try: idx = self.inputIds.index(i) except ValueError: @@ -285,7 +285,7 @@ def action(self): values[k] = bool(self.params.get(id + '|' + k , False) if val else v) val = values - elif isinstance(opts, str) or isinstance(opts, unicode): + elif isinstance(opts, basestring): if opts == '__genome__': self.inputTypes += ['__genome__'] try: @@ -347,7 +347,7 @@ def action(self): val = opts[1] #elif val: # val = unquote(val) - elif len(opts) in [2, 3] and (isinstance(opts[0], str) or isinstance(opts[0], unicode)): + elif len(opts) in [2, 3] and (isinstance(opts[0], basestring)): if len(opts) == 2: opts = opts + (False,) if isinstance(opts[1], int): @@ -500,8 +500,8 @@ def execute(self): ChoiceTuple = namedtuple('ChoiceTuple', self.inputIds) choices = ChoiceTuple._make(self.inputValues) - #batchargs = '|'.join([';'.join(c.itervalues()) if not isinstance(c, str) else c for c in choices]) - #batchargs = '|'.join([repr(c.items()) if not isinstance(c, str) else c for c in choices]) + #batchargs = '|'.join([';'.join(c.itervalues()) if not isinstance(c, basestring) else c for c in choices]) + #batchargs = '|'.join([repr(c.items()) if not isinstance(c, basestring) else c for c in choices]) #print choices if outputFormat == 'html': From d5ffa53de4455d160a478a37a4a3f6f056eecc70 Mon Sep 17 00:00:00 2001 From: Morten Johansen Date: Tue, 6 Dec 2016 23:21:38 +0100 Subject: [PATCH 024/123] Misc bugfixes and cleanup (cherry picked from commit f74fd3e01f18522d08305be916383df2fde69598) --- lib/proto/CommonFunctions.py | 14 ++++++++++---- lib/proto/StaticFile.py | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/proto/CommonFunctions.py b/lib/proto/CommonFunctions.py index 5e27c3a75a19..79c90d596fa8 100644 --- a/lib/proto/CommonFunctions.py +++ b/lib/proto/CommonFunctions.py @@ -183,7 +183,11 @@ def createGalaxyFilesFn(galaxyFn, filename): def extractFnFromDatasetInfo(datasetInfo): if isinstance(datasetInfo, basestring): datasetInfo = datasetInfo.split(':') - return getGalaxyFnFromEncodedDatasetId(datasetInfo[2]) + try: + return getGalaxyFnFromEncodedDatasetId(datasetInfo[2]) + except TypeError: + # full path, not id + return datasetInfo[2] def extractFileSuffixFromDatasetInfo(datasetInfo, fileSuffixFilterList=None): @@ -217,7 +221,7 @@ def createGalaxyToolURL(toolId, **kwArgs): ''.join(['&' + urllib.quote(key) + '=' + urllib.quote(value) for key,value in kwArgs.iteritems()]) -def getLoadToGalaxyHistoryURL(fn, genome='hg18', galaxyDataType='bed', urlPrefix=None): +def getLoadToGalaxyHistoryURL(fn, genome='', galaxyDataType='bed', urlPrefix=None): if urlPrefix is None: from proto.config.Config import URL_PREFIX urlPrefix = URL_PREFIX @@ -225,8 +229,10 @@ def getLoadToGalaxyHistoryURL(fn, genome='hg18', galaxyDataType='bed', urlPrefix import base64 assert galaxyDataType is not None - return urlPrefix + '/tool_runner?tool_id=file_import&dbkey=%s&runtool_btn=yes&input=' % (genome,) \ - + base64.urlsafe_b64encode(fn) + ('&format=' + galaxyDataType if galaxyDataType is not None else '') + return urlPrefix + '/tool_runner?tool_id=file_import' + \ + ('&dbkey=' + genome if genome else '') + \ + '&runtool_btn=yes&input=' + base64.urlsafe_b64encode(fn) + \ + ('&format=' + galaxyDataType if galaxyDataType is not None else '') def strWithStdFormatting(val, separateThousands=True, floatFormatFlag='g'): diff --git a/lib/proto/StaticFile.py b/lib/proto/StaticFile.py index 1819ece7451e..6ea796d8d0f3 100644 --- a/lib/proto/StaticFile.py +++ b/lib/proto/StaticFile.py @@ -41,7 +41,7 @@ def getEmbeddedImage(self): def getLoadToHistoryLink(self, linkText, galaxyDataType='bed'): return str(HtmlCore().link(linkText, getLoadToGalaxyHistoryURL - (self.getDiskPath(), galaxyDataType))) + (self.getDiskPath(), galaxyDataType=galaxyDataType))) def openRFigure(self, h=600, w=800): from proto.RSetup import r From 40b05f5558eaea03f45a20c6ec7dd8a20d55ae6d Mon Sep 17 00:00:00 2001 From: Morten Johansen Date: Wed, 7 Dec 2016 13:02:21 +0100 Subject: [PATCH 025/123] ProTo: Small code cleanups (cherry picked from commit 5adef2455ff1f44f4dd0ca1d90b9d40840d8cf82) --- lib/proto/hyper_gui.py | 13 ++----------- templates/proto/generictool.mako | 1 - 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/lib/proto/hyper_gui.py b/lib/proto/hyper_gui.py index 4291d9fbe50e..9ba216cf80ea 100644 --- a/lib/proto/hyper_gui.py +++ b/lib/proto/hyper_gui.py @@ -83,12 +83,10 @@ def __init__(self, trans): def encode_id(self, id): from proto.config.Security import galaxySecureEncodeId return galaxySecureEncodeId(id) - # return self.trans.security.encode_id(id) def decode_id(self, id): from proto.config.Security import galaxySecureDecodeId return galaxySecureDecodeId(id) - # return self.trans.security.decode_id(id) def getDataFilePath(self, id): from proto.CommonFunctions import getGalaxyFnFromAnyDatasetId @@ -104,18 +102,15 @@ def getHistory(self, exts = None, dbkey = None): datasets.append(dataset) return datasets - def optionsFromHistory(self, exts, sel=None, datasets=None): html = '' if not datasets: - #datasets = self.trans.get_history().active_datasets datasets = self.getHistory(exts) for dataset in datasets: - #if exts is None or dataset.extension in exts: - option = self.makeHistoryOption(dataset, sel)[0] - html += option + option = self.makeHistoryOption(dataset, sel)[0] + html += option return html def optionsFromHistoryFn(self, exts = None, tools = None, select = None): @@ -125,8 +120,6 @@ def optionsFromHistoryFn(self, exts = None, tools = None, select = None): if tools: job = getJobFromDataset(dataset) tool_id = job.tool_id if job else None - #if dataset.visible and dataset.state == 'ok': - # if exts == None or dataset.extension in exts or any([isinstance(dataset.datatype, ext) for ext in exts if isinstance(ext, type)]): if tools is None or tool_id in tools: option, val = self.makeHistoryOption(dataset, select) vals.append(val) @@ -136,7 +129,6 @@ def optionsFromHistoryFn(self, exts = None, tools = None, select = None): def itemsFromHistoryFn(self, exts = None): items = OrderedDict() for dataset in self.getHistory(exts): - #if (exts == None or dataset.extension in exts) and dataset.state == 'ok': option_tag, val = self.makeHistoryOption(dataset) items[str(dataset.dataset_id)] = val return items @@ -147,7 +139,6 @@ def makeHistoryOption(self, dataset, select=None, sep=':'): sel_id = self.getHistoryOptionId(select) if sep == ',': - #vals = ['galaxy', self.encode_id(dataset.dataset_id), dataset.extension, self.makeHistoryOptionName(dataset)] vals = ['galaxy', self.encode_id(dataset.dataset_id), dataset.extension] else: vals = ['galaxy', dataset.extension, self.encode_id(dataset.dataset_id), self.makeHistoryOptionName(dataset)] diff --git a/templates/proto/generictool.mako b/templates/proto/generictool.mako index ec9e5cf6c09b..ed21e882eb3b 100644 --- a/templates/proto/generictool.mako +++ b/templates/proto/generictool.mako @@ -16,7 +16,6 @@ # along with The Genomic HyperBrowser. If not, see . import sys, os, traceback, json -import cPickle as pickle from zlib import compress from base64 import urlsafe_b64encode from cgi import escape From 12343ab0740d8fad8faed2e4eb93b4e829ef371a Mon Sep 17 00:00:00 2001 From: Morten Johansen Date: Wed, 21 Dec 2016 02:14:43 +0100 Subject: [PATCH 026/123] ProTo: support for checkbox paragraph id for CSS (cherry picked from commit 3cd0778419b0e8f24067f0fffddb86525f8b3eb8) --- templates/proto/functions.mako | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/proto/functions.mako b/templates/proto/functions.mako index 9a524ed20b2d..19019753b053 100644 --- a/templates/proto/functions.mako +++ b/templates/proto/functions.mako @@ -55,7 +55,7 @@ import proto.hyper_gui as gui <%def name="checkbox(name, opts, value, label = None, info = None)"> -

+

${staticInfoBox(name, info)}

From 2fd00c4925dc6a574c745526d53be942f9e00390 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Wed, 21 Dec 2016 02:18:19 +0100 Subject: [PATCH 027/123] ProTo: Checkbox bugfix (cherry picked from commit 2829900a258b7a0f83f7d2e9f0ac14d984c302e6) --- templates/proto/generictool.mako | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/proto/generictool.mako b/templates/proto/generictool.mako index ed21e882eb3b..6ec47c61089c 100644 --- a/templates/proto/generictool.mako +++ b/templates/proto/generictool.mako @@ -56,7 +56,7 @@ else: %elif control.inputTypes[i] == 'multi': ${functions.multichoice(control.inputIds[i], control.options[i], control.inputValues[i], control.inputNames[i], info=control.inputInfo[i])} %elif control.inputTypes[i] == 'checkbox': - ${functions.checkbox(control.inputIds[i], control.options[i], control.displayValues[i], control.inputNames[i], info=control.inputInfo[i])} + ${functions.checkbox(control.inputIds[i], control.options[i], control.inputValues[i], control.inputNames[i], info=control.inputInfo[i])} %elif control.inputTypes[i] == 'text': ${functions.text(control.inputIds[i], control.displayValues[i], control.inputNames[i], control.options[i][1], readonly=False, reload=control.prototype.isDynamic(), info=control.inputInfo[i])} %elif control.inputTypes[i] == 'text_readonly': From fe7ff46ee69e9f0124733e8f289a22971c4a555d Mon Sep 17 00:00:00 2001 From: Morten Johansen Date: Wed, 21 Dec 2016 02:52:55 +0100 Subject: [PATCH 028/123] ProTo: Added getOutputName to GeneralGuiTool, defining the title of the main output element (cherry picked from commit 01485db3abe005c16fc8b700284114970dd6593c) --- lib/proto/tools/GeneralGuiTool.py | 10 ++++++++-- lib/proto/tools/ToolTemplate.py | 10 ++++++++++ lib/proto/tools/ToolTemplateMinimal.py | 4 ++++ static/proto/html/ToolTemplate.html | 4 ++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/proto/tools/GeneralGuiTool.py b/lib/proto/tools/GeneralGuiTool.py index 9eb91e63fbe8..a1e32360df0e 100644 --- a/lib/proto/tools/GeneralGuiTool.py +++ b/lib/proto/tools/GeneralGuiTool.py @@ -55,8 +55,10 @@ def getResetBoxes(): def getInputBoxOrder(): return None - @staticmethod - def getInputBoxGroups(choices=None): + @classmethod + def getInputBoxGroups(cls, choices=None): + if hasattr(super(GeneralGuiTool, cls), 'getInputBoxGroups'): + return super(GeneralGuiTool, cls).getInputBoxGroups(choices) return None @staticmethod @@ -79,6 +81,10 @@ def isDebugMode(): def getOutputFormat(choices=None): return 'html' + @classmethod + def getOutputName(cls, choices=None): + return cls.getToolSelectionName() + @staticmethod def validateAndReturnErrors(choices): return None diff --git a/lib/proto/tools/ToolTemplate.py b/lib/proto/tools/ToolTemplate.py index 7a87ff319758..2cbf80e60db4 100644 --- a/lib/proto/tools/ToolTemplate.py +++ b/lib/proto/tools/ToolTemplate.py @@ -379,3 +379,13 @@ def validateAndReturnErrors(cls, choices): # 'html' # """ # return 'html' + # + # @classmethod + # def getOutputName(cls, choices=None): + # return cls.getToolSelectionName() + # """ + # The title (name) of the main output history element. + # + # Optional method. Default return value if method is not defined: + # the name of the tool. + # """ diff --git a/lib/proto/tools/ToolTemplateMinimal.py b/lib/proto/tools/ToolTemplateMinimal.py index 4fd355e6905a..ce981d1349af 100644 --- a/lib/proto/tools/ToolTemplateMinimal.py +++ b/lib/proto/tools/ToolTemplateMinimal.py @@ -94,3 +94,7 @@ def validateAndReturnErrors(cls, choices): # @classmethod # def getOutputFormat(cls, choices): # return 'html' + # + # @classmethod + # def getOutputName(cls, choices=None): + # return cls.getToolSelectionName() diff --git a/static/proto/html/ToolTemplate.html b/static/proto/html/ToolTemplate.html index a3e161bcf231..ecd8416eead9 100644 --- a/static/proto/html/ToolTemplate.html +++ b/static/proto/html/ToolTemplate.html @@ -230,6 +230,10 @@ Optional method. Default return value if method is not defined:
'html' +
getOutputName(cls, choices) from __builtin__.type
The title (name) of the main output history element.
+
+Optional method. Default return value if method is not defined: the name of the tool.
+
getRedirectURL(cls, choices) from __builtin__.type
This method is called to return an URL if the isRedirectTool method
returns True.
 
From e1a40325f9490bc2fba832ac3f81867ee432bf46 Mon Sep 17 00:00:00 2001 From: Morten Johansen Date: Wed, 21 Dec 2016 02:57:09 +0100 Subject: [PATCH 029/123] ProTo: Added secure encoding to cached choices (in hidden field) to close a security hole related to Pickle. Forgot fix for getOutputName() to work (cherry picked from commit 4e7dfe0053facd307cf2f2fde1e68b82cddb65f4) --- lib/proto/generictool.py | 44 +++++++++++++++++++++++++------- templates/proto/generictool.mako | 7 ++--- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/lib/proto/generictool.py b/lib/proto/generictool.py index 913a921ee425..716c2a8f18a1 100644 --- a/lib/proto/generictool.py +++ b/lib/proto/generictool.py @@ -25,13 +25,16 @@ from proto.tools.GeneralGuiTool import HistElement from proto.HtmlCore import HtmlCore from proto.config.Config import URL_PREFIX, GALAXY_BASE_DIR +from proto.config.Security import galaxySecureEncodeId, galaxySecureDecodeId, GALAXY_SECURITY_HELPER_OBJ from BaseToolController import BaseToolController from proto.CommonFunctions import getToolPrototype from proto.StaticFile import StaticImage + def getClassName(obj): return obj.__class__.__name__ + class GenericToolController(BaseToolController): initChoicesDict = None @@ -205,26 +208,47 @@ def _getOptionsBox(self, i, val = None): def _initCache(self): self.input_changed = False try: - self.cachedParams = json.loads(decompress(urlsafe_b64decode(str(self.params.get('cached_params'))))) - except: + self.cachedParams = self.decodeCache(self.params.get('cached_params')) + except Exception as e: + #print 'cached_params', e self.cachedParams = {} try: - self.cachedOptions = json.loads(decompress(urlsafe_b64decode(str(self.params.get('cached_options'))))) - except: + self.cachedOptions = self.decodeCache(self.params.get('cached_options')) + except Exception as e: + #print 'cached_options', e self.cachedOptions = {} try: - self.cachedExtra = json.loads(decompress(urlsafe_b64decode(str(self.params.get('cached_extra'))))) - except: + self.cachedExtra = self.decodeCache(self.params.get('cached_extra')) + except Exception as e: + #print 'cached_extra', e self.cachedExtra = {} + def encodeCache(self, data): + if not data: + return '' + return GALAXY_SECURITY_HELPER_OBJ.encode_guid(json.dumps(data)) + #return urlsafe_b64encode(compress(json.dumps(data))) + + def decodeCache(self, data): + if not data: + raise Exception('Nothing to decode') + return json.loads(GALAXY_SECURITY_HELPER_OBJ.decode_guid(str(data))) + #return json.loads(decompress(urlsafe_b64decode(str(data)))) + def putCacheData(self, id, data): self.cachedExtra[id] = pickle.dumps(data) def getCacheData(self, id): return pickle.loads(str(self.cachedExtra[id])) + def putCachedOption(self, id, data): + self.cachedOptions[id] = pickle.dumps(data) + + def getCachedOption(self, id): + return pickle.loads(str(self.cachedOptions[id])) + def getOptionsBox(self, id, i, val): #print id, '=', val, 'cache=', self.cachedParams[id] if id in self.cachedParams else 'NOT' @@ -233,7 +257,7 @@ def getOptionsBox(self, id, i, val): self.input_changed = True else: try: - opts, info = pickle.loads(str(self.cachedOptions[id])) + opts, info = self.getCachedOption(id) #print 'from cache:',id self.input_changed = (val != self.cachedParams[id]) except Exception as e: @@ -243,7 +267,7 @@ def getOptionsBox(self, id, i, val): #print repr(opts) self.cachedParams[id] = val - self.cachedOptions[id] = pickle.dumps((opts, info)) + self.putCachedOption(id, (opts, info)) self.inputInfo.append(info) return opts @@ -291,8 +315,10 @@ def action(self): try: genomeCache = self.getCacheData(id) except Exception as e: - print 'genome cache error', e + #raise e + print 'genome cache empty', e genomeCache = self._getAllGenomes() + #print genomeCache self.putCacheData(id, genomeCache) opts = self.getGenomeElement(id, genomeCache) diff --git a/templates/proto/generictool.mako b/templates/proto/generictool.mako index 6ec47c61089c..db67ae0db739 100644 --- a/templates/proto/generictool.mako +++ b/templates/proto/generictool.mako @@ -84,11 +84,12 @@ else:
- - - + + + + From 16ab031ec497faf332b51b7ff7d02c0fa664062c Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Wed, 21 Dec 2016 03:01:37 +0100 Subject: [PATCH 030/123] ProTo: Misc. fixed to support ProTo - HyperBrowser divide. Increased ProTo timeout. (cherry picked from commit f4e28d0ba610d3b369bd93f13a737e73f5290860) --- lib/galaxy/webapps/galaxy/controllers/proto.py | 5 +++-- lib/proto/CommonFunctions.py | 16 ++++++++++++++++ lib/proto/StaticFile.py | 9 ++++++--- lib/proto/generictool.py | 6 ++++-- lib/proto/hyper_gui.py | 15 ++------------- 5 files changed, 31 insertions(+), 20 deletions(-) diff --git a/lib/galaxy/webapps/galaxy/controllers/proto.py b/lib/galaxy/webapps/galaxy/controllers/proto.py index 304c080c4ad1..95bbd5a2e538 100644 --- a/lib/galaxy/webapps/galaxy/controllers/proto.py +++ b/lib/galaxy/webapps/galaxy/controllers/proto.py @@ -103,6 +103,7 @@ def index(self, trans, mako = 'generictool', **kwd): if isinstance(mako, list): mako = mako[0] + timeout = 30 retry = 3 while retry > 0: retry -= 1 @@ -125,13 +126,13 @@ def index(self, trans, mako = 'generictool', **kwd): proc.start() html = '' if proc.is_alive(): - if my_end.poll(15): + if my_end.poll(timeout): #ping = my_end.recv_bytes() html = my_end.recv_bytes() my_end.close() break else: - log.warn('Fork timed out after 15 sec. Retrying...') + log.warn('Fork timed out after %d sec. Retrying...' % (timeout,)) else: log.warn('Fork died on startup.') break diff --git a/lib/proto/CommonFunctions.py b/lib/proto/CommonFunctions.py index 79c90d596fa8..93eb9a391685 100644 --- a/lib/proto/CommonFunctions.py +++ b/lib/proto/CommonFunctions.py @@ -210,6 +210,22 @@ def extractNameFromDatasetInfo(datasetInfo): return unquote(datasetInfo[-1]) +def getSecureIdAndExtFromDatasetInfoAsStr(datasetInfo): + if datasetInfo and datasetInfo.startswith('galaxy'): + sep = datasetInfo[6] + if sep == ',': + splitted = datasetInfo.split(',') + id_sel = splitted[1] + ext = splitted[2] + else: + splitted = datasetInfo.split(':') + id_sel = splitted[2] + ext = splitted[1] + else: + id_sel = 0 + ext = '' + return id_sel, ext + def createToolURL(toolId, **kwArgs): from proto.tools.GeneralGuiTool import GeneralGuiTool return GeneralGuiTool.createGenericGuiToolURL(toolId, tool_choices=kwArgs) diff --git a/lib/proto/StaticFile.py b/lib/proto/StaticFile.py index 6ea796d8d0f3..85fc266addeb 100644 --- a/lib/proto/StaticFile.py +++ b/lib/proto/StaticFile.py @@ -5,18 +5,21 @@ import os class StaticFile(object): + STATIC_PATH = STATIC_PATH + STATIC_REL_PATH = STATIC_REL_PATH + def __init__(self, id): #assert id[0] in ['files','images','run_specific'], 'Only a restricted set of first elements of id is supported, in order to have control of phyical storage locations. ID: '+str(id) assert id[0] in ['files','images'], 'Only a restricted set of first elements of id is supported, in order to have control of phyical storage locations. ID: '+str(id) self._id = id def getDiskPath(self, ensurePath=False): - fn = os.sep.join([STATIC_PATH] + self._id) + fn = os.sep.join([self.STATIC_PATH] + self._id) if ensurePath: ensurePathExists(fn) return fn - def getFile(self,mode='w'): + def getFile(self, mode='w'): fn = self.getDiskPath(True) return open(fn,mode) @@ -29,7 +32,7 @@ def writeTextToFile(self, text, mode='w'): f.close() def getURL(self): - return os.sep.join([STATIC_REL_PATH] + self._id) + return os.sep.join([self.STATIC_REL_PATH] + self._id) def getLink(self, linkText): return str(HtmlCore().link(linkText, self.getURL())) diff --git a/lib/proto/generictool.py b/lib/proto/generictool.py index 716c2a8f18a1..a4ced74e75c6 100644 --- a/lib/proto/generictool.py +++ b/lib/proto/generictool.py @@ -469,12 +469,14 @@ def decodeChoice(self, opts, id, choice): return choice + @staticmethod + def _getStdOutToHistoryDatatypes(): + return ['html', 'customhtml'] def execute(self): outputFormat = self.params['datatype'] if self.params.has_key('datatype') else 'html' - if outputFormat in ['html','customhtml','hbfunction']: + if outputFormat in self._getStdOutToHistoryDatatypes(): self.stdoutToHistory() - #print self.params for i in range(len(self.inputIds)): id = self.inputIds[i] diff --git a/lib/proto/hyper_gui.py b/lib/proto/hyper_gui.py index 9ba216cf80ea..9cf1cf8a8ff4 100644 --- a/lib/proto/hyper_gui.py +++ b/lib/proto/hyper_gui.py @@ -148,19 +148,8 @@ def makeHistoryOption(self, dataset, select=None, sep=':'): return (html, val) def getHistoryOptionSecureIdAndExt(self, select): - if select and select.startswith('galaxy'): - sep = select[6] - if sep == ',': - splitted = select.split(',') - id_sel = splitted[1] - ext = splitted[2] - else: - splitted = select.split(':') - id_sel = splitted[2] - ext = splitted[1] - else: - id_sel = 0 - ext = '' + from proto.CommonFunctions import getSecureIdAndExtFromDatasetInfoAsStr + id_sel, ext = getSecureIdAndExtFromDatasetInfoAsStr(select) return id_sel, ext def getHistoryOptionId(self, select): From b5325fcb20ea33f2498f354458940bf1134912f2 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Wed, 21 Dec 2016 03:02:50 +0100 Subject: [PATCH 031/123] ProTo: Added support in Explore and Install tools for excluding subtools of MultiGenericGuiTool from the tool list. (cherry picked from commit 0ed590ce977b1e87469688c7394eaff671de0a8a) --- lib/proto/tools/ManageTools.py | 62 +++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/lib/proto/tools/ManageTools.py b/lib/proto/tools/ManageTools.py index 1fb79c2e909b..824d747654d5 100644 --- a/lib/proto/tools/ManageTools.py +++ b/lib/proto/tools/ManageTools.py @@ -59,41 +59,62 @@ def createToolXml(self, tool_fn, tool_id, tool_name, tool_module, tool_cls, tool def getProtoToolList(except_class_names=[]): except_class_names.append('ToolTemplate') + except_class_names.append('ToolTemplateMinimal') + tmp_tools = {} tools = {} tool_classes = [] + all_sub_classes = set() pys = [] for d in os.walk(PROTO_TOOL_DIR, followlinks=True): if d[0].find('.svn') == -1: pys += [os.path.join(d[0], f) for f in d[2] if f.endswith('.py') and not any(f.startswith(x) for x in ['.', '#'])] - - #print 'Num py', len(pys) + + # To fix import issue if there are modules in /lib and /lib/proto with the + # same name (e.g. 'config'). + tmpSysPath = sys.path + if sys.path[0].endswith('/proto'): + sys.path = tmpSysPath[1:] + + #print 'Num py', len(pys) for fn in pys: with open(fn) as f: for line in f: m = re.match(r'class +(\w+) *\((\w+)\)', line) if m: class_name = m.group(1) - if class_name not in except_class_names: - module_name = os.path.splitext(os.path.relpath(os.path.abspath(fn), SOURCE_CODE_BASE_DIR))[0].replace(os.path.sep, '.') - # print module_name - try: + module_name = os.path.splitext(os.path.relpath(os.path.abspath(fn), SOURCE_CODE_BASE_DIR))[0].replace(os.path.sep, '.') + try: + if module_name != __name__: module = import_module(module_name) prototype_cls = getattr(module, class_name) - if issubclass(prototype_cls, GeneralGuiTool) and not issubclass(prototype_cls, MultiGeneralGuiTool) and hasattr(prototype_cls, 'getToolName'): - prototype = prototype_cls('hb_no_tool_id_yet') - toolModule = module_name.split('.')[2:] - if class_name != toolModule[-1]: - toolSelectionName = '.'.join(toolModule) + ' [' + class_name + ']' - else: - toolSelectionName = '.'.join(toolModule) - - # print (fn, m.group(2), prototype_cls, module_name) - tools[toolSelectionName] = (fn, m.group(2), prototype_cls, module_name) - tool_classes.append(prototype_cls) - except Exception as e: - traceback.print_exc() + if issubclass(prototype_cls, GeneralGuiTool): + if issubclass(prototype_cls, MultiGeneralGuiTool): + if prototype_cls.getSubToolClasses(): + for sub_cls in prototype_cls.getSubToolClasses(): + all_sub_classes.add(sub_cls) + elif hasattr(prototype_cls, 'getToolName'): + if class_name not in except_class_names: + prototype = prototype_cls('hb_no_tool_id_yet') + tool_module = module_name.split('.')[2:] + if class_name != tool_module[-1]: + tool_selection_name = '.'.join(tool_module) + ' [' + class_name + ']' + else: + tool_selection_name = '.'.join(tool_module) + + # print (fn, m.group(2), prototype_cls, module_name) + tmp_tools[tool_selection_name] = (fn, m.group(2), prototype_cls, module_name) + except Exception as e: + traceback.print_exc() #break #print 'Num protopy', len(tools) + + for tool_selection_name, tool_info in tmp_tools.iteritems(): + prototype_cls = tool_info[2] + if prototype_cls not in all_sub_classes: + tools[tool_selection_name] = tool_info + tool_classes.append(prototype_cls) + + sys.path = tmpSysPath return tools, tool_classes @@ -113,7 +134,8 @@ def useSubToolPrefix(): @classmethod def getSubToolClasses(cls): tool_shelve = shelve.open(TOOL_SHELVE, 'r') - installed_classes = [tool_shelve.get(t)[1] for t in tool_shelve.keys() if os.path.exists(os.path.join(SOURCE_CODE_BASE_DIR, tool_shelve.get(t)[0].replace('.', os.path.sep)) + '.py') ] + installed_classes = [tool_shelve.get(t)[1] for t in tool_shelve.keys() + if os.path.exists(os.path.join(SOURCE_CODE_BASE_DIR, tool_shelve.get(t)[0].replace('.', os.path.sep)) + '.py') ] tool_shelve.close() tool_list = getProtoToolList(installed_classes)[1] return sorted(tool_list, key=lambda c: c.__module__) From ff90fd902bf1dc35efee714811e5d6fc5be799ca Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Wed, 21 Dec 2016 04:15:57 +0100 Subject: [PATCH 032/123] ProTo: Created proto.less -> proto.css for ProTo related CSS changes (cherry picked from commit 8e7c36caa4a2c9cf4da51413f6e645f1dac30991) --- client/galaxy/style/less/proto.less | 181 ++++++++++++++++++++++++++++ client/grunt-tasks/style.js | 6 +- static/proto/style/base.css | 43 ------- static/style/blue/proto | 2 +- static/style/blue/proto.css | 1 + templates/proto/base.mako | 124 +------------------ 6 files changed, 187 insertions(+), 170 deletions(-) create mode 100644 client/galaxy/style/less/proto.less delete mode 100644 static/proto/style/base.css create mode 100644 static/style/blue/proto.css diff --git a/client/galaxy/style/less/proto.less b/client/galaxy/style/less/proto.less new file mode 100644 index 000000000000..ab45d31ea2cd --- /dev/null +++ b/client/galaxy/style/less/proto.less @@ -0,0 +1,181 @@ +// morj: override inline-block with inline so that relative width of input boxes +// relates to parent block +label { + font-weight: normal; + display: inline !important; +} + +div.toolFormTitle { + font-weight: bold !important; +} + +.toolForm > .toolFormBody { + padding: 10px; +} + +.showInfo { + cursor: pointer; +} + +.hideInfo { + cursor: pointer; +} + +.hidden { + display: none; +} + +fieldset { + padding: 8px; + margin-top: 6px; + margin-bottom: 6px; +} + +textarea { + margin-top: 6px; + margin-bottom: 6px; +} + +input { + margin-top: 6px; + margin-bottom: 6px; +} + +.genome { + float:left; + margin-top:10px; + margin-bottom:10px +} + +.option { +} + +a { + text-decoration: none; +} + +a:hover { + text-decoration: none; +} + +a:visited { + text-decoration: underline; +} + +a:link { + text-decoration: underline; +} + +a.help { + font-weight: bold; + text-decoration: none; + font-size: 15px; +} + +div.help { + display: none; + b__order: dashed 1px; + padding: 2px; + background-image: none; +} + +div.toolHelp { + margin-top: 0px; +} + +hr { + margin-top: 6px; + margin-bottom: 6px; +} + +hr.space { + margin-top: 15px; + margin-bottom: 15px; +} + +a.option { + cursor: pointer; + display: block; + text-decoration: none; + padding: 2px; + width: 99%; +} + +a.option:hover { + background-color: #006; + color: #fff; +} + +a.selected { + border: solid 1px #006; +} + +div.options { + width: 90%; + height: auto; + overflow: auto; + display: none; + position: absolute; + border: 2px outset; + background-color: #fff; +} + +.infomessagesmall { + margin: 5px; +} + +.invisible { + visibility: hidden; +} + +.options label { + display: block; +} + +#_stats_text { + font-weight: bold; +} + +td { + padding: 20px; + white-space: normal; +} + +table { + max-width: 100%; + text-align: center; + margin-left: auto; + margin-right: auto; + table-layout:auto; + word-wrap:break-word; + margin-top: 20px; + margin-bottom: 20px; +} + +h4 { + padding-top: 20px; + text-align: center; +} + +p { + margin: 12px 0 12px; +} + +#__disabled { + display:none; + position:absolute; + top:0px; + left:0px; + width: 100%; + height: 100%; + opacity: 0.50; + filter:alpha(opacity=50); + z-Index:1000; + background-color: #EEEEEE; + background-image: url("proto/images/rays.gif"); + background-repeat: no-repeat; + background-attachment: fixed; + background-position: center center; + background-size: 32px; +} + diff --git a/client/grunt-tasks/style.js b/client/grunt-tasks/style.js index 484a5ac151ff..cc794e9f408f 100644 --- a/client/grunt-tasks/style.js +++ b/client/grunt-tasks/style.js @@ -9,7 +9,7 @@ module.exports = function( grunt ){ var _ = grunt.util._, fmt = _.sprintf, theme = grunt.option( 'theme', 'blue' ), - styleDistPath = '../static/style/blue', + styleDistPath = '../static/style/' + theme, imagesPath = '../static/images', lessPath = './galaxy/style/less', lessFiles = [ @@ -17,11 +17,11 @@ module.exports = function( grunt ){ 'autocomplete_tagging', 'embed_item', 'iphone', - 'masthead', 'library', 'trackster', 'circster', - 'reports' + 'reports', + 'proto' ]; diff --git a/static/proto/style/base.css b/static/proto/style/base.css deleted file mode 100644 index 553024ebb5e5..000000000000 --- a/static/proto/style/base.css +++ /dev/null @@ -1,43 +0,0 @@ -label { - font-weight: normal; - display: inline !important; -} - -div.toolFormTitle { - font-weight: bold !important; -} - -.toolForm > .toolFormBody { - padding: 10px; -} - -div.debug { - font-size: 90%; - font-family: monospace; - white-space: pre; -} - - -/* -table.colored th.header { - background: url('form_title_bg.png') repeat-x scroll center top / auto 100% #FFE899; - border-bottom: 1px solid #CC9E00; -} - -table.bordered td, table.bordered th { - font-size: inherit; - border: 1px solid #CC9E00; -} - -legend { - font-size: (@font-size-base * 1.2); - margin-bottom: 4px; - width: auto; - padding: auto; - border-bottom: 0; -} - -fieldset { - border: 1px solid #C0C0C0; -} -*/ \ No newline at end of file diff --git a/static/style/blue/proto b/static/style/blue/proto index 138e9b87723a..b943f9399110 120000 --- a/static/style/blue/proto +++ b/static/style/blue/proto @@ -1 +1 @@ -../../proto/style \ No newline at end of file +../../proto \ No newline at end of file diff --git a/static/style/blue/proto.css b/static/style/blue/proto.css new file mode 100644 index 000000000000..fecc0518cac2 --- /dev/null +++ b/static/style/blue/proto.css @@ -0,0 +1 @@ +label{font-weight:normal;display:inline !important}div.toolFormTitle{font-weight:bold !important}.toolForm>.toolFormBody{padding:10px}.showInfo{cursor:pointer}.hideInfo{cursor:pointer}.hidden{display:none}fieldset{padding:8px;margin-top:6px;margin-bottom:6px}textarea{margin-top:6px;margin-bottom:6px}input{margin-top:6px;margin-bottom:6px}.genome{float:left;margin-top:10px;margin-bottom:10px}a{text-decoration:none}a:hover{text-decoration:none}a:visited{text-decoration:underline}a:link{text-decoration:underline}a.help{font-weight:bold;text-decoration:none;font-size:15px}div.help{display:none;b__order:dashed 1px;padding:2px;background-image:none}div.toolHelp{margin-top:0}hr{margin-top:6px;margin-bottom:6px}hr.space{margin-top:15px;margin-bottom:15px}a.option{cursor:pointer;display:block;text-decoration:none;padding:2px;width:99%}a.option:hover{background-color:#006;color:#fff}a.selected{border:solid 1px #006}div.options{width:90%;height:auto;overflow:auto;display:none;position:absolute;border:2px outset;background-color:#fff}.infomessagesmall{margin:5px}.invisible{visibility:hidden}.options label{display:block}#_stats_text{font-weight:bold}td{padding:20px;white-space:normal}table{max-width:100%;text-align:center;margin-left:auto;margin-right:auto;table-layout:auto;word-wrap:break-word;margin-top:20px;margin-bottom:20px}h4{padding-top:20px;text-align:center}p{margin:12px 0 12px}#__disabled{display:none;position:absolute;top:0;left:0;width:100%;height:100%;opacity:.5;filter:alpha(opacity=50);z-Index:1000;background-color:#EEEEEE;background-image:url("proto/images/rays.gif");background-repeat:no-repeat;background-attachment:fixed;background-position:center center;background-size:32px} \ No newline at end of file diff --git a/templates/proto/base.mako b/templates/proto/base.mako index 1aa761909229..62d7b91f3797 100644 --- a/templates/proto/base.mako +++ b/templates/proto/base.mako @@ -23,129 +23,7 @@ <%def name="stylesheets()"> ${h.css('base')} - ${h.css('proto/base')} - + ${h.css('proto')} <%def name="javascripts()"> From 8e2b91f187c791b4c533d351b2d74b722885749a Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 22 Dec 2016 13:22:26 +0100 Subject: [PATCH 033/123] ProTo: Fixed bug in list of uninstalled tools. Refactored install tool output printing. (cherry picked from commit ecbab997edeb8332d6c9851599190cf76473f44a) --- lib/proto/HtmlCore.py | 4 +++ lib/proto/tools/ManageTools.py | 47 +++++++++++++++++++++------------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/lib/proto/HtmlCore.py b/lib/proto/HtmlCore.py index 0ceb12a1422a..e1a482688c24 100644 --- a/lib/proto/HtmlCore.py +++ b/lib/proto/HtmlCore.py @@ -124,6 +124,10 @@ def emphasize(self, text): self._str += '' + text + '' return self + def preformatted(self, text): + self._str += '
' + text + '
' + return self + def tableHeader(self, headerRow, tagRow=None, firstRow=True, sortable=False, tableId=None, tableClass='colored bordered', headerClass='header', style='table-layout:auto;word-wrap:break-word;', **kwargs): diff --git a/lib/proto/tools/ManageTools.py b/lib/proto/tools/ManageTools.py index 824d747654d5..1304d9d55ec3 100644 --- a/lib/proto/tools/ManageTools.py +++ b/lib/proto/tools/ManageTools.py @@ -79,7 +79,7 @@ def getProtoToolList(except_class_names=[]): for fn in pys: with open(fn) as f: for line in f: - m = re.match(r'class +(\w+) *\((\w+)\)', line) + m = re.match(r'class +(\w+) *\(([\w ,]+)\)', line) if m: class_name = m.group(1) module_name = os.path.splitext(os.path.relpath(os.path.abspath(fn), SOURCE_CODE_BASE_DIR))[0].replace(os.path.sep, '.') @@ -314,23 +314,30 @@ def execute(cls, choices, galaxyFn=None, username=''): toolConf.write() - print ''' - - ''' - print 'Reload toolbox/menu' - print '
' + escape(xml) + '
' + '
' + escape(tool_xml) + '
' + from proto.HtmlCore import HtmlCore + core = HtmlCore() + + extraJavaScriptCode = ''' + + ''' + core.begin(extraJavaScriptCode=extraJavaScriptCode) + core.link('Reload toolbox/menu', url='#', args='id="reload_toolbox"') + core.preformatted(escape(xml)) + core.preformatted(escape(tool_xml)) + core.end() + print>>open(galaxyFn, 'w'), core @staticmethod def getToolDescription(): @@ -389,6 +396,10 @@ def getToolDescription(): "tool hierarchy.", emphasize=True) return str(core) + @staticmethod + def getOutputFormat(choices): + return 'customhtml' + class GenerateToolsTool(GeneralGuiTool): @staticmethod From 27d9187c216859601213a070fb43e1bf429e57e2 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 22 Dec 2016 13:24:03 +0100 Subject: [PATCH 034/123] ProTo: Added support for history output naming in load to history URL function (cherry picked from commit 680c900918ae06a49cb5593d90528f450569e279) --- lib/proto/CommonFunctions.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/proto/CommonFunctions.py b/lib/proto/CommonFunctions.py index 93eb9a391685..869a01104f8b 100644 --- a/lib/proto/CommonFunctions.py +++ b/lib/proto/CommonFunctions.py @@ -237,7 +237,8 @@ def createGalaxyToolURL(toolId, **kwArgs): ''.join(['&' + urllib.quote(key) + '=' + urllib.quote(value) for key,value in kwArgs.iteritems()]) -def getLoadToGalaxyHistoryURL(fn, genome='', galaxyDataType='bed', urlPrefix=None): +def getLoadToGalaxyHistoryURL(fn, genome='', galaxyDataType='bed', urlPrefix=None, + histElementName=None): if urlPrefix is None: from proto.config.Config import URL_PREFIX urlPrefix = URL_PREFIX @@ -248,7 +249,8 @@ def getLoadToGalaxyHistoryURL(fn, genome='', galaxyDataType='bed', urlPrefix=Non return urlPrefix + '/tool_runner?tool_id=file_import' + \ ('&dbkey=' + genome if genome else '') + \ '&runtool_btn=yes&input=' + base64.urlsafe_b64encode(fn) + \ - ('&format=' + galaxyDataType if galaxyDataType is not None else '') + ('&format=' + galaxyDataType if galaxyDataType is not None else '') + \ + ('&job_name=' + histElementName if histElementName is not None else '') def strWithStdFormatting(val, separateThousands=True, floatFormatFlag='g'): @@ -294,9 +296,20 @@ def _strIsFloat(s): return False +def isNan(a): + import numpy + + try: + return numpy.isnan(a) + except (TypeError, NotImplementedError): + return False + + def forceNumericSortingKey(key): sortKey1 = 0 sortKey2 = key + if isNan(key): + return [sortKey1, sortKey2] if _strIsFloat(str(key).replace(THOUSANDS_SEPARATOR, '')): sortKey1 = 1 sortKey2 = float(str(key).replace(THOUSANDS_SEPARATOR, '')) From 34f43f5b99cf922019c5d804bce11df2a22389b1 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 22 Dec 2016 19:18:52 +0100 Subject: [PATCH 035/123] ProTo: fixed import bugs (in particular that proto.config is imported instead of config). Former-commit-id: 6e7dc620c78172fdc1199d84ebadb284b92e97d7 (cherry picked from commit 6913872b68bb25876603fda41fc323768b433d01) --- lib/proto/protoToolExecute.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/proto/protoToolExecute.py b/lib/proto/protoToolExecute.py index f070cd41b2d9..8347c5e4c5c3 100644 --- a/lib/proto/protoToolExecute.py +++ b/lib/proto/protoToolExecute.py @@ -18,5 +18,6 @@ from proto.generictool import getController +sys.path = sys.path[1:] # to remove the '/proto' directory from the Python path getController(None, sys.argv[1]).execute() From d4005613d1a860c4cda7f41f9d8e2e88ee1f529a Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 22 Dec 2016 19:21:14 +0100 Subject: [PATCH 036/123] ProTo: changed tool ids into underscore. Made tool_type selectable. Former-commit-id: 6e88edd0b8ce7caaddeeb1dc6f3b8ca1f4c046af (cherry picked from commit 7b85acc7e7e2dc178549a50e18330f63cb3d422c) --- .../dependencies/proto-requirements.txt | 1 + lib/proto/tools/ExploreToolsTool.xml | 5 +++- lib/proto/tools/GenerateToolsTool.xml | 2 +- lib/proto/tools/InstallToolsTool.xml | 2 +- lib/proto/tools/ManageTools.py | 25 ++++++++++++++----- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/lib/galaxy/dependencies/proto-requirements.txt b/lib/galaxy/dependencies/proto-requirements.txt index fdd0321c0277..6878d495ded3 100644 --- a/lib/galaxy/dependencies/proto-requirements.txt +++ b/lib/galaxy/dependencies/proto-requirements.txt @@ -1,4 +1,5 @@ # Python package requirements of Galaxy ProTo +inflection>=0.3.1 # Optional packages rpy2>=2.7 diff --git a/lib/proto/tools/ExploreToolsTool.xml b/lib/proto/tools/ExploreToolsTool.xml index 005e9a3a502b..d622ec8f49be 100644 --- a/lib/proto/tools/ExploreToolsTool.xml +++ b/lib/proto/tools/ExploreToolsTool.xml @@ -1 +1,4 @@ - + + + diff --git a/lib/proto/tools/GenerateToolsTool.xml b/lib/proto/tools/GenerateToolsTool.xml index c7b5ffc9bd19..77dbda20e4c7 100644 --- a/lib/proto/tools/GenerateToolsTool.xml +++ b/lib/proto/tools/GenerateToolsTool.xml @@ -1,4 +1,4 @@ - diff --git a/lib/proto/tools/InstallToolsTool.xml b/lib/proto/tools/InstallToolsTool.xml index d7725fbef785..acbc67e41f78 100644 --- a/lib/proto/tools/InstallToolsTool.xml +++ b/lib/proto/tools/InstallToolsTool.xml @@ -1,4 +1,4 @@ - diff --git a/lib/proto/tools/ManageTools.py b/lib/proto/tools/ManageTools.py index 1304d9d55ec3..9737ec54b26f 100644 --- a/lib/proto/tools/ManageTools.py +++ b/lib/proto/tools/ManageTools.py @@ -20,7 +20,7 @@ class GalaxyToolConfig: tool_xml_template = ''' + tool_type="%s_generic" proto_tool_module="%s" proto_tool_class="%s"> %s \n''' @@ -52,8 +52,9 @@ def write(self): with open(self.tool_conf_fn, 'w') as f: f.write(self.tool_conf_data) - def createToolXml(self, tool_fn, tool_id, tool_name, tool_module, tool_cls, tool_descr): - tool_xml = self.tool_xml_template % (tool_id, tool_name, tool_module, tool_cls, tool_descr) + def createToolXml(self, tool_fn, tool_id, tool_name, tool_type, tool_module, tool_cls, tool_descr): + tool_xml = self.tool_xml_template % (tool_id, tool_name, tool_type, + tool_module, tool_cls, tool_descr) return tool_xml @@ -220,6 +221,7 @@ def getToolName(): @staticmethod def getInputBoxNames(): return [('Select tool', 'tool'), + ('Tool type', 'toolType'), ('Tool ID', 'toolID'), ('Tool name', 'name'), ('Tool description', 'description'), @@ -228,7 +230,7 @@ def getInputBoxNames(): @staticmethod def getResetBoxes(): - return [1] + return [1, 2] # @staticmethod # def isHistoryTool(): @@ -243,12 +245,19 @@ def getOptionsBoxTool(cls): tool_list = cls._getToolList() return ['-- Select tool --'] + sorted(tool_list) + @classmethod + def getOptionsBoxToolType(cls, prevchoices): + return ['proto'] + @classmethod def getOptionsBoxToolID(cls, prevchoices): + import inflection if prevchoices.tool is None or prevchoices.tool.startswith('--'): return '' tool_list = cls._getToolList() - return 'ProTo_' + tool_list[prevchoices.tool][2].__name__ + module_name = tool_list[prevchoices.tool][2].__name__ + return prevchoices.toolType + '_' + inflection.underscore(module_name) + @classmethod def getOptionsBoxName(cls, prevchoices): @@ -300,9 +309,13 @@ def execute(cls, choices, galaxyFn=None, username=''): tool_cls = choices.tool prototype = cls._getProtoType(choices.tool) tool_file = choices.toolXMLPath + tool_type = choices.toolType toolConf = GalaxyToolConfig() xml = toolConf.addTool(choices.section, tool_file) - tool_xml = toolConf.createToolXml(tool_file, choices.toolID, choices.name, prototype.__module__, prototype.__class__.__name__, choices.description) + tool_xml = toolConf.createToolXml(tool_file, choices.toolID, + choices.name, prototype.__module__, + prototype.__class__.__name__, + choices.description) abs_tool_xml_path = GALAXY_TOOL_XML_PATH + choices.toolXMLPath try: From e4b3a48ad6f4df81f72dc8aa10968cd0f121deae Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 23 Feb 2017 12:20:53 +0100 Subject: [PATCH 037/123] ProTo: refactored run_tool method in ProTo controller to support for providing usable error messages with exceptions. Also supports subclass overloading of mako template and tool controller paths, as well as mako template filling. All of these are needed by the HyperBrowser controller (cherry picked from commit 56a4b9b0e78a5767c477e606a2eb1707c647b29a) --- .../webapps/galaxy/controllers/proto.py | 72 ++++++++++++------- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/lib/galaxy/webapps/galaxy/controllers/proto.py b/lib/galaxy/webapps/galaxy/controllers/proto.py index 95bbd5a2e538..79e65900f350 100644 --- a/lib/galaxy/webapps/galaxy/controllers/proto.py +++ b/lib/galaxy/webapps/galaxy/controllers/proto.py @@ -16,17 +16,19 @@ from __future__ import absolute_import -import sys, os - -from galaxy.web.base.controller import web, error, BaseUIController import logging - -log = logging.getLogger( __name__ ) - +import os +import pkgutil import traceback + from multiprocessing import Process, Pipe, Queue from importlib import import_module +from galaxy.web.base.controller import web, error, BaseUIController + + +log = logging.getLogger( __name__ ) + #from sqlalchemy import create_engine, event, exc #from sqlalchemy.orm.session import Session, sessionmaker #from sqlalchemy.orm.scoping import scoped_session @@ -63,13 +65,11 @@ def run_fork(self, response, trans, mako): #response.send_bytes('ping') try: - html, exc_info = self.run_tool(mako, trans) + html = self.run_tool(mako, trans) except Exception, e: html = '
\n'
-            if exc_info:
-               html += str(e) + ':\n' + exc_info + '\n\n'
-#               html += str(e) + ':\n' + ''.join(traceback.format_exception(exc_info[0],exc_info[1],exc_info[2])) + '\n\n'
-            html += str(e) + ':\n' + traceback.format_exc() + '\n
' + html += str(e) + ':\n' + traceback.format_exc() + html += '\n' response.send_bytes(html) response.close() @@ -77,25 +77,45 @@ def run_fork(self, response, trans, mako): #trans.sa_session.flush() #engine.dispose() + @staticmethod + def _convert_mako_from_rel_to_abs(mako): + return '/proto/' + mako + + @staticmethod + def _get_controller_module_name(rel_mako): + return 'proto.' + rel_mako + + def _parse_mako_filename(self, mako): + if not mako.startswith('/'): + rel_mako = mako + mako = self._convert_mako_from_rel_to_abs(mako) + else: + rel_mako = mako.split('/')[-1] + + controller_module_name = self._get_controller_module_name(rel_mako) + template_mako = mako + '.mako' + return controller_module_name, template_mako + + @staticmethod + def _fill_mako_template(template_mako, tool_controller, trans): + return trans.fill_template(template_mako, trans=trans, control=tool_controller) + def run_tool(self, mako, trans): - toolController = None - exc_info = None - try: - toolModule = import_module('proto.' + mako) - toolController = toolModule.getController(trans) - except Exception, e: - # print e - exc_info = sys.exc_info() - if mako.startswith('/'): - template_mako = mako + '.mako' - html = trans.fill_template(template_mako, trans=trans, control=toolController) + controller_module_name, template_mako = self._parse_mako_filename(mako) + + controller_loader = pkgutil.find_loader(controller_module_name) + if controller_loader: + tool_module = import_module(controller_module_name) + tool_controller = tool_module.getController(trans) else: - template_mako = '/proto/' + mako + '.mako' - html = trans.fill_template(template_mako, trans=trans, control=toolController) - return html, exc_info + tool_controller = None + + html = self._fill_mako_template(template_mako, tool_controller, trans) + + return html @web.expose - def index(self, trans, mako = 'generictool', **kwd): + def index(self, trans, mako='generictool', **kwd): if kwd.has_key('rerun_hda_id'): self._import_job_params(trans, kwd['rerun_hda_id']) From 3e967978f7eb420b8e02efa85cfa69a40ef74adb Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 23 Dec 2016 09:22:39 +0100 Subject: [PATCH 038/123] ProTo: Correct list elements Former-commit-id: 2974d32051d253de6f7a516c3c94625be9118aaf (cherry picked from commit a7b92b74c05623069b9648e5d463638e41531cbe) --- lib/proto/HtmlCore.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/proto/HtmlCore.py b/lib/proto/HtmlCore.py index e1a482688c24..dd1f0a4bf14c 100644 --- a/lib/proto/HtmlCore.py +++ b/lib/proto/HtmlCore.py @@ -237,7 +237,7 @@ def formEnd(self): def unorderedList(self, strList): self._str += '
    ' for s in strList: - self._str += '
  • %s' % s + self._str += '
  • %s
  • ' % s self._str += '
' return self From d7b1023d8feb68e382cefc06d7a74d24021f4462 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 6 Jan 2017 09:44:49 +0100 Subject: [PATCH 039/123] ProTo: Fix to also show all subtools of uninstalled multitools (cherry picked from commit 80f4c268ce4e2176757a081273bb5a3b05313844) --- lib/proto/tools/ManageTools.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/proto/tools/ManageTools.py b/lib/proto/tools/ManageTools.py index 9737ec54b26f..053f2a084a03 100644 --- a/lib/proto/tools/ManageTools.py +++ b/lib/proto/tools/ManageTools.py @@ -64,7 +64,7 @@ def getProtoToolList(except_class_names=[]): tmp_tools = {} tools = {} tool_classes = [] - all_sub_classes = set() + all_installed_sub_classes = set() pys = [] for d in os.walk(PROTO_TOOL_DIR, followlinks=True): if d[0].find('.svn') == -1: @@ -90,9 +90,9 @@ def getProtoToolList(except_class_names=[]): prototype_cls = getattr(module, class_name) if issubclass(prototype_cls, GeneralGuiTool): if issubclass(prototype_cls, MultiGeneralGuiTool): - if prototype_cls.getSubToolClasses(): + if class_name in except_class_names and prototype_cls.getSubToolClasses(): for sub_cls in prototype_cls.getSubToolClasses(): - all_sub_classes.add(sub_cls) + all_installed_sub_classes.add(sub_cls) elif hasattr(prototype_cls, 'getToolName'): if class_name not in except_class_names: prototype = prototype_cls('hb_no_tool_id_yet') @@ -111,7 +111,7 @@ def getProtoToolList(except_class_names=[]): for tool_selection_name, tool_info in tmp_tools.iteritems(): prototype_cls = tool_info[2] - if prototype_cls not in all_sub_classes: + if prototype_cls not in all_installed_sub_classes: tools[tool_selection_name] = tool_info tool_classes.append(prototype_cls) From 8e3ceb3581cdffb0b2c3b0e8a98cf90c5fdbe928 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 6 Jan 2017 15:55:04 +0100 Subject: [PATCH 040/123] ProTo: Added "upload file" tool link methods (cherry picked from commit d6a29e303fdae5f3905354a79a971c02ebef01d7) --- lib/proto/CommonFunctions.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/proto/CommonFunctions.py b/lib/proto/CommonFunctions.py index 869a01104f8b..4d92681e2dc6 100644 --- a/lib/proto/CommonFunctions.py +++ b/lib/proto/CommonFunctions.py @@ -233,10 +233,17 @@ def createToolURL(toolId, **kwArgs): def createGalaxyToolURL(toolId, **kwArgs): from proto.config.Config import URL_PREFIX + if toolId == 'upload1': + return "javascript:void(0)" return URL_PREFIX + '/tool_runner?tool_id=' + toolId + \ ''.join(['&' + urllib.quote(key) + '=' + urllib.quote(value) for key,value in kwArgs.iteritems()]) +def getGalaxyUploadLinkOnclick(): + return "event.preventDefault(); jQuery('#tool-panel-upload-button', " \ + "window.parent.jQuery(window.parent.document)).trigger('click');" + + def getLoadToGalaxyHistoryURL(fn, genome='', galaxyDataType='bed', urlPrefix=None, histElementName=None): if urlPrefix is None: From 091a7c60904e7f1ba24ac25e982dd1aae33dd39f Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Mon, 9 Jan 2017 14:28:51 +0100 Subject: [PATCH 041/123] ProTo: FIxed bug in job rerun in older browser versions (e.g. Opera 12) due to use of new javascript function "startsWith". (cherry picked from commit c138d2d843d97def03f0a136f7a7f17d66b3fd70) --- client/galaxy/scripts/mvc/dataset/dataset-li-edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/galaxy/scripts/mvc/dataset/dataset-li-edit.js b/client/galaxy/scripts/mvc/dataset/dataset-li-edit.js index 936a1508889b..edcdf1077252 100644 --- a/client/galaxy/scripts/mvc/dataset/dataset-li-edit.js +++ b/client/galaxy/scripts/mvc/dataset/dataset-li-edit.js @@ -173,7 +173,7 @@ var DatasetListItemEdit = _super.extend( var form = new ToolForm.View({ 'job_id' : creating_job }); form.deferred.execute( function(){ // console.log(form.options); - if (form.options.model_class.startsWith('Proto')) + if (form.options.model_class.lastIndexOf('Proto', 0) === 0) galaxy_main.location = rerun_url; else Galaxy.app.display( form ); From f83111dc1f464cba670b1956538740d98136c1ee Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Wed, 11 Jan 2017 12:02:54 +0100 Subject: [PATCH 042/123] Fixed bug with sorting of collapsed tables. (cherry picked from commit 35b39d1072a970e775e4d226e4cec4defa0b9972) --- lib/proto/TableCoreMixin.py | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/lib/proto/TableCoreMixin.py b/lib/proto/TableCoreMixin.py index 65ff3d92c6cc..ecdc378aabc4 100644 --- a/lib/proto/TableCoreMixin.py +++ b/lib/proto/TableCoreMixin.py @@ -92,6 +92,7 @@ def tableExpandButton(self, tableId, totalRows, visibleRows=6): $(tblId).find("tr").show(); btnDivId = "#toggle_table_" + tableId; $(btnDivId).find("input").toggle(); + $(tblId).off("click"); } function collapseTable(tableId, visibleRows) { @@ -100,13 +101,36 @@ def tableExpandButton(self, tableId, totalRows, visibleRows=6): $(trScltr).hide(); btnDivId = "#toggle_table_" + tableId; $(btnDivId).find("input").toggle(); + $(tblId).on("click", resetFunc(tableId, visibleRows)); +} + +var resetFunc = function(tableId, visibleRows) { + return function(e) { + return resetTable(e, tableId, visibleRows); + } +} + +function resetTable(e, tableId, visibleRows) { + expandTable(tableId) + collapseTable(tableId, visibleRows) } $(document).ready(function(){ - hiddenRowsSlctr = "table.expandable tr:nth-child(n + %s)"; - $(hiddenRowsSlctr).hide(); + tableId = "%s"; + visibleRows = %s; + tblId = "#" + tableId; + hiddenRowsSlctr = tblId + " tr:nth-child(n + " + (visibleRows+2) + ")"; + // 'visibleRows+2' for some reason (one of life's great mysteries) + if ($(hiddenRowsSlctr).length>0) { + $(hiddenRowsSlctr).hide(); + $(tblId).on("click", resetFunc(tableId, visibleRows+1)); + // 'visibleRows+1' for some other reason (one of life's other great mysteries) + + } } -);''' % str(visibleRows + 2)) # '+2' for some reason (one of life's great mysteries) +); + +''' % (tableId, visibleRows)) self._str += '''
From 7cd4e61784da2b089bc15f3834138f5c4fb088c9 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 12 Jan 2017 22:06:07 +0100 Subject: [PATCH 043/123] ProTo: "Load to history" file paths are now relative to the Galaxy base dir (cherry picked from commit 2e4c1606d1a5c981432eec0ef756b592a433e76d) --- lib/proto/StaticFile.py | 21 +++++++++++++++------ lib/proto/config/Config.py | 5 +++-- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/lib/proto/StaticFile.py b/lib/proto/StaticFile.py index 85fc266addeb..a317d9716f68 100644 --- a/lib/proto/StaticFile.py +++ b/lib/proto/StaticFile.py @@ -1,10 +1,12 @@ -from proto.config.Config import STATIC_PATH, STATIC_REL_PATH +from proto.config.Config import GALAXY_BASE_DIR, STATIC_DIR, STATIC_PATH, STATIC_REL_PATH from proto.CommonFunctions import ensurePathExists, getLoadToGalaxyHistoryURL, \ extractNameFromDatasetInfo, extractIdFromGalaxyFn, getGalaxyFilesFilename from proto.HtmlCore import HtmlCore import os + class StaticFile(object): + STATIC_DIR = STATIC_DIR STATIC_PATH = STATIC_PATH STATIC_REL_PATH = STATIC_REL_PATH @@ -13,8 +15,9 @@ def __init__(self, id): assert id[0] in ['files','images'], 'Only a restricted set of first elements of id is supported, in order to have control of phyical storage locations. ID: '+str(id) self._id = id - def getDiskPath(self, ensurePath=False): - fn = os.sep.join([self.STATIC_PATH] + self._id) + def getDiskPath(self, ensurePath=False, relativeToBase=False): + path = self.STATIC_DIR if relativeToBase else self.STATIC_PATH + fn = os.sep.join([path] + self._id) if ensurePath: ensurePathExists(fn) return fn @@ -44,7 +47,8 @@ def getEmbeddedImage(self): def getLoadToHistoryLink(self, linkText, galaxyDataType='bed'): return str(HtmlCore().link(linkText, getLoadToGalaxyHistoryURL - (self.getDiskPath(), galaxyDataType=galaxyDataType))) + (self.getDiskPath(relativeToBase=True), + galaxyDataType=galaxyDataType))) def openRFigure(self, h=600, w=800): from proto.RSetup import r @@ -150,8 +154,13 @@ def __init__(self, id, galaxyFn): #galaxyId = galaxyFn if type(galaxyFn) in (list,tuple) else extractIdFromGalaxyFn(galaxyFn) #StaticFile.__init__(self, getUniqueRunSpecificId(galaxyId + id)) - def getDiskPath(self, ensurePath=False): - fn = getGalaxyFilesFilename(self._galaxyFn, self._relativeId) + def getDiskPath(self, ensurePath=False, relativeToBase=False): + path = self._galaxyFn + if relativeToBase: + if path.startswith(GALAXY_BASE_DIR): + path = path[len(GALAXY_BASE_DIR):] + + fn = getGalaxyFilesFilename(path, self._relativeId) #fn = os.sep.join([GALAXY_FILE_PATH] + [self._id[1], 'dataset_'+self._id[2]+'_files'] + self._id[3:]) if ensurePath: ensurePathExists(fn) diff --git a/lib/proto/config/Config.py b/lib/proto/config/Config.py index 2048e96d7fe0..6cdebc0e83c3 100644 --- a/lib/proto/config/Config.py +++ b/lib/proto/config/Config.py @@ -18,8 +18,9 @@ def getUrlPrefix(config): RESTRICTED_USERS = [username.strip() for username in config.getWithDefault('restricted_users', '', 'galaxy_proto').split(',')] OUTPUT_PRECISION = int(config.getWithDefault('output_precision', '4', 'galaxy_proto')) -STATIC_REL_PATH = URL_PREFIX + '/static/proto' -STATIC_PATH = GALAXY_BASE_DIR + '/' + STATIC_REL_PATH +STATIC_DIR = '/static/proto' +STATIC_REL_PATH = URL_PREFIX + STATIC_DIR +STATIC_PATH = GALAXY_BASE_DIR + STATIC_DIR GALAXY_FILE_PATH = GALAXY_BASE_DIR + '/' + config.getWithDefault('file_path', 'database/files') From b94ef448e98faefe08320b2af1fbe2fa603dafd1 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 12 Jan 2017 22:59:16 +0100 Subject: [PATCH 044/123] ProTo: Moved FileImport tool from HyperBrowser code. Updated to only support encoding for strict security. (cherry picked from commit 8f6c91a64d1f4f72048b3e1d32971a92757bb715) --- lib/proto/CommonFunctions.py | 6 +- lib/proto/tools/FileImport.py | 342 +++++++++++++++++++++++++++++++++ lib/proto/tools/FileImport.xml | 4 + 3 files changed, 350 insertions(+), 2 deletions(-) create mode 100644 lib/proto/tools/FileImport.py create mode 100644 lib/proto/tools/FileImport.xml diff --git a/lib/proto/CommonFunctions.py b/lib/proto/CommonFunctions.py index 4d92681e2dc6..eba64cf313b7 100644 --- a/lib/proto/CommonFunctions.py +++ b/lib/proto/CommonFunctions.py @@ -22,7 +22,8 @@ from proto.CommonConstants import THOUSANDS_SEPARATOR from proto.config.Config import GALAXY_BASE_DIR, OUTPUT_PRECISION -from proto.config.Security import galaxySecureEncodeId, galaxySecureDecodeId +from proto.config.Security import galaxySecureEncodeId, galaxySecureDecodeId, \ + GALAXY_SECURITY_HELPER_OBJ """ Note on datasetInfo and datasetId (used in several functions): @@ -251,11 +252,12 @@ def getLoadToGalaxyHistoryURL(fn, genome='', galaxyDataType='bed', urlPrefix=Non urlPrefix = URL_PREFIX import base64 + encodedFn = base64.urlsafe_b64encode(GALAXY_SECURITY_HELPER_OBJ.encode_guid(fn)) assert galaxyDataType is not None return urlPrefix + '/tool_runner?tool_id=file_import' + \ ('&dbkey=' + genome if genome else '') + \ - '&runtool_btn=yes&input=' + base64.urlsafe_b64encode(fn) + \ + '&runtool_btn=yes&input=' + encodedFn + \ ('&format=' + galaxyDataType if galaxyDataType is not None else '') + \ ('&job_name=' + histElementName if histElementName is not None else '') diff --git a/lib/proto/tools/FileImport.py b/lib/proto/tools/FileImport.py new file mode 100644 index 000000000000..d6cd16f40ca6 --- /dev/null +++ b/lib/proto/tools/FileImport.py @@ -0,0 +1,342 @@ +import base64 +import os + +import shutil + +from proto.config.Config import STATIC_PATH, GALAXY_FILE_PATH, GALAXY_BASE_DIR +from proto.config.Security import GALAXY_SECURITY_HELPER_OBJ +from proto.tools.GeneralGuiTool import GeneralGuiTool + + +class FileImport(GeneralGuiTool): + @staticmethod + def getToolName(): + """ + Specifies a header of the tool, which is displayed at the top of the + page. + """ + return "Import file to history" + + @staticmethod + def getInputBoxNames(): + """ + Specifies a list of headers for the input boxes, and implicitly also + the number of input boxes to display on the page. The returned list + can have two syntaxes: + + 1) A list of strings denoting the headers for the input boxes in + numerical order. + 2) A list of tuples of strings, where each tuple has + two items: a header and a key. + + The contents of each input box must be defined by the function + getOptionsBoxK, where K is either a number in the range of 1 to the + number of boxes (case 1), or the specified key (case 2). + + Note: the key has to be camelCase and start with a non-capital letter + (e.g. "firstKey") + """ + return [('', 'basicQuestionId'), + ('Input filename', 'input'), + ('Format', 'format'), + ('Datatype', 'datatype')] + + # @staticmethod + # def getInputBoxOrder(): + # """ + # Specifies the order in which the input boxes should be displayed, + # as a list. The input boxes are specified by index (starting with 1) + # or by key. If None, the order of the input boxes is in the order + # specified by getInputBoxNames. + # """ + # return None + # + # @staticmethod + # def getInputBoxGroups(choices=None): + # """ + # Creates a visual separation of groups of consecutive option boxes + # from the rest (fieldset). Each such group has an associated label + # (string), which is shown to the user. To define groups of option + # boxes, return a list of BoxGroup namedtuples with the label, the key + # (or index) of the first and last options boxes (inclusive). + # + # Example: + # from quick.webtool.GeneralGuiTool import BoxGroup + # return [BoxGroup(label='A group of choices', first='firstKey', + # last='secondKey')] + # """ + # return None + + @staticmethod + def getOptionsBoxBasicQuestionId(): # Alternatively: getOptionsBox1() + """ + Defines the type and contents of the input box. User selections are + returned to the tools in the prevChoices and choices attributes to + other methods. These are lists of results, one for each input box + (in the order specified by getInputBoxOrder()). + + The input box is defined according to the following syntax: + + Selection box: ['choice1','choice2'] + - Returns: string + + Text area: 'textbox' | ('textbox',1) | ('textbox',1,False) + - Tuple syntax: (contents, height (#lines) = 1, read only flag = False) + - The contents is the default value shown inside the text area + - Returns: string + + Raw HTML code: '__rawstr__', 'HTML code' + - This is mainly intended for read only usage. Even though more + advanced hacks are possible, it is discouraged. + + Password field: '__password__' + - Returns: string + + Genome selection box: '__genome__' + - Returns: string + + Track selection box: '__track__' + - Requires genome selection box. + - Returns: colon-separated string denoting track name + + History selection box: ('__history__',) | + ('__history__', 'bed', 'wig') + - Only history items of specified types are shown. + - Returns: colon-separated string denoting galaxy track name, as + specified in ExternalTrackManager.py. + + History check box list: ('__multihistory__', ) | + ('__multihistory__', 'bed', 'wig') + - Only history items of specified types are shown. + - Returns: OrderedDict with galaxy id as key and galaxy track name + as value if checked, else None. + + Hidden field: ('__hidden__', 'Hidden value') + - Returns: string + + Table: [['header1','header2'], ['cell1_1','cell1_2'], + ['cell2_1','cell2_2']] + - Returns: None + + Check box list: OrderedDict([('key1', True), ('key2', False), + ('key3', False)]) + - Returns: OrderedDict from key to selection status (bool). + """ + return '__hidden__', '' + + @staticmethod + def getOptionsBoxInput(prevChoices): # Alternatively: getOptionsBox2() + return '__hidden__', '' + + @staticmethod + def getOptionsBoxDatatype(prevChoices): + """ + See getOptionsBoxFirstKey(). + + prevChoices is a namedtuple of selections made by the user in the + previous input boxes (that is, a namedtuple containing only one element + in this case). The elements can accessed either by index, e.g. + prevChoices[0] for the result of input box 1, or by key, e.g. + prevChoices.key (case 2). + """ + return '__hidden__', prevChoices.format if prevChoices.format else 'bed' + + @staticmethod + def getOptionsBoxFormat(prevChoices): + return '__hidden__', '' + + # @staticmethod + # def getInfoForOptionsBoxKey(prevChoices): + # """ + # If not None, defines the string content of an clickable info box + # beside the corresponding input box. HTML is allowed. + # """ + # return None + # + # @staticmethod + # def getDemoSelections(): + # """ + # Defines a set of demo inputs to the option boxes in the + # order defined by getOptionBoxNames and getOptionsBoxOrder. + # If not None, a Demo button appears in the interface. Clicking the + # button fills the option boxed with the defined demo values. + # """ + # return ['testChoice1', '..'] + # + # @classmethod + # def getExtraHistElements(cls, choices): + # """ + # Defines extra history elements to be created when clicking execute. + # This is defined by a list of HistElement objects, as in the + # following example: + # + # from proto.GeneralGuiTool import HistElement + # return [HistElement(cls.HISTORY_TITLE, 'bed', hidden=False)] + # + # It is good practice to use class constants for longer strings. + # + # In the execute() method, one typically needs to fetch the path to + # the dataset referred to by the extra history element. To fetch the + # path, use the dict cls.extraGalaxyFn with the defined history title + # as key, e.g. "cls.extraGalaxyFn[cls.HISTORY_TITLE]". + # """ + # return None + + @staticmethod + def execute(choices, galaxyFn=None, username=''): + """ + Is called when execute-button is pushed by web-user. Should print + output as HTML to standard out, which will be directed to a results + page in Galaxy history. If getOutputFormat is anything else than + HTML, the output should be written to the file with path galaxyFn. + If needed, StaticFile can be used to get a path where additional + files can be put (e.g. generated image files). choices is a list of + selections made by web-user in each options box. + """ + input = str(choices.input) + + try: + input = base64.urlsafe_b64decode(input) + input = GALAXY_SECURITY_HELPER_OBJ.decode_guid(input) + except: + raise Exception('Old-style "import to history" URLs have been deprecated due to ' + 'security concerns. If you clicked from the output of an analysis ' + 'job, please re-run the job to get updated links.') + + input = os.path.abspath(input) + output = galaxyFn + + if not input.startswith(GALAXY_BASE_DIR): + input = os.path.sep.join([GALAXY_BASE_DIR.rstrip(os.path.sep), + input.lstrip(os.path.sep)]) + + datatype = choices.format if choices.format else choices.datatype + + if (input.startswith(STATIC_PATH) + or input.startswith(GALAXY_FILE_PATH)) \ + and input.endswith('.' + datatype): + shutil.copy(input, output) + else: + # print input, input_real, 'not allowed', os.path.realpath(STATIC_PATH), \ + # os.path.realpath(GALAXY_FILE_PATH), datatype + raise Exception(input + ' not allowed to import! %s %s %s' % + (GALAXY_BASE_DIR, STATIC_PATH, GALAXY_FILE_PATH)) + + + @staticmethod + def validateAndReturnErrors(choices): + """ + Should validate the selected input parameters. If the parameters are + not valid, an error text explaining the problem should be returned. + The GUI then shows this text to the user (if not empty) and greys + out the execute button (even if the text is empty). If all + parameters are valid, the method should return None, which enables + the execute button. + """ + return '' + + # @staticmethod + # def getSubToolClasses(): + # """ + # Specifies a list of classes for subtools of the main tool. These + # subtools will be selectable from a selection box at the top of the + # page. The input boxes will change according to which subtool is + # selected. + # """ + # return None + # + # @staticmethod + # def isPublic(): + # """ + # Specifies whether the tool is accessible to all users. If False, the + # tool is only accessible to a restricted set of users as defined in + # LocalOSConfig.py. + # """ + # return False + # + # @staticmethod + # def isRedirectTool(): + # """ + # Specifies whether the tool should redirect to an URL when the Execute + # button is clicked. + # """ + # return False + # + # @staticmethod + # def getRedirectURL(choices): + # """ + # This method is called to return an URL if the isRedirectTool method + # returns True. + # """ + # return '' + # + # @staticmethod + # def isHistoryTool(): + # """ + # Specifies if a History item should be created when the Execute button + # is clicked. + # """ + # return True + # + # @classmethod + # def isBatchTool(cls): + # """ + # Specifies if this tool could be run from batch using the batch. The + # batch run line can be fetched from the info box at the bottom of the + # tool. + # """ + # return cls.isHistoryTool() + # + # @staticmethod + # def isDynamic(): + # """ + # Specifies whether changing the content of texboxes causes the page to + # reload. + # """ + # return True + # + # @staticmethod + # def getResetBoxes(): + # """ + # Specifies a list of input boxes which resets the subsequent stored + # choices previously made. The input boxes are specified by index + # (starting with 1) or by key. + # """ + # return [] + # + # @staticmethod + # def getToolDescription(): + # """ + # Specifies a help text in HTML that is displayed below the tool. + # """ + # return '' + # + # @staticmethod + # def getToolIllustration(): + # """ + # Specifies an id used by StaticFile.py to reference an illustration file + # on disk. The id is a list of optional directory names followed by a file + # name. The base directory is STATIC_PATH as defined by Config.py. The + # full path is created from the base directory followed by the id. + # """ + # return None + # + # @staticmethod + # def getFullExampleURL(): + # """ + # Specifies an URL to an example page that describes the tool, for + # instance a Galaxy page. + # """ + # return None + # + # @staticmethod + # def isDebugMode(): + # """ + # Specifies whether the debug mode is turned on. + # """ + # return False + # + + @staticmethod + def getOutputFormat(choices): + return choices.format if choices.format else choices.datatype diff --git a/lib/proto/tools/FileImport.xml b/lib/proto/tools/FileImport.xml new file mode 100644 index 000000000000..50657b5d4066 --- /dev/null +++ b/lib/proto/tools/FileImport.xml @@ -0,0 +1,4 @@ + From 768eaad311813b606c57859f1e1b0c4bebf9a3f2 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 12 Jan 2017 23:25:42 +0100 Subject: [PATCH 045/123] Detailed path checks are no longer needed for security. Removed the checks. Cleanup. (cherry picked from commit d004589f68c835d5a079aa4bbf3a206a7325193b) --- lib/proto/tools/FileImport.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/proto/tools/FileImport.py b/lib/proto/tools/FileImport.py index d6cd16f40ca6..2f9a1caed054 100644 --- a/lib/proto/tools/FileImport.py +++ b/lib/proto/tools/FileImport.py @@ -212,15 +212,12 @@ def execute(choices, galaxyFn=None, username=''): datatype = choices.format if choices.format else choices.datatype - if (input.startswith(STATIC_PATH) - or input.startswith(GALAXY_FILE_PATH)) \ - and input.endswith('.' + datatype): + if input.endswith('.' + datatype): shutil.copy(input, output) else: # print input, input_real, 'not allowed', os.path.realpath(STATIC_PATH), \ # os.path.realpath(GALAXY_FILE_PATH), datatype - raise Exception(input + ' not allowed to import! %s %s %s' % - (GALAXY_BASE_DIR, STATIC_PATH, GALAXY_FILE_PATH)) + raise Exception(input + ' not allowed to import!') @staticmethod From 4deedbc901352f5b789aa9ccf767f7aba576036c Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 12 Jan 2017 23:47:43 +0100 Subject: [PATCH 046/123] ProTo: Added support for StaticImage overloading (cherry picked from commit b71493afdf00c59f1b925f858726bf588aef6f38) --- lib/proto/generictool.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/proto/generictool.py b/lib/proto/generictool.py index a4ced74e75c6..59cbc83744d3 100644 --- a/lib/proto/generictool.py +++ b/lib/proto/generictool.py @@ -36,6 +36,7 @@ def getClassName(obj): class GenericToolController(BaseToolController): + STATIC_IMAGE_CLS = StaticImage initChoicesDict = None def __init__(self, trans, job): @@ -612,7 +613,7 @@ def getIllustrationImage(self): image = None id = self.prototype.getToolIllustration() if id: - image = StaticImage(id) + image = self.STATIC_IMAGE_CLS(id) return image def getDemoURL(self): From 367aecaa033513d07bb86a072394e1068e82d259 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Sat, 14 Jan 2017 14:19:30 +0100 Subject: [PATCH 047/123] Added R library dependencies. Added automatic dependency installation (cherry picked from commit 69e75b4af3124b3fd501da19468ebdf3d42022aa) --- scripts/R_install_packages.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/R_install_packages.py b/scripts/R_install_packages.py index 0307d4a65434..893da91770c4 100644 --- a/scripts/R_install_packages.py +++ b/scripts/R_install_packages.py @@ -23,9 +23,11 @@ def _install_and_check_r_library(library): r("library('%s')" % library) except: install_cmds = \ - ["install.packages('%s', repos='http://cran.r-project.org')", - "source('http://www.bioconductor.org/biocLite.R'); biocLite('%s', suppressUpdates=TRUE)", - "install.packages('%s', repos='http://hyperbrowser.uio.no/eggs_repo/R')"] + ["install.packages('%s', repos='http://cran.r-project.org', dependencies=TRUE)", + "source('http://www.bioconductor.org/biocLite.R'); " + "biocLite('%s', suppressUpdates=TRUE, dependencies=TRUE)", + "install.packages('%s', repos='http://hyperbrowser.uio.no/eggs_repo/R', " + "dependencies=TRUE)"] exceptions = [] for cmd in install_cmds: From ffd8c7ad21d9e727eae2d4b3b6a7bac4d3f2f911 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Sun, 15 Jan 2017 12:11:02 +0100 Subject: [PATCH 048/123] ProTo: Added Proto tool shelf config (cherry picked from commit 1520b2670e7e44a2097c57cb3d1e462d67a0ad07) --- lib/proto/CommonFunctions.py | 5 ++--- lib/proto/config/Config.py | 1 + lib/proto/tools/ManageTools.py | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/proto/CommonFunctions.py b/lib/proto/CommonFunctions.py index eba64cf313b7..d89f47e713a3 100644 --- a/lib/proto/CommonFunctions.py +++ b/lib/proto/CommonFunctions.py @@ -21,7 +21,7 @@ from collections import OrderedDict from proto.CommonConstants import THOUSANDS_SEPARATOR -from proto.config.Config import GALAXY_BASE_DIR, OUTPUT_PRECISION +from proto.config.Config import PROTO_TOOL_SHELVE_FN, OUTPUT_PRECISION from proto.config.Security import galaxySecureEncodeId, galaxySecureDecodeId, \ GALAXY_SECURITY_HELPER_OBJ @@ -42,8 +42,7 @@ def getToolPrototype(toolId): tool_shelve = None try: - tool_shelve = shelve.open( - GALAXY_BASE_DIR + '/database/proto-tool-cache.shelve', 'r') + tool_shelve = shelve.open(PROTO_TOOL_SHELVE_FN, 'r') module_name, class_name = tool_shelve[str(toolId)] module = __import__(module_name, fromlist=[class_name]) # print module, class_name, toolId diff --git a/lib/proto/config/Config.py b/lib/proto/config/Config.py index 6cdebc0e83c3..014361dc0953 100644 --- a/lib/proto/config/Config.py +++ b/lib/proto/config/Config.py @@ -22,6 +22,7 @@ def getUrlPrefix(config): STATIC_REL_PATH = URL_PREFIX + STATIC_DIR STATIC_PATH = GALAXY_BASE_DIR + STATIC_DIR GALAXY_FILE_PATH = GALAXY_BASE_DIR + '/' + config.getWithDefault('file_path', 'database/files') +PROTO_TOOL_SHELVE_FN = GALAXY_BASE_DIR + '/database/proto-tool-cache.shelve' def userHasFullAccess(galaxyUserName): diff --git a/lib/proto/tools/ManageTools.py b/lib/proto/tools/ManageTools.py index 053f2a084a03..b05762bd74ed 100644 --- a/lib/proto/tools/ManageTools.py +++ b/lib/proto/tools/ManageTools.py @@ -1,5 +1,6 @@ from proto.tools.GeneralGuiTool import GeneralGuiTool, MultiGeneralGuiTool -from proto.config.Config import GALAXY_BASE_DIR, GALAXY_REL_TOOL_CONFIG_FILE, URL_PREFIX +from proto.config.Config import GALAXY_BASE_DIR, GALAXY_REL_TOOL_CONFIG_FILE, \ + URL_PREFIX, PROTO_TOOL_SHELVE_FN import os, re, shelve, shutil, sys, traceback from importlib import import_module from collections import OrderedDict @@ -11,7 +12,6 @@ #HB_TOOL_DIR = GALAXY_BASE_DIR + '/tools/hyperbrowser/new-xml/' #PROTO_TOOL_DIR = HB_SOURCE_CODE_BASE_DIR + '/quick/webtools/' PROTO_TOOL_DIR = GALAXY_BASE_DIR + '/lib/proto/tools/' -TOOL_SHELVE = GALAXY_BASE_DIR + '/database/proto-tool-cache.shelve' TOOL_CONF = GALAXY_BASE_DIR + '/' + GALAXY_REL_TOOL_CONFIG_FILE GALAXY_TOOL_XML_PATH = GALAXY_BASE_DIR + '/tools/' TOOL_XML_REL_PATH = 'hyperbrowser/' @@ -134,7 +134,7 @@ def useSubToolPrefix(): @classmethod def getSubToolClasses(cls): - tool_shelve = shelve.open(TOOL_SHELVE, 'r') + tool_shelve = shelve.open(PROTO_TOOL_SHELVE_FN, 'r') installed_classes = [tool_shelve.get(t)[1] for t in tool_shelve.keys() if os.path.exists(os.path.join(SOURCE_CODE_BASE_DIR, tool_shelve.get(t)[0].replace('.', os.path.sep)) + '.py') ] tool_shelve.close() @@ -199,7 +199,7 @@ class InstallToolsTool(GeneralGuiTool): @classmethod def _getToolList(cls): - tool_shelve = shelve.open(TOOL_SHELVE, 'r') + tool_shelve = shelve.open(PROTO_TOOL_SHELVE_FN, 'r') tool_IDs = set(tool_shelve.keys()) installed_classes = [tool_shelve.get(t)[1] for t in tool_IDs] tool_shelve.close() From 1429d846c9abc71f8dd713dfa86287275d98c596 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Sun, 15 Jan 2017 12:12:36 +0100 Subject: [PATCH 049/123] ProTo: Small fixes. Changed to unicode casting some places (cherry picked from commit 28478c5bc058390406211615fc177e4f9a5992ac) --- lib/proto/galaxy_tool_classes.py | 2 +- lib/proto/generictool.py | 8 ++++---- lib/proto/hyper_gui.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/proto/galaxy_tool_classes.py b/lib/proto/galaxy_tool_classes.py index 3dcb1fd2dc16..f1605be5ea7e 100644 --- a/lib/proto/galaxy_tool_classes.py +++ b/lib/proto/galaxy_tool_classes.py @@ -67,7 +67,7 @@ def parse( self, tool_source, guid=None ): inputs = ElementTree.Element('inputs') inputs.append(ElementTree.Element('param', name='mako', type='hidden', value=self.proto_mako)) inputs.append(ElementTree.Element('param', name='tool_id', type='hidden', value=root.get('id'))) - inputs.append(ElementTree.Element('param', name='tool_name', type='hidden', value=root.get('id'))) + inputs.append(ElementTree.Element('param', name='tool_name', type='hidden', value=root.get('name'))) root.append(inputs) if root.find('outputs') is None: outputs = ElementTree.Element('outputs') diff --git a/lib/proto/generictool.py b/lib/proto/generictool.py index 59cbc83744d3..b1931d115a0e 100644 --- a/lib/proto/generictool.py +++ b/lib/proto/generictool.py @@ -360,7 +360,7 @@ def action(self): for k,v in opts.items(): itemval = self.params.get(id + '|' + k, None) #if itemval: - values[str(k)] = itemval + values[unicode(k)] = itemval val = values @@ -404,7 +404,7 @@ def action(self): for r in range(1, len(opts)): core.tableLine(opts[r]) core.tableFooter() - val = str(core) + val = unicode(core) display_only = True else: @@ -561,9 +561,9 @@ def execute(self): if isinstance(extra_output, list): for output in extra_output: if isinstance(output, HistElement): - self.extraGalaxyFn[str(output.name)] = self.params[output.name] + self.extraGalaxyFn[unicode(output.name)] = self.params[output.name] else: - self.extraGalaxyFn[str(output[0])] = self.params[output[0]] + self.extraGalaxyFn[unicode(output[0])] = self.params[output[0]] username = self.params['userEmail'] if 'userEmail' in self.params else '' diff --git a/lib/proto/hyper_gui.py b/lib/proto/hyper_gui.py index 9cf1cf8a8ff4..703d4ecb39d2 100644 --- a/lib/proto/hyper_gui.py +++ b/lib/proto/hyper_gui.py @@ -76,7 +76,7 @@ def __init__(self, trans): params = trans.request.rerun_job_params if hasattr(trans.request, 'rerun_job_params') else trans.request.params for key in params.keys(): try: - self.params[key] = str(params[key]) if params[key] != None else None + self.params[key] = unicode(params[key]) if params[key] != None else None except: self.params[key] = params[key] @@ -130,7 +130,7 @@ def itemsFromHistoryFn(self, exts = None): items = OrderedDict() for dataset in self.getHistory(exts): option_tag, val = self.makeHistoryOption(dataset) - items[str(dataset.dataset_id)] = val + items[unicode(dataset.dataset_id)] = val return items def makeHistoryOption(self, dataset, select=None, sep=':'): From eb17840fca6a19a21e18db52b7f9320fb95340bf Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Sun, 15 Jan 2017 12:40:35 +0100 Subject: [PATCH 050/123] ProTo: Prepare for usage and error logging in HyperBrowser (cherry picked from commit ba3d920fecedb174bc94a0fbc8fff8c85f673e8c) --- lib/proto/generictool.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/proto/generictool.py b/lib/proto/generictool.py index b1931d115a0e..d56670934c3c 100644 --- a/lib/proto/generictool.py +++ b/lib/proto/generictool.py @@ -579,8 +579,10 @@ def execute(self): ''' - def _executeTool(self, toolClassName, choices, galaxyFn, username): + super(GenericToolController, self)._executeTool( + toolClassName, choices, galaxyFn, username) + self._monkeyPatchAttr('extraGalaxyFn', self.extraGalaxyFn) self._monkeyPatchAttr('runParams', self.json_params) self.prototype.execute(choices, galaxyFn=galaxyFn, username=username) From eae57283e40e2133ec6094efcb442f1a4dd6feab Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Sun, 15 Jan 2017 14:44:30 +0100 Subject: [PATCH 051/123] ProTo: Fixed super() calls. Removed cache fail error printing (cherry picked from commit c537164c2d8891d9da1cbd493a62db63f06e3ad1) --- lib/proto/BaseToolController.py | 4 ---- lib/proto/generictool.py | 10 ++++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/proto/BaseToolController.py b/lib/proto/BaseToolController.py index a9f18ed50239..4c3ba30debb6 100644 --- a/lib/proto/BaseToolController.py +++ b/lib/proto/BaseToolController.py @@ -30,10 +30,6 @@ def __init__(self, trans = None, job = None): elif job: self.openJobParams(job) - def _init(self): - if hasattr(super(BaseToolController, self), '_init'): - super(BaseToolController, self)._init() - def openTransaction(self, trans): self.transaction = trans self.galaxy = GalaxyWrapper(trans) diff --git a/lib/proto/generictool.py b/lib/proto/generictool.py index d56670934c3c..8388fd78a90b 100644 --- a/lib/proto/generictool.py +++ b/lib/proto/generictool.py @@ -114,7 +114,8 @@ def __init__(self, trans, job): self.extra_output.append(e) def _init(self): - super(GenericToolController, self)._init() + if hasattr(super(GenericToolController, self), '_init'): + super(GenericToolController, self)._init() def _getInputGroup(self, inputBoxGroups): startGroupInfo = defaultdict(list) @@ -262,7 +263,7 @@ def getOptionsBox(self, id, i, val): #print 'from cache:',id self.input_changed = (val != self.cachedParams[id]) except Exception as e: - print 'cache load failed:', e, id + # print 'cache load failed for id "%s": %s' % (id, e) opts, info = self._getOptionsBox(i, val) self.input_changed = True @@ -580,8 +581,9 @@ def execute(self): ''' def _executeTool(self, toolClassName, choices, galaxyFn, username): - super(GenericToolController, self)._executeTool( - toolClassName, choices, galaxyFn, username) + if hasattr(super(GenericToolController, self), '_executeTool'): + super(GenericToolController, self)._executeTool( + toolClassName, choices, galaxyFn, username) self._monkeyPatchAttr('extraGalaxyFn', self.extraGalaxyFn) self._monkeyPatchAttr('runParams', self.json_params) From c527b73da80a3c31e6330d0609bc1c5a16e7a8bf Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Sun, 15 Jan 2017 20:30:08 +0100 Subject: [PATCH 052/123] ProTo: Better handling of unicode values (cherry picked from commit 5e68467798f90abdc3b5c79ae88167e1c505099e) --- lib/proto/HtmlCore.py | 8 ++++---- lib/proto/generictool.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/proto/HtmlCore.py b/lib/proto/HtmlCore.py index dd1f0a4bf14c..f9aa09542c0f 100644 --- a/lib/proto/HtmlCore.py +++ b/lib/proto/HtmlCore.py @@ -150,7 +150,7 @@ def tableHeader(self, headerRow, tagRow=None, firstRow=True, sortable=False, for tag, el in zip(tagRow, headerRow): self._str += '' + str(el) + '' + '>' + unicode(el) + '' self._str += '' + os.linesep return self @@ -159,7 +159,7 @@ def tableLine(self, row, rowSpanList=None, **kwargs): self.tableRowBegin(**kwargs) for i, el in enumerate(row): rowSpan = rowSpanList[i] if rowSpanList else None - self.tableCell(str(el), rowSpan=rowSpan, **kwargs) + self.tableCell(unicode(el), rowSpan=rowSpan, **kwargs) self.tableRowEnd(**kwargs) return self @@ -190,9 +190,9 @@ def tableCell(self, content, cellClass=None, style=None, if style: self._str += ' style="%s"' % style if rowSpan: - self._str += ' rowspan="' + str(rowSpan) + '"' + self._str += ' rowspan="' + unicode(rowSpan) + '"' if colSpan: - self._str += ' colspan="' + str(colSpan) + '"' + self._str += ' colspan="' + unicode(colSpan) + '"' self._str += '>' + content + '' def tableFooter(self, **kwargs): diff --git a/lib/proto/generictool.py b/lib/proto/generictool.py index 8388fd78a90b..51d70476efd7 100644 --- a/lib/proto/generictool.py +++ b/lib/proto/generictool.py @@ -19,7 +19,7 @@ import sys, os, json, shelve import cPickle as pickle from zlib import compress, decompress -from base64 import urlsafe_b64decode, urlsafe_b64encode +from base64 import urlsafe_b64decode, urlsafe_b64encode, b64encode, b64decode from collections import namedtuple, OrderedDict, defaultdict from urllib import quote, unquote from proto.tools.GeneralGuiTool import HistElement @@ -243,10 +243,10 @@ def putCacheData(self, id, data): self.cachedExtra[id] = pickle.dumps(data) def getCacheData(self, id): - return pickle.loads(str(self.cachedExtra[id])) + return pickle.loads(b64decode(str(self.cachedExtra[id]))) def putCachedOption(self, id, data): - self.cachedOptions[id] = pickle.dumps(data) + self.cachedOptions[id] = b64encode(pickle.dumps(data)) def getCachedOption(self, id): return pickle.loads(str(self.cachedOptions[id])) From 54d5e6007966f5f857a18a67eba9fe27436d4503 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Wed, 18 Jan 2017 05:23:44 +0100 Subject: [PATCH 053/123] Merged with feature branch "hb_feature/text_table_output" (cherry picked from commit 4937e4e937ed5556371c6f4341ec8fcb5bd22eeb) --- lib/proto/CommonFunctions.py | 23 +++++++ lib/proto/HtmlCore.py | 114 ++++++++++++++++++++++++++++++++-- lib/proto/TableCoreMixin.py | 103 ++++-------------------------- lib/proto/TextCore.py | 32 +++++++--- lib/proto/tools/FileImport.py | 8 +-- 5 files changed, 170 insertions(+), 110 deletions(-) diff --git a/lib/proto/CommonFunctions.py b/lib/proto/CommonFunctions.py index d89f47e713a3..484568ebba91 100644 --- a/lib/proto/CommonFunctions.py +++ b/lib/proto/CommonFunctions.py @@ -322,3 +322,26 @@ def forceNumericSortingKey(key): sortKey1 = 1 sortKey2 = float(str(key).replace(THOUSANDS_SEPARATOR, '')) return [sortKey1, sortKey2] + + +def convertToDictOfLists(dataDict): + """Convert a dict of tuples or single values to dict of lists""" + dataDictOfLists = OrderedDict() + for key, val in dataDict.iteritems(): + if isinstance(val, list): + dataDictOfLists[key] = val + elif isinstance(val, tuple): + dataDictOfLists[key] = list(val) + else: + dataDictOfLists[key] = [val] + return dataDictOfLists + + +def fromDictOfDictsToDictOfListsAndColumnNameList(dataDict, firstColName=''): + colNames = [] + convertedDataDict = OrderedDict() + for key1, val1 in dataDict.iteritems(): + if not colNames: + colNames = [firstColName] + val1.keys() + convertedDataDict[key1] = val1.values() + return convertedDataDict, colNames diff --git a/lib/proto/HtmlCore.py b/lib/proto/HtmlCore.py index f9aa09542c0f..e524a529bbba 100644 --- a/lib/proto/HtmlCore.py +++ b/lib/proto/HtmlCore.py @@ -1,8 +1,6 @@ import os import re -from config.Config import URL_PREFIX -from proto.CommonConstants import THOUSANDS_SEPARATOR from proto.TableCoreMixin import TableCoreMixin @@ -10,7 +8,14 @@ class HtmlCore(TableCoreMixin): def __init__(self): self._str = '' + @staticmethod + def _getTextCoreCls(): + from proto.TextCore import TextCore + return TextCore + def begin(self, extraJavaScriptFns=[], extraJavaScriptCode=None, extraCssFns=[], redirectUrl=None, reloadTime=None): + from config.Config import URL_PREFIX + self._str = ''' ''' @@ -175,13 +180,18 @@ def tableRowEnd(self, **kwargs): return self def tableCell(self, content, cellClass=None, style=None, - rowSpan=None, colSpan=None, **kwargs): + rowSpan=None, colSpan=None, removeThousandsSep=False, **kwargs): self._str += '' - def tableFooter(self, **kwargs): + def tableFooter(self, expandable=False, tableId=None, numRows=None, visibleRows=6, **kwargs): self._str += ''+ os.linesep + + if expandable: + assert tableId, 'Table ID must be set for expandable tables.' + assert numRows, 'Number of rows must be set for expandable tables.' + + if numRows > visibleRows: + self.tableExpandButton(tableId, numRows, + visibleRows=visibleRows) + return self + + def tableExpandButton(self, tableId, totalRows, visibleRows=6): + self.script(''' + function expandTable(tableId) { + tblId = "#" + tableId; + $(tblId).find("tr").show(); + btnDivId = "#toggle_table_" + tableId; + $(btnDivId).find("input").toggle(); + $(tblId).off("click"); + } + + function collapseTable(tableId, visibleRows) { + tblId = "#" + tableId; + trScltr = tblId + " tr:nth-child(n + " + visibleRows + ")"; + $(trScltr).hide(); + btnDivId = "#toggle_table_" + tableId; + $(btnDivId).find("input").toggle(); + $(tblId).on("click", resetFunc(tableId, visibleRows)); + } + + var resetFunc = function(tableId, visibleRows) { + return function(e) { + return resetTable(e, tableId, visibleRows); + } + } + + function resetTable(e, tableId, visibleRows) { + expandTable(tableId) + collapseTable(tableId, visibleRows) + } + + $(document).ready(function(){ + tableId = "%s"; + visibleRows = %s; + tblId = "#" + tableId; + hiddenRowsSlctr = tblId + " tr:nth-child(n + " + (visibleRows+2) + ")"; + // 'visibleRows+2' for some reason (one of life's great mysteries) + if ($(hiddenRowsSlctr).length>0) { + $(hiddenRowsSlctr).hide(); + $(tblId).on("click", resetFunc(tableId, visibleRows+1)); + // 'visibleRows+1' for some other reason (one of life's other great mysteries) + + } + } + ); + + ''' % (tableId, visibleRows)) + + self._str += ''' +
+ + + ''' % (tableId, visibleRows, totalRows, tableId, totalRows, totalRows, tableId, + visibleRows + 1) + return self + + def tableWithTabularImportButton(self, tabularFn=None, + tabularHistElementName='Raw table', + produceTableCallbackFunc=None, + **kwArgsToCallback): + assert produceTableCallbackFunc is not None + assert tabularFn is not None + + textCore = self._getTextCoreCls()() + textCore = produceTableCallbackFunc(textCore, **kwArgsToCallback) + + from proto.CommonFunctions import ensurePathExists + ensurePathExists(tabularFn) + open(tabularFn, 'w').write(str(textCore)) + + self.importFileToHistoryButton("Import table to history (tabular)", + tabularFn, 'tabular', tabularHistElementName) + + return produceTableCallbackFunc(self, **kwArgsToCallback) + + def importFileToHistoryButton(self, label, importFn, galaxyDataType, histElementName): + from proto.CommonFunctions import getLoadToGalaxyHistoryURL + importUrl = getLoadToGalaxyHistoryURL(importFn, galaxyDataType=galaxyDataType, + histElementName=histElementName) + self._str += '''''' \ + % (importUrl, label) return self def divider(self, withSpacing=False): diff --git a/lib/proto/TableCoreMixin.py b/lib/proto/TableCoreMixin.py index ecdc378aabc4..248d9e2ffb15 100644 --- a/lib/proto/TableCoreMixin.py +++ b/lib/proto/TableCoreMixin.py @@ -11,16 +11,8 @@ def tableFromDictionary(self, dataDict, columnNames=None, sortable=True, If presorted is set to a number and tableId != None and sortable == True, that column will be presorted (using a hacky solution using jquery. """ - # transform dicts with a single value to a dict of lists for easier - # sorting and html table generation - dataDictOfLists = OrderedDict() - for key, val in dataDict.iteritems(): - if isinstance(val, list): - dataDictOfLists[key] = val - elif isinstance(val, tuple): - dataDictOfLists[key] = list(val) - else: - dataDictOfLists[key] = [val] + from proto import CommonFunctions + dataDictOfLists = CommonFunctions.convertToDictOfLists(dataDict) if presorted is not None and presorted > -1: assert isinstance(presorted, int), 'presorted must be int' @@ -30,7 +22,6 @@ def tableFromDictionary(self, dataDict, columnNames=None, sortable=True, tableClass = 'colored bordered' if expandable: - assert tableId, 'Table ID must be set for expandable tables.' tableClass += ' expandable' self.tableHeader(headerRow=columnNames, sortable=sortable, @@ -39,26 +30,16 @@ def tableFromDictionary(self, dataDict, columnNames=None, sortable=True, for key, val in dataDictOfLists.iteritems(): if isinstance(val, list): - self.tableLine([key] + val) + self.tableLine([key] + val, **kwargs) else: - self.tableLine([key] + [val]) - - self.tableFooter() - - # if tableId != None and sortable and presorted: - # # Javascript code for clicking on the column (so that it is sorted client side) - # # Hacky solution: Emulates a click on the header with a 500 ms delay so that sorttable.js is done first - # self._str += "" + self.tableLine([key] + [val], **kwargs) - if expandable and len(dataDict) > visibleRows: - self.tableExpandButton(tableId, len(dataDict), - visibleRows=visibleRows) + self.tableFooter(expandable=expandable, tableId=tableId, + numRows=len(dataDict), visibleRows=visibleRows, **kwargs) return self - def tableFromDictOfDicts(self, dataDict, firstColName='', sortable=True, - tableId=None, expandable=False, visibleRows=6, - presorted=None, **kwargs): + def tableFromDictOfDicts(self, dataDict, firstColName='', **kwargs): """ # Note: it is assumed that dataDict is a full matrix, i.e. each element in # the dict is a dict of the same size. @@ -68,73 +49,11 @@ def tableFromDictOfDicts(self, dataDict, firstColName='', sortable=True, all(isinstance(x, OrderedDict) for x in dataDict.values()), \ 'dataDict must be an OrderedDict of OrderedDicts' - colNames = [] - convertedDataDict = OrderedDict() - - for key1, val1 in dataDict.iteritems(): - if not colNames: - colNames = [firstColName] + val1.keys() - convertedDataDict[key1] = val1.values() + from proto.CommonFunctions import fromDictOfDictsToDictOfListsAndColumnNameList + convertedDataDict, colNames = \ + fromDictOfDictsToDictOfListsAndColumnNameList(dataDict, firstColName) self.tableFromDictionary(convertedDataDict, columnNames=colNames, - sortable=sortable, tableId=tableId, - expandable=expandable, - visibleRows=visibleRows, - presorted=presorted, **kwargs) - - return self - - def tableExpandButton(self, tableId, totalRows, visibleRows=6): - self.script(''' -function expandTable(tableId) { - tblId = "#" + tableId; - $(tblId).find("tr").show(); - btnDivId = "#toggle_table_" + tableId; - $(btnDivId).find("input").toggle(); - $(tblId).off("click"); -} - -function collapseTable(tableId, visibleRows) { - tblId = "#" + tableId; - trScltr = tblId + " tr:nth-child(n + " + visibleRows + ")"; - $(trScltr).hide(); - btnDivId = "#toggle_table_" + tableId; - $(btnDivId).find("input").toggle(); - $(tblId).on("click", resetFunc(tableId, visibleRows)); -} - -var resetFunc = function(tableId, visibleRows) { - return function(e) { - return resetTable(e, tableId, visibleRows); - } -} - -function resetTable(e, tableId, visibleRows) { - expandTable(tableId) - collapseTable(tableId, visibleRows) -} - -$(document).ready(function(){ - tableId = "%s"; - visibleRows = %s; - tblId = "#" + tableId; - hiddenRowsSlctr = tblId + " tr:nth-child(n + " + (visibleRows+2) + ")"; - // 'visibleRows+2' for some reason (one of life's great mysteries) - if ($(hiddenRowsSlctr).length>0) { - $(hiddenRowsSlctr).hide(); - $(tblId).on("click", resetFunc(tableId, visibleRows+1)); - // 'visibleRows+1' for some other reason (one of life's other great mysteries) - - } -} -); - -''' % (tableId, visibleRows)) - - self._str += ''' -
- - -''' % (tableId, visibleRows, totalRows, tableId, totalRows, totalRows, tableId, visibleRows + 1) + **kwargs) return self diff --git a/lib/proto/TextCore.py b/lib/proto/TextCore.py index 8a80097b0d11..efd1a8d69234 100644 --- a/lib/proto/TextCore.py +++ b/lib/proto/TextCore.py @@ -1,16 +1,19 @@ import os -from proto.HtmlCore import HtmlCore + from proto.TableCoreMixin import TableCoreMixin class TextCore(TableCoreMixin): - HTML_CORE_CLS = HtmlCore - def __init__(self): self._str = '' + @staticmethod + def _getHtmlCoreCls(): + from proto.HtmlCore import HtmlCore + return HtmlCore + def __getattr__(self, item): - if getattr(self.HTML_CORE_CLS, item): + if getattr(self._getHtmlCoreCls(), item): return self._default else: raise AttributeError('TextCore does not contain member "%s".' % item) @@ -56,13 +59,26 @@ def emphasize(self, text): return self def tableHeader(self, headerRow, **kwargs): - self._str += '\t'.join([str(el).upper() for el in headerRow]) + self._str += '#' + ('\t'.join([str(el) for el in headerRow])) self._str += os.linesep return self - def tableLine(self, row, rowSpanList=None, **kwargs): - self._str += '\t'.join(['' if rowSpanList is not None and rowSpanList[i]==0 \ - else str(el) for i,el in enumerate(row)]) + def tableLine(self, row, rowSpanList=None, removeThousandsSep=True, **kwargs): + rowOutput = [] + + for i, el in enumerate(row): + if rowSpanList is not None and rowSpanList[i] == 0: + rowOutput.append('') + else: + if removeThousandsSep: + try: + from proto.CommonConstants import THOUSANDS_SEPARATOR + el = el.replace(THOUSANDS_SEPARATOR, '') + except: + pass + rowOutput.append(str(el)) + + self._str += '\t'.join(rowOutput) self._str += os.linesep return self diff --git a/lib/proto/tools/FileImport.py b/lib/proto/tools/FileImport.py index 2f9a1caed054..3a50317f1a20 100644 --- a/lib/proto/tools/FileImport.py +++ b/lib/proto/tools/FileImport.py @@ -212,12 +212,12 @@ def execute(choices, galaxyFn=None, username=''): datatype = choices.format if choices.format else choices.datatype - if input.endswith('.' + datatype): - shutil.copy(input, output) - else: + # if input.endswith('.' + datatype): + shutil.copy(input, output) + # else: # print input, input_real, 'not allowed', os.path.realpath(STATIC_PATH), \ # os.path.realpath(GALAXY_FILE_PATH), datatype - raise Exception(input + ' not allowed to import!') + # raise Exception(input + ' not allowed to import!') @staticmethod From 99ec78b4a263a6365ba6aeeffbb3cb38a3758950 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Sat, 6 Jan 2018 02:06:49 +0100 Subject: [PATCH 054/123] Removed HyperBrowser import --- lib/proto/TableCoreMixin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/proto/TableCoreMixin.py b/lib/proto/TableCoreMixin.py index 248d9e2ffb15..c50ddbe69afc 100644 --- a/lib/proto/TableCoreMixin.py +++ b/lib/proto/TableCoreMixin.py @@ -16,7 +16,6 @@ def tableFromDictionary(self, dataDict, columnNames=None, sortable=True, if presorted is not None and presorted > -1: assert isinstance(presorted, int), 'presorted must be int' - from quick.util import CommonFunctions dataDictOfLists = CommonFunctions.smartSortDictOfLists( dataDictOfLists, sortColumnIndex=presorted) From 9d3357e346e73269c128004ea8354243a38c172c Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 16 Feb 2017 16:38:33 +0100 Subject: [PATCH 055/123] ProTo: Moved Proto development tools into separate modules. Other related refactoring. (cherry picked from commit 15b8725f8902a0adc097128acb0d499921a3b861) --- lib/proto/CommonFunctions.py | 20 +- lib/proto/ProtoToolRegister.py | 107 ++++++ lib/proto/config/Config.py | 11 +- lib/proto/generictool.py | 2 +- lib/proto/tools/ExploreToolsTool.py | 40 ++ lib/proto/tools/ExploreToolsTool.xml | 2 +- lib/proto/tools/GeneralGuiTool.py | 2 +- lib/proto/tools/GenerateToolsTool.py | 118 ++++++ lib/proto/tools/GenerateToolsTool.xml | 2 +- lib/proto/tools/InstallToolsTool.py | 267 +++++++++++++ lib/proto/tools/InstallToolsTool.xml | 2 +- lib/proto/tools/ManageTools.py | 527 -------------------------- 12 files changed, 546 insertions(+), 554 deletions(-) create mode 100644 lib/proto/ProtoToolRegister.py create mode 100644 lib/proto/tools/ExploreToolsTool.py create mode 100644 lib/proto/tools/GenerateToolsTool.py create mode 100644 lib/proto/tools/InstallToolsTool.py delete mode 100644 lib/proto/tools/ManageTools.py diff --git a/lib/proto/CommonFunctions.py b/lib/proto/CommonFunctions.py index 484568ebba91..ce04b7aaba5d 100644 --- a/lib/proto/CommonFunctions.py +++ b/lib/proto/CommonFunctions.py @@ -16,12 +16,11 @@ import os import re -import shelve import urllib from collections import OrderedDict from proto.CommonConstants import THOUSANDS_SEPARATOR -from proto.config.Config import PROTO_TOOL_SHELVE_FN, OUTPUT_PRECISION +from proto.config.Config import OUTPUT_PRECISION from proto.config.Security import galaxySecureEncodeId, galaxySecureDecodeId, \ GALAXY_SECURITY_HELPER_OBJ @@ -39,23 +38,6 @@ """ -def getToolPrototype(toolId): - tool_shelve = None - try: - tool_shelve = shelve.open(PROTO_TOOL_SHELVE_FN, 'r') - module_name, class_name = tool_shelve[str(toolId)] - module = __import__(module_name, fromlist=[class_name]) - # print module, class_name, toolId - prototype = getattr(module, class_name)(toolId) - # print "Loaded proto tool:", class_name - #except KeyError: - # prototype = None - finally: - if tool_shelve: - tool_shelve.close() - return prototype - - def ensurePathExists(fn): "Assumes that fn consists of a basepath (folder) and a filename, and ensures that the folder exists." path = os.path.split(fn)[0] diff --git a/lib/proto/ProtoToolRegister.py b/lib/proto/ProtoToolRegister.py new file mode 100644 index 000000000000..af34c023887c --- /dev/null +++ b/lib/proto/ProtoToolRegister.py @@ -0,0 +1,107 @@ +import os +import re +import shelve +import sys +import traceback +from importlib import import_module + +from proto.config.Config import SOURCE_CODE_BASE_DIR, PROTO_TOOL_DIR, PROTO_TOOL_SHELVE_FN +from proto.tools.GeneralGuiTool import GeneralGuiTool, MultiGeneralGuiTool + + +EXCEPT_MODULE_NAMES = ['proto.tools.ExploreToolsTool', + 'proto.tools.InstallToolsTool', + 'proto.tools.ToolTemplate', + 'proto.tools.ToolTemplateMinimal'] + + +def getInstalledProtoTools(): + tool_shelve = shelve.open(PROTO_TOOL_SHELVE_FN, 'r') + installed_classes = [tool_shelve.get(t)[1] for t in tool_shelve.keys() if os.path.exists( + os.path.join(SOURCE_CODE_BASE_DIR, + tool_shelve.get(t)[0].replace('.', os.path.sep)) + + '.py')] + tool_shelve.close() + return installed_classes + + +def getProtoToolList(exceptClassNames=[]): + tmp_tools = {} + tools = {} + tool_classes = [] + all_installed_sub_classes = set() + pys = [] + for d in os.walk(PROTO_TOOL_DIR, followlinks=True): + if d[0].find('.svn') == -1: + pys += [os.path.join(d[0], f) for f in d[2] if f.endswith('.py') and + not any(f.startswith(x) for x in ['.', '#'])] + + # To fix import issue if there are modules in /lib and /lib/proto with the + # same name (e.g. 'config'). + tmpSysPath = sys.path + if sys.path[0].endswith('/proto'): + sys.path = tmpSysPath[1:] + + # print 'Num py', len(wpys) + for fn in pys: + with open(fn) as f: + for line in f: + m = re.match(r'class +(\w+) *\(([\w ,]+)\)', line) + if m: + class_name = m.group(1) + module_name = os.path.splitext(os.path.relpath( + os.path.abspath(fn), SOURCE_CODE_BASE_DIR))[0].replace(os.path.sep, '.') + try: + if module_name not in EXCEPT_MODULE_NAMES: + module = import_module(module_name) + prototype_cls = getattr(module, class_name) + if issubclass(prototype_cls, GeneralGuiTool): + if issubclass(prototype_cls, MultiGeneralGuiTool): + if class_name in exceptClassNames and \ + prototype_cls.getSubToolClasses(): + for sub_cls in prototype_cls.getSubToolClasses(): + all_installed_sub_classes.add(sub_cls) + elif hasattr(prototype_cls, 'getToolName'): + if class_name not in exceptClassNames: + tool_module = module_name.split('.')[2:] + if class_name != tool_module[-1]: + tool_selection_name = '.'.join(tool_module) + \ + ' [' + class_name + ']' + else: + tool_selection_name = '.'.join(tool_module) + + # print (fn, m.group(2), prototype_cls, module_name) + from gold.application.LogSetup import logMessage + logMessage(repr((fn, m.group(2), prototype_cls, module_name))) + tmp_tools[tool_selection_name] = \ + (fn, m.group(2), prototype_cls, module_name) + except Exception as e: + traceback.print_exc() + # break + # print 'Num protopy', len(tools) + + for tool_selection_name, tool_info in tmp_tools.iteritems(): + prototype_cls = tool_info[2] + if prototype_cls not in all_installed_sub_classes: + tools[tool_selection_name] = tool_info + tool_classes.append(prototype_cls) + + sys.path = tmpSysPath + return tools, tool_classes + + +def getToolPrototype(toolId): + tool_shelve = None + try: + tool_shelve = shelve.open(PROTO_TOOL_SHELVE_FN, 'r') + module_name, class_name = tool_shelve[str(toolId)] + module = __import__(module_name, fromlist=[class_name]) + # print module, class_name, toolId + prototype = getattr(module, class_name)(toolId) + # print "Loaded proto tool:", class_name + #except KeyError: + # prototype = None + finally: + if tool_shelve: + tool_shelve.close() + return prototype diff --git a/lib/proto/config/Config.py b/lib/proto/config/Config.py index 014361dc0953..795192b81ad1 100644 --- a/lib/proto/config/Config.py +++ b/lib/proto/config/Config.py @@ -12,17 +12,22 @@ def getUrlPrefix(config): if not globals().get('URL_PREFIX'): URL_PREFIX = getUrlPrefix(config) +GALAXY_FILE_PATH = GALAXY_BASE_DIR + '/' + config.getWithDefault('file_path', 'database/files') GALAXY_REL_TOOL_CONFIG_FILE = config.getWithDefault('tool_config_file', 'config/tool_conf.xml') ADMIN_USERS = [username.strip() for username in config.getWithDefault('admin_users', '').split(',')] RESTRICTED_USERS = [username.strip() for username in config.getWithDefault('restricted_users', '', 'galaxy_proto').split(',')] OUTPUT_PRECISION = int(config.getWithDefault('output_precision', '4', 'galaxy_proto')) + +GALAXY_TOOL_CONFIG_FILE = GALAXY_BASE_DIR + '/' + GALAXY_REL_TOOL_CONFIG_FILE +GALAXY_TOOL_XML_PATH = GALAXY_BASE_DIR + '/tools/' +PROTO_TOOL_DIR = GALAXY_BASE_DIR + '/lib/proto/tools/' +PROTO_TOOL_SHELVE_FN = GALAXY_BASE_DIR + '/database/proto-tool-cache.shelve' +SOURCE_CODE_BASE_DIR = GALAXY_BASE_DIR + '/lib' STATIC_DIR = '/static/proto' -STATIC_REL_PATH = URL_PREFIX + STATIC_DIR STATIC_PATH = GALAXY_BASE_DIR + STATIC_DIR -GALAXY_FILE_PATH = GALAXY_BASE_DIR + '/' + config.getWithDefault('file_path', 'database/files') -PROTO_TOOL_SHELVE_FN = GALAXY_BASE_DIR + '/database/proto-tool-cache.shelve' +STATIC_REL_PATH = URL_PREFIX + STATIC_DIR def userHasFullAccess(galaxyUserName): diff --git a/lib/proto/generictool.py b/lib/proto/generictool.py index 51d70476efd7..6b64d4463a2e 100644 --- a/lib/proto/generictool.py +++ b/lib/proto/generictool.py @@ -27,7 +27,7 @@ from proto.config.Config import URL_PREFIX, GALAXY_BASE_DIR from proto.config.Security import galaxySecureEncodeId, galaxySecureDecodeId, GALAXY_SECURITY_HELPER_OBJ from BaseToolController import BaseToolController -from proto.CommonFunctions import getToolPrototype +from proto.ProtoToolRegister import getToolPrototype from proto.StaticFile import StaticImage diff --git a/lib/proto/tools/ExploreToolsTool.py b/lib/proto/tools/ExploreToolsTool.py new file mode 100644 index 000000000000..cf2a3cc4368e --- /dev/null +++ b/lib/proto/tools/ExploreToolsTool.py @@ -0,0 +1,40 @@ +from proto.ProtoToolRegister import getInstalledProtoTools, getProtoToolList +from proto.tools.GeneralGuiTool import MultiGeneralGuiTool + + +class ExploreToolsTool(MultiGeneralGuiTool): + @staticmethod + def getToolName(): + return "ProTo tool explorer" + + @staticmethod + def getToolSelectionName(): + return "----- Select tool -----" + + @staticmethod + def useSubToolPrefix(): + return True + + @classmethod + def getSubToolClasses(cls): + installed_classes = getInstalledProtoTools() + tool_list = getProtoToolList(installed_classes)[1] + return sorted(tool_list, key=lambda c: c.__module__) + + @staticmethod + def getToolDescription(): + from proto.HtmlCore import HtmlCore + core = HtmlCore() + core.smallHeader("General description") + core.paragraph("This tool is used to try out ProTo tools that have " + "not been installed as separate tools in the tool " + "menu. This is typically used for development " + "purposes, so that one can polish the tool until it" + "is finished for deployment in the tool menu. " + "When a tool is installed into the menu, the tool " + "disappears from the tool list in this tool." + "The logic for inclusion in the list is that there " + "exists a Python module with a class that inherits " + "from GeneralGuiTool, without there existing " + "a Galaxy xml file for the tool.") + return str(core) diff --git a/lib/proto/tools/ExploreToolsTool.xml b/lib/proto/tools/ExploreToolsTool.xml index d622ec8f49be..903d3df7f2a2 100644 --- a/lib/proto/tools/ExploreToolsTool.xml +++ b/lib/proto/tools/ExploreToolsTool.xml @@ -1,4 +1,4 @@ + tool_type="proto_generic" proto_tool_module="proto.tools.ExploreToolsTool" proto_tool_class="ExploreToolsTool"> diff --git a/lib/proto/tools/GeneralGuiTool.py b/lib/proto/tools/GeneralGuiTool.py index a1e32360df0e..8188ecb24002 100644 --- a/lib/proto/tools/GeneralGuiTool.py +++ b/lib/proto/tools/GeneralGuiTool.py @@ -166,7 +166,7 @@ def makeHistElement(cls, galaxyExt='html', title='new Dataset', label='Newly cr @classmethod def createGenericGuiToolURL(cls, tool_id, sub_class_name=None, tool_choices=None): - from proto.CommonFunctions import getToolPrototype + from proto.ProtoToolRegister import getToolPrototype tool = getToolPrototype(tool_id) base_url = '?mako=generictool&tool_id=' + tool_id + '&' if sub_class_name and isinstance(tool, MultiGeneralGuiTool): diff --git a/lib/proto/tools/GenerateToolsTool.py b/lib/proto/tools/GenerateToolsTool.py new file mode 100644 index 000000000000..5a3cc81ee831 --- /dev/null +++ b/lib/proto/tools/GenerateToolsTool.py @@ -0,0 +1,118 @@ +import os +from urllib import quote + +from proto.config.Config import URL_PREFIX, PROTO_TOOL_DIR +from proto.tools.GeneralGuiTool import GeneralGuiTool + + +class GenerateToolsTool(GeneralGuiTool): + @staticmethod + def getToolName(): + return "ProTo tool generator" + + @staticmethod + def getInputBoxNames(): + return [('Package name', 'packageName'), + ('Module/class name', 'moduleName'), + ('Tool name', 'toolName'), + ('Use template with inline documentation', 'template')] + + #@staticmethod + #def getResetBoxes(): + # return ['moduleName'] + + @staticmethod + def getOptionsBoxPackageName(): + return '' + + @staticmethod + def getOptionsBoxModuleName(prevchoices): + return 'ChangeMeTool' + + @staticmethod + def getOptionsBoxToolName(prevchoices): + return 'Title of tool' + + @staticmethod + def getOptionsBoxTemplate(prevchoices): + return ['Yes', 'No'] + + @staticmethod + def execute(choices, galaxyFn=None, username=''): + packagePath = choices.packageName.split('.') + packageDir = PROTO_TOOL_DIR + '/'.join(packagePath) + if not os.path.exists(packageDir): + os.makedirs(packageDir) + + for i in range(len(packagePath)): + init_py = PROTO_TOOL_DIR + '/'.join(packagePath[0:i+1]) + '/__init__.py' + if not os.path.exists(init_py): + print 'creating ', init_py + open(init_py, 'a').close() + + pyname = packageDir + '/' + choices.moduleName + '.py' + + if choices.template == 'Yes': + templatefn = PROTO_TOOL_DIR + 'ToolTemplate.py' + else: + templatefn = PROTO_TOOL_DIR + 'ToolTemplateMinimal.py' + + with open(templatefn) as t: + template = t.read() + + #template = re.sub(r'ToolTemplate', choices.moduleName, template) + template = template.replace('ToolTemplate', choices.moduleName) + template = template.replace('Tool not yet in use', choices.toolName) + + with open(pyname, 'w') as p: + p.write(template) + explore_id = quote(choices.moduleName + ': ' + choices.toolName) + print 'Tool generated: %s: %s' % (URL_PREFIX, explore_id, choices.moduleName, choices.toolName) + print 'Tool source path: ', pyname + + @staticmethod + def getToolDescription(): + from proto.HtmlCore import HtmlCore + core = HtmlCore() + core.smallHeader("General description") + core.paragraph("This tool is used to dynamically generate a Python " + "module defining a new ProTo tool. After tool " + "execution, The tool will be available from the " + "'ProTo tool explorer' tool for development purposes.") + core.divider() + core.smallHeader("Parameters") + core.descriptionLine("Package name", + "The name of the package where the new tool " + "should be installed. The package path is " + "relative to 'proto.tools'. If, for instance, " + "the package is set to 'mypackage.core', the full" + "package hierarchy is 'proto.tools.mypackage." + "core'. Any non-existing directories will be " + "created as needed.", emphasize=True) + core.descriptionLine("Module/class name", + "The name of the Python module (filename) and " + "class for the new tool. For historical reasons, " + "ProTo uses 'MixedCase' naming for both the " + "module and the class. By convention, it is " + "advised (but not required) to end the name " + "with 'Tool', e.g. 'MyNewTool'. This will create " + "a Python module 'MyNewTool.py' with the class " + "'MyNewTool', inheriting from " + "'proto.GeneralGuiTool'.", emphasize=True) + core.descriptionLine("Tool name", + "A string with the name or title of the tool. " + "This will appear on the top of the tool GUI " + "as well as being the default value for the " + "tool name in the menu (which can be changed " + "when installing).", emphasize=True) + core.descriptionLine("Use template with inline documentation", + "The new Python module is based upon a template" + "file containing a simple example tool with " + "two option boxes (one selection box and one " + "text box). There are two such template files, " + "one that contains inline documentation of the " + "methods and possible choices, and one without " + "the documentation. Advanced users could select " + "the latter to make the tool code itself shorter " + "and more readable.", emphasize=True) + return str(core) diff --git a/lib/proto/tools/GenerateToolsTool.xml b/lib/proto/tools/GenerateToolsTool.xml index 77dbda20e4c7..f62838a9e1fc 100644 --- a/lib/proto/tools/GenerateToolsTool.xml +++ b/lib/proto/tools/GenerateToolsTool.xml @@ -1,4 +1,4 @@ + tool_type="proto_generic" proto_tool_module="proto.tools.GenerateToolsTool" proto_tool_class="GenerateToolsTool"> diff --git a/lib/proto/tools/InstallToolsTool.py b/lib/proto/tools/InstallToolsTool.py new file mode 100644 index 000000000000..ada16814566d --- /dev/null +++ b/lib/proto/tools/InstallToolsTool.py @@ -0,0 +1,267 @@ +import os +import re +import shutil +from cgi import escape + +from proto.ProtoToolRegister import getProtoToolList, getInstalledProtoTools +from proto.config.Config import (GALAXY_TOOL_CONFIG_FILE, + GALAXY_TOOL_XML_PATH) +from proto.tools.GeneralGuiTool import GeneralGuiTool + + +class InstallToolsTool(GeneralGuiTool): + prototype = None + + @classmethod + def _getToolList(cls): + installed_classes = getInstalledProtoTools() + return getProtoToolList(installed_classes)[0] + + @classmethod + def _getProtoType(cls, tool): + try: + prototype = cls._getToolList()[tool][2]() + except: + prototype = None + return prototype + + + @staticmethod + def getToolName(): + return "ProTo tool installer" + + @staticmethod + def getInputBoxNames(): + return [('Select tool', 'tool'), + ('Tool type', 'toolType'), + ('Tool ID', 'toolID'), + ('Tool name', 'name'), + ('Tool description', 'description'), + ('Tool XML file', 'toolXMLPath'), + ('Select section', 'section')] + + @staticmethod + def getResetBoxes(): + return [1, 2] + +# @staticmethod +# def isHistoryTool(): +# return False + + @staticmethod + def useSubToolPrefix(): + return True + + @classmethod + def getOptionsBoxTool(cls): + tool_list = cls._getToolList() + return ['-- Select tool --'] + sorted(tool_list) + + @classmethod + def getOptionsBoxToolType(cls, prevchoices): + return ['hb', 'proto'] + + @classmethod + def getOptionsBoxToolID(cls, prevchoices): + import inflection + if prevchoices.tool is None or prevchoices.tool.startswith('--'): + return '' + tool_list = cls._getToolList() + module_name = tool_list[prevchoices.tool][2].__name__ + return prevchoices.toolType + '_' + inflection.underscore(module_name) + + + @classmethod + def getOptionsBoxName(cls, prevchoices): + prototype = cls._getProtoType(prevchoices.tool) + if prototype is not None: + return prototype.getToolName() + + @classmethod + def getOptionsBoxDescription(cls, prevchoices): + return '' + + @classmethod + def getOptionsBoxToolXMLPath(cls, prevchoices): + prototype = cls._getProtoType(prevchoices.tool) + if prototype is not None: + package = prototype.__module__.split('.') + package_dir = '/'.join(package[2:-1]) + '/' if len(package) > 3 else '' + return 'proto/' + package_dir + prototype.__class__.__name__ + '.xml' + + @classmethod + def getOptionsBoxSection(cls, prevchoices): + toolConf = GalaxyToolConfig() + return toolConf.getSections() + + #@classmethod + #def getOptionsBoxInfo(cls, prevchoices): + # txt = '' + # if prevchoices.tool and prevchoices.section: + # txt = 'Install %s into %s' % (prevchoices.tool, prevchoices.section) + # tool_cls = prevchoices.tool + # prototype = cls.prototype + # tool_file = prevchoices.toolXMLPath + # xml = cls.toolConf.addTool(prevchoices.section, tool_file) + # tool_xml = cls.toolConf.createToolXml(tool_file, prevchoices.toolID, prevchoices.name, prototype.__module__, prototype.__class__.__name__, prevchoices.description) + # return 'rawstr', '
' + escape(xml) + '
' + '
' + escape(tool_xml) + '
' + + @classmethod + def validateAndReturnErrors(cls, choices): + if not choices.toolID or len(choices.toolID) < 6 or not re.match(r'^[a-zA-Z0-9_]+$', choices.toolID): + return 'Tool ID must be at least 6 characters and not contain special chars' + return None + + @classmethod + def execute(cls, choices, galaxyFn=None, username=''): + # txt = '' + # if choices.tool and choices.section: + # txt = 'Install %s into %s' % (choices.tool, choices.section) + # tool_cls = choices.tool + + prototype = cls._getProtoType(choices.tool) + tool_file = choices.toolXMLPath + tool_type = choices.toolType + toolConf = GalaxyToolConfig() + xml = toolConf.addTool(choices.section, tool_file) + tool_xml = toolConf.createToolXml(choices.toolID, + choices.name, tool_type, + prototype.__module__, + prototype.__class__.__name__, + choices.description) + + abs_tool_xml_path = GALAXY_TOOL_XML_PATH + choices.toolXMLPath + try: + os.makedirs(os.path.dirname(abs_tool_xml_path)) + except: + pass + with open(abs_tool_xml_path, 'w') as tf: + tf.write(tool_xml) + + toolConf.write() + + from proto.HtmlCore import HtmlCore + core = HtmlCore() + + extraJavaScriptCode = ''' + + ''' + core.begin(extraJavaScriptCode=extraJavaScriptCode) + core.link('Reload toolbox/menu', url='#', args='id="reload_toolbox"') + core.preformatted(escape(xml)) + core.preformatted(escape(tool_xml)) + core.end() + print>>open(galaxyFn, 'w'), core + + @staticmethod + def getToolDescription(): + from proto.HtmlCore import HtmlCore + core = HtmlCore() + core.smallHeader("General description") + core.paragraph( + "This tool is used to install ProTo tools into the tool menu. " + "The installation process creates a Galaxy tool XML file and " + "adds the tool to the tool menu (in the 'tool_conf.xml' file). " + "After execution, the XML file has been generated and added " + "to the tool configuration file, but Galaxy needs to reload " + "the tool menu for it to become visible. This is done by a " + "Galaxy administrator, either from the Admin menu, or from a " + "link in the output history element from this tool.") + core.paragraph("Note that the after this tool has been executed " + "but before a Galaxy administrator has reloaded the " + "tool menu, the tool is not available from neither " + "of the 'ProTo tool explorer' tool or from the " + "Galaxy menu.") + core.divider() + core.smallHeader("Parameters") + + core.descriptionLine("Select tool", "The tool to install.", + emphasize=True) + core.descriptionLine("Tool ID", + "The Galaxy tool id for the new tool to be " + "created. This is the 'id' argument to the " + " tag in the tool XML file.", + emphasize=True) + core.descriptionLine("Tool name", + "The name of the tool as it will appear in the " + "tool menu. The tool name will appear as a HTML " + "link.", emphasize=True) + core.descriptionLine("Tool description", + "The description of the tool as it will appear " + "in the tool menu. The tool description will " + "appear directly after the tool name as " + "normal text.", emphasize=True) + core.descriptionLine("Tool XML file", + "The path (relative to 'tools/proto/') and name " + "of the Galaxy tool XML file to be created. " + "The tool file can be named anything and be " + "placed anywhere (as the 'tool_conf.xml' file" + "contains the path to the tool XML file). " + "However, we encourage the practice of placing " + "the Galaxy tool XML file together with the " + "Python module, in the same directory and " + "with the same name as tool module (with e.g. " + "'ABCTool.xml' instead of 'AbcTool.py').", + emphasize=True) + core.descriptionLine("Select section in tool_conf.xml file", + "The section in the tool_conf.xml file where" + "the tool should be placed in the menu. " + "This corresponds to the first level in the" + "tool hierarchy.", emphasize=True) + return str(core) + + @staticmethod + def getOutputFormat(choices): + return 'customhtml' + + +class GalaxyToolConfig(object): + tool_xml_template = ''' + %s +\n''' + + def __init__(self, tool_conf_fn=GALAXY_TOOL_CONFIG_FILE): + self.tool_conf_fn = tool_conf_fn + with open(self.tool_conf_fn, 'r') as tcf: + self.tool_conf_data = tcf.read() + + def getSections(self): + self.sectionPos = {} + section_names = [] + for m in re.finditer(r'
]+)>', self.tool_conf_data): + attrib = {} + for a in re.findall(r'([^ =]+)="([^"]+)"', m.group(1)): + attrib[a[0]] = a[1] + self.sectionPos[attrib['name']] = m.end(0) + section_names.append(attrib['name']) + return section_names + + def addTool(self, section_name, tool_file): + self.getSections() + tool_tag = '\n\t' % (tool_file,) + pos = self.sectionPos[section_name] + self.tool_conf_data = self.tool_conf_data[:pos] + tool_tag + self.tool_conf_data[pos:] + return self.tool_conf_data + + def write(self): + shutil.copy(self.tool_conf_fn, self.tool_conf_fn + '.bak') + with open(self.tool_conf_fn, 'w') as f: + f.write(self.tool_conf_data) + + def createToolXml(self, tool_id, tool_name, tool_type, tool_module, tool_cls, tool_descr): + tool_xml = self.tool_xml_template % (tool_id, tool_name, tool_type, + tool_module, tool_cls, tool_descr) + return tool_xml diff --git a/lib/proto/tools/InstallToolsTool.xml b/lib/proto/tools/InstallToolsTool.xml index acbc67e41f78..aa139380aa34 100644 --- a/lib/proto/tools/InstallToolsTool.xml +++ b/lib/proto/tools/InstallToolsTool.xml @@ -1,4 +1,4 @@ + tool_type="proto_generic" proto_tool_module="proto.tools.InstallToolsTool" proto_tool_class="InstallToolsTool"> diff --git a/lib/proto/tools/ManageTools.py b/lib/proto/tools/ManageTools.py deleted file mode 100644 index b05762bd74ed..000000000000 --- a/lib/proto/tools/ManageTools.py +++ /dev/null @@ -1,527 +0,0 @@ -from proto.tools.GeneralGuiTool import GeneralGuiTool, MultiGeneralGuiTool -from proto.config.Config import GALAXY_BASE_DIR, GALAXY_REL_TOOL_CONFIG_FILE, \ - URL_PREFIX, PROTO_TOOL_SHELVE_FN -import os, re, shelve, shutil, sys, traceback -from importlib import import_module -from collections import OrderedDict -#import xml.etree.ElementTree as ET -from cgi import escape -from urllib import quote - -SOURCE_CODE_BASE_DIR = GALAXY_BASE_DIR + '/lib' -#HB_TOOL_DIR = GALAXY_BASE_DIR + '/tools/hyperbrowser/new-xml/' -#PROTO_TOOL_DIR = HB_SOURCE_CODE_BASE_DIR + '/quick/webtools/' -PROTO_TOOL_DIR = GALAXY_BASE_DIR + '/lib/proto/tools/' -TOOL_CONF = GALAXY_BASE_DIR + '/' + GALAXY_REL_TOOL_CONFIG_FILE -GALAXY_TOOL_XML_PATH = GALAXY_BASE_DIR + '/tools/' -TOOL_XML_REL_PATH = 'hyperbrowser/' - - -class GalaxyToolConfig: - - tool_xml_template = ''' - %s -\n''' - - def __init__(self, tool_conf_fn=TOOL_CONF): - self.tool_conf_fn = tool_conf_fn - with open(self.tool_conf_fn, 'r') as tcf: - self.tool_conf_data = tcf.read() - - def getSections(self): - self.sectionPos = {} - section_names = [] - for m in re.finditer(r'
]+)>', self.tool_conf_data): - attrib = {} - for a in re.findall(r'([^ =]+)="([^"]+)"', m.group(1)): - attrib[a[0]] = a[1] - self.sectionPos[attrib['name']] = m.end(0) - section_names.append(attrib['name']) - return section_names - - def addTool(self, section_name, tool_file): - self.getSections() - tool_tag = '\n\t' % (tool_file,) - pos = self.sectionPos[section_name] - self.tool_conf_data = self.tool_conf_data[:pos] + tool_tag + self.tool_conf_data[pos:] - return self.tool_conf_data - - def write(self): - shutil.copy(self.tool_conf_fn, self.tool_conf_fn + '.bak') - with open(self.tool_conf_fn, 'w') as f: - f.write(self.tool_conf_data) - - def createToolXml(self, tool_fn, tool_id, tool_name, tool_type, tool_module, tool_cls, tool_descr): - tool_xml = self.tool_xml_template % (tool_id, tool_name, tool_type, - tool_module, tool_cls, tool_descr) - return tool_xml - - -def getProtoToolList(except_class_names=[]): - except_class_names.append('ToolTemplate') - except_class_names.append('ToolTemplateMinimal') - tmp_tools = {} - tools = {} - tool_classes = [] - all_installed_sub_classes = set() - pys = [] - for d in os.walk(PROTO_TOOL_DIR, followlinks=True): - if d[0].find('.svn') == -1: - pys += [os.path.join(d[0], f) for f in d[2] if f.endswith('.py') and not any(f.startswith(x) for x in ['.', '#'])] - - # To fix import issue if there are modules in /lib and /lib/proto with the - # same name (e.g. 'config'). - tmpSysPath = sys.path - if sys.path[0].endswith('/proto'): - sys.path = tmpSysPath[1:] - - #print 'Num py', len(pys) - for fn in pys: - with open(fn) as f: - for line in f: - m = re.match(r'class +(\w+) *\(([\w ,]+)\)', line) - if m: - class_name = m.group(1) - module_name = os.path.splitext(os.path.relpath(os.path.abspath(fn), SOURCE_CODE_BASE_DIR))[0].replace(os.path.sep, '.') - try: - if module_name != __name__: - module = import_module(module_name) - prototype_cls = getattr(module, class_name) - if issubclass(prototype_cls, GeneralGuiTool): - if issubclass(prototype_cls, MultiGeneralGuiTool): - if class_name in except_class_names and prototype_cls.getSubToolClasses(): - for sub_cls in prototype_cls.getSubToolClasses(): - all_installed_sub_classes.add(sub_cls) - elif hasattr(prototype_cls, 'getToolName'): - if class_name not in except_class_names: - prototype = prototype_cls('hb_no_tool_id_yet') - tool_module = module_name.split('.')[2:] - if class_name != tool_module[-1]: - tool_selection_name = '.'.join(tool_module) + ' [' + class_name + ']' - else: - tool_selection_name = '.'.join(tool_module) - - # print (fn, m.group(2), prototype_cls, module_name) - tmp_tools[tool_selection_name] = (fn, m.group(2), prototype_cls, module_name) - except Exception as e: - traceback.print_exc() - #break - #print 'Num protopy', len(tools) - - for tool_selection_name, tool_info in tmp_tools.iteritems(): - prototype_cls = tool_info[2] - if prototype_cls not in all_installed_sub_classes: - tools[tool_selection_name] = tool_info - tool_classes.append(prototype_cls) - - sys.path = tmpSysPath - return tools, tool_classes - - -class ExploreToolsTool(MultiGeneralGuiTool): - @staticmethod - def getToolName(): - return "ProTo tool explorer" - - @staticmethod - def getToolSelectionName(): - return "----- Select tool -----" - - @staticmethod - def useSubToolPrefix(): - return True - - @classmethod - def getSubToolClasses(cls): - tool_shelve = shelve.open(PROTO_TOOL_SHELVE_FN, 'r') - installed_classes = [tool_shelve.get(t)[1] for t in tool_shelve.keys() - if os.path.exists(os.path.join(SOURCE_CODE_BASE_DIR, tool_shelve.get(t)[0].replace('.', os.path.sep)) + '.py') ] - tool_shelve.close() - tool_list = getProtoToolList(installed_classes)[1] - return sorted(tool_list, key=lambda c: c.__module__) - - @staticmethod - def getToolDescription(): - from proto.HtmlCore import HtmlCore - core = HtmlCore() - core.smallHeader("General description") - core.paragraph("This tool is used to try out ProTo tools that have " - "not been installed as separate tools in the tool " - "menu. This is typically used for development " - "purposes, so that one can polish the tool until it" - "is finished for deployment in the tool menu. " - "When a tool is installed into the menu, the tool " - "disappears from the tool list in this tool." - "The logic for inclusion in the list is that there " - "exists a Python module with a class that inherits " - "from GeneralGuiTool, without there existing " - "a Galaxy xml file for the tool.") - return str(core) - - - - #@classmethod - #def getOptionsBoxInstalled(cls): - # cls.existing_tools = cls.get_existing_tool_xml_list() - # tool_shelve = shelve.open(TOOL_SHELVE, 'r') - # cls.installed_classes = [tool_shelve.get(t)[1] for t in tool_shelve.keys()] - # tool_shelve.close() - # return sorted(cls.installed_classes) - # - # - #@classmethod - #def getOptionsBoxProtoTools(cls, choices): - # proto_tools = sorted(cls.get_proto_tool_list().keys()) - # return None - # - #@classmethod - #def getOptionsBoxOrphanTools(cls, choices): - # orphans = [t for t in cls.get_proto_tool_list().keys() if t not in cls.installed_classes] - # return sorted(orphans) - - #@classmethod - #def get_existing_tool_xml_list(cls): - # tools = {} - # xmls = [x for x in os.listdir(HB_TOOL_DIR) if x.endswith('.xml')] - # for fn in xmls: - # tree = ET.parse(HB_TOOL_DIR + fn) - # root = tree.getroot() - # if root.tag == 'tool' and root.attrib.get('tool_type') == 'hyperbrowser_generic': - # tool_id = root.attrib['id'] - # tools[tool_id] = root.attrib - # return tools - - - -class InstallToolsTool(GeneralGuiTool): - prototype = None - - @classmethod - def _getToolList(cls): - tool_shelve = shelve.open(PROTO_TOOL_SHELVE_FN, 'r') - tool_IDs = set(tool_shelve.keys()) - installed_classes = [tool_shelve.get(t)[1] for t in tool_IDs] - tool_shelve.close() - return getProtoToolList(installed_classes)[0] - - @classmethod - def _getProtoType(cls, tool): - try: - prototype = cls._getToolList()[tool][2]() - except: - prototype = None - return prototype - - - @staticmethod - def getToolName(): - return "ProTo tool installer" - - @staticmethod - def getInputBoxNames(): - return [('Select tool', 'tool'), - ('Tool type', 'toolType'), - ('Tool ID', 'toolID'), - ('Tool name', 'name'), - ('Tool description', 'description'), - ('Tool XML file', 'toolXMLPath'), - ('Select section', 'section')] - - @staticmethod - def getResetBoxes(): - return [1, 2] - -# @staticmethod -# def isHistoryTool(): -# return False - - @staticmethod - def useSubToolPrefix(): - return True - - @classmethod - def getOptionsBoxTool(cls): - tool_list = cls._getToolList() - return ['-- Select tool --'] + sorted(tool_list) - - @classmethod - def getOptionsBoxToolType(cls, prevchoices): - return ['proto'] - - @classmethod - def getOptionsBoxToolID(cls, prevchoices): - import inflection - if prevchoices.tool is None or prevchoices.tool.startswith('--'): - return '' - tool_list = cls._getToolList() - module_name = tool_list[prevchoices.tool][2].__name__ - return prevchoices.toolType + '_' + inflection.underscore(module_name) - - - @classmethod - def getOptionsBoxName(cls, prevchoices): - prototype = cls._getProtoType(prevchoices.tool) - if prototype is not None: - return prototype.getToolName() - - @classmethod - def getOptionsBoxDescription(cls, prevchoices): - return '' - - @classmethod - def getOptionsBoxToolXMLPath(cls, prevchoices): - prototype = cls._getProtoType(prevchoices.tool) - if prototype is not None: - package = prototype.__module__.split('.') - package_dir = '/'.join(package[2:-1]) + '/' if len(package) > 3 else '' - return 'proto/' + package_dir + prototype.__class__.__name__ + '.xml' - - @classmethod - def getOptionsBoxSection(cls, prevchoices): - toolConf = GalaxyToolConfig() - return toolConf.getSections() - - #@classmethod - #def getOptionsBoxInfo(cls, prevchoices): - # txt = '' - # if prevchoices.tool and prevchoices.section: - # txt = 'Install %s into %s' % (prevchoices.tool, prevchoices.section) - # tool_cls = prevchoices.tool - # prototype = cls.prototype - # #tool_file = TOOL_XML_REL_PATH + tool_cls + '.xml' - # tool_file = prevchoices.toolXMLPath - # xml = cls.toolConf.addTool(prevchoices.section, tool_file) - # tool_xml = cls.toolConf.createToolXml(tool_file, prevchoices.toolID, prevchoices.name, prototype.__module__, prototype.__class__.__name__, prevchoices.description) - # return 'rawstr', '
' + escape(xml) + '
' + '
' + escape(tool_xml) + '
' - - @classmethod - def validateAndReturnErrors(cls, choices): - if not choices.toolID or len(choices.toolID) < 6 or not re.match(r'^[a-zA-Z0-9_]+$', choices.toolID): - return 'Tool ID must be at least 6 characters and not contain special chars' - return None - - @classmethod - def execute(cls, choices, galaxyFn=None, username=''): - txt = '' - if choices.tool and choices.section: - txt = 'Install %s into %s' % (choices.tool, choices.section) - tool_cls = choices.tool - prototype = cls._getProtoType(choices.tool) - tool_file = choices.toolXMLPath - tool_type = choices.toolType - toolConf = GalaxyToolConfig() - xml = toolConf.addTool(choices.section, tool_file) - tool_xml = toolConf.createToolXml(tool_file, choices.toolID, - choices.name, prototype.__module__, - prototype.__class__.__name__, - choices.description) - - abs_tool_xml_path = GALAXY_TOOL_XML_PATH + choices.toolXMLPath - try: - os.makedirs(os.path.dirname(abs_tool_xml_path)) - except: - pass - with open(abs_tool_xml_path, 'w') as tf: - tf.write(tool_xml) - - toolConf.write() - - from proto.HtmlCore import HtmlCore - core = HtmlCore() - - extraJavaScriptCode = ''' - - ''' - core.begin(extraJavaScriptCode=extraJavaScriptCode) - core.link('Reload toolbox/menu', url='#', args='id="reload_toolbox"') - core.preformatted(escape(xml)) - core.preformatted(escape(tool_xml)) - core.end() - print>>open(galaxyFn, 'w'), core - - @staticmethod - def getToolDescription(): - from proto.HtmlCore import HtmlCore - core = HtmlCore() - core.smallHeader("General description") - core.paragraph( - "This tool is used to install ProTo tools into the tool menu. " - "The installation process creates a Galaxy tool XML file and " - "adds the tool to the tool menu (in the 'tool_conf.xml' file). " - "After execution, the XML file has been generated and added " - "to the tool configuration file, but Galaxy needs to reload " - "the tool menu for it to become visible. This is done by a " - "Galaxy administrator, either from the Admin menu, or from a " - "link in the output history element from this tool.") - core.paragraph("Note that the after this tool has been executed " - "but before a Galaxy administrator has reloaded the " - "tool menu, the tool is not available from neither " - "of the 'ProTo tool explorer' tool or from the " - "Galaxy menu.") - core.divider() - core.smallHeader("Parameters") - - core.descriptionLine("Select tool", "The tool to install.", - emphasize=True) - core.descriptionLine("Tool ID", - "The Galaxy tool id for the new tool to be " - "created. This is the 'id' argument to the " - " tag in the tool XML file.", - emphasize=True) - core.descriptionLine("Tool name", - "The name of the tool as it will appear in the " - "tool menu. The tool name will appear as a HTML " - "link.", emphasize=True) - core.descriptionLine("Tool description", - "The description of the tool as it will appear " - "in the tool menu. The tool description will " - "appear directly after the tool name as " - "normal text.", emphasize=True) - core.descriptionLine("Tool XML file", - "The path (relative to 'tools/proto/') and name " - "of the Galaxy tool XML file to be created. " - "The tool file can be named anything and be " - "placed anywhere (as the 'tool_conf.xml' file" - "contains the path to the tool XML file). " - "However, we encourage the practice of placing " - "the Galaxy tool XML file together with the " - "Python module, in the same directory and " - "with the same name as tool module (with e.g. " - "'ABCTool.xml' instead of 'AbcTool.py').", - emphasize=True) - core.descriptionLine("Select section in tool_conf.xml file", - "The section in the tool_conf.xml file where" - "the tool should be placed in the menu. " - "This corresponds to the first level in the" - "tool hierarchy.", emphasize=True) - return str(core) - - @staticmethod - def getOutputFormat(choices): - return 'customhtml' - - -class GenerateToolsTool(GeneralGuiTool): - @staticmethod - def getToolName(): - return "ProTo tool generator" - - @staticmethod - def getInputBoxNames(): - return [('Package name', 'packageName'), - ('Module/class name', 'moduleName'), - ('Tool name', 'toolName'), - ('Use template with inline documentation', 'template')] - - #@staticmethod - #def getResetBoxes(): - # return ['moduleName'] - - @staticmethod - def getOptionsBoxPackageName(): - return '' - - @staticmethod - def getOptionsBoxModuleName(prevchoices): - return 'ChangeMeTool' - - @staticmethod - def getOptionsBoxToolName(prevchoices): - return 'Title of tool' - - @staticmethod - def getOptionsBoxTemplate(prevchoices): - return ['Yes', 'No'] - - @staticmethod - def execute(choices, galaxyFn=None, username=''): - packagePath = choices.packageName.split('.') - packageDir = PROTO_TOOL_DIR + '/'.join(packagePath) - if not os.path.exists(packageDir): - os.makedirs(packageDir) - - for i in range(len(packagePath)): - init_py = PROTO_TOOL_DIR + '/'.join(packagePath[0:i+1]) + '/__init__.py' - if not os.path.exists(init_py): - print 'creating ', init_py - open(init_py, 'a').close() - - pyname = packageDir + '/' + choices.moduleName + '.py' - - if choices.template == 'Yes': - templatefn = PROTO_TOOL_DIR + 'ToolTemplate.py' - else: - templatefn = PROTO_TOOL_DIR + 'ToolTemplateMinimal.py' - - with open(templatefn) as t: - template = t.read() - - #template = re.sub(r'ToolTemplate', choices.moduleName, template) - template = template.replace('ToolTemplate', choices.moduleName) - template = template.replace('Tool not yet in use', choices.toolName) - - with open(pyname, 'w') as p: - p.write(template) - explore_id = quote(choices.moduleName + ': ' + choices.toolName) - print 'Tool generated: %s: %s' % (URL_PREFIX, explore_id, choices.moduleName, choices.toolName) - print 'Tool source path: ', pyname - - @staticmethod - def getToolDescription(): - from proto.HtmlCore import HtmlCore - core = HtmlCore() - core.smallHeader("General description") - core.paragraph("This tool is used to dynamically generate a Python " - "module defining a new ProTo tool. After tool " - "execution, The tool will be available from the " - "'ProTo tool explorer' tool for development purposes.") - core.divider() - core.smallHeader("Parameters") - core.descriptionLine("Package name", - "The name of the package where the new tool " - "should be installed. The package path is " - "relative to 'proto.tools'. If, for instance, " - "the package is set to 'mypackage.core', the full" - "package hierarchy is 'proto.tools.mypackage." - "core'. Any non-existing directories will be " - "created as needed.", emphasize=True) - core.descriptionLine("Module/class name", - "The name of the Python module (filename) and " - "class for the new tool. For historical reasons, " - "ProTo uses 'MixedCase' naming for both the " - "module and the class. By convention, it is " - "advised (but not required) to end the name " - "with 'Tool', e.g. 'MyNewTool'. This will create " - "a Python module 'MyNewTool.py' with the class " - "'MyNewTool', inheriting from " - "'proto.GeneralGuiTool'.", emphasize=True) - core.descriptionLine("Tool name", - "A string with the name or title of the tool. " - "This will appear on the top of the tool GUI " - "as well as being the default value for the " - "tool name in the menu (which can be changed " - "when installing).", emphasize=True) - core.descriptionLine("Use template with inline documentation", - "The new Python module is based upon a template" - "file containing a simple example tool with " - "two option boxes (one selection box and one " - "text box). There are two such template files, " - "one that contains inline documentation of the " - "methods and possible choices, and one without " - "the documentation. Advanced users could select " - "the latter to make the tool code itself shorter " - "and more readable.", emphasize=True) - return str(core) From f02e10f060e40bdab4f6e7b05559e540e862266a Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 23 Feb 2017 06:16:56 +0100 Subject: [PATCH 056/123] ProTo: removed logging (cherry picked from commit bafb54d070229c45fbab30a531736a50eaa29fa0) --- lib/proto/ProtoToolRegister.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/proto/ProtoToolRegister.py b/lib/proto/ProtoToolRegister.py index af34c023887c..e377e6e45d31 100644 --- a/lib/proto/ProtoToolRegister.py +++ b/lib/proto/ProtoToolRegister.py @@ -71,8 +71,6 @@ def getProtoToolList(exceptClassNames=[]): tool_selection_name = '.'.join(tool_module) # print (fn, m.group(2), prototype_cls, module_name) - from gold.application.LogSetup import logMessage - logMessage(repr((fn, m.group(2), prototype_cls, module_name))) tmp_tools[tool_selection_name] = \ (fn, m.group(2), prototype_cls, module_name) except Exception as e: From e9dcd4ae4868226086ea9c777947f52d9a3a3691 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 23 Feb 2017 06:19:38 +0100 Subject: [PATCH 057/123] ProTo: Unfinished update of core dev tools. Added directory listing and the possibility for explicit creation of new dirs. Some cleanup (cherry picked from commit 69bebcdbc07a1e4605208d839ba204a20e78c168) --- lib/proto/tools/GenerateToolsTool.py | 65 ++++++++++++++++++++++++++-- lib/proto/tools/InstallToolsTool.py | 2 +- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/lib/proto/tools/GenerateToolsTool.py b/lib/proto/tools/GenerateToolsTool.py index 5a3cc81ee831..b320b6a82c1a 100644 --- a/lib/proto/tools/GenerateToolsTool.py +++ b/lib/proto/tools/GenerateToolsTool.py @@ -6,13 +6,23 @@ class GenerateToolsTool(GeneralGuiTool): + MAX_DIR_LEVELS = 5 + NO_SELECTION = '--- Select a tool directory ---' + NEW_DIR = '-- Create a new directory --' + @staticmethod def getToolName(): return "ProTo tool generator" - @staticmethod - def getInputBoxNames(): - return [('Package name', 'packageName'), + @classmethod + def getInputBoxNames(cls): + return [('', 'hidden')] + \ + [(' ' * i + ('|_' if i > 0 else '') + + 'Choose directory for new tool (level %s)' % (i+1), 'dirLevel%s' % i) + for i in range(cls.MAX_DIR_LEVELS)] + \ + [(' ' * i + 'Name of new directory', 'newName%s' % i) + for i in range(cls.MAX_DIR_LEVELS)] + \ + [('Package name', 'packageName'), ('Module/class name', 'moduleName'), ('Tool name', 'toolName'), ('Use template with inline documentation', 'template')] @@ -21,6 +31,53 @@ def getInputBoxNames(): #def getResetBoxes(): # return ['moduleName'] + @staticmethod + def getOptionsBoxHidden(): + return '__hidden__' + + @classmethod + def _getSelectedDir(cls, prevChoices, index=MAX_DIR_LEVELS): + dirs = [] + for i in range(index): + dirSelection = getattr(prevChoices, 'dirLevel' + i) + + if dirSelection == cls.NEW_DIR: + dirSelection = getattr(prevChoices, 'newDir' + i).strip() + + if dirSelection != cls.NO_SELECTION: + dirs.append(dirSelection) + else: + break + + return os.path.sep.join([PROTO_TOOL_DIR] + dirs) + + @classmethod + def _getOptionsBoxDirLevel(cls, prevChoices, index): + from gold.application.LogSetup import logMessage + logMessage(repr(prevChoices)) + if index == 0 or getattr(prevChoices, 'dirLevel' + (index - 1)) != cls.NO_SELECTION: + selectedDir = cls._getSelectedDir(prevChoices, index) + subDirs = [x for x in os.listdir(selectedDir) if + os.path.isdir(os.sep.join(selectedDir, x))] + return [cls.NO_SELECTION] + subDirs + [cls.NEW_DIR] + + @classmethod + def _getOptionsBoxNewDir(cls, prevChoices, index): + curDirChoice = getattr(prevChoices, 'dirLevel' + index) + if curDirChoice == cls.NEW_DIR: + return '', 1 + + @classmethod + def setupExtraBoxMethods(cls): + from functools import partial + for i in xrange(cls.MAX_DIR_LEVELS): + setattr(cls, 'getOptionsBoxDirLevel%s' % i, + partial(cls._getOptionsBoxDirLevel, index=i)) + setattr(cls, 'getOptionsBoxNewDir%s' % i, + partial(cls._getOptionsBoxNewDir, index=i)) + + from gold.application.LogSetup import logMessage + @staticmethod def getOptionsBoxPackageName(): return '' @@ -116,3 +173,5 @@ def getToolDescription(): "the latter to make the tool code itself shorter " "and more readable.", emphasize=True) return str(core) + +GenerateToolsTool.setupExtraBoxMethods() diff --git a/lib/proto/tools/InstallToolsTool.py b/lib/proto/tools/InstallToolsTool.py index ada16814566d..5a0c7cf296fc 100644 --- a/lib/proto/tools/InstallToolsTool.py +++ b/lib/proto/tools/InstallToolsTool.py @@ -59,7 +59,7 @@ def getOptionsBoxTool(cls): @classmethod def getOptionsBoxToolType(cls, prevchoices): - return ['hb', 'proto'] + return ['proto'] @classmethod def getOptionsBoxToolID(cls, prevchoices): From 2059ac7f2a65006c8a7cab5083d60a0a62e0d878 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 23 Feb 2017 19:20:47 +0100 Subject: [PATCH 058/123] ProTo: Finished support for package selection via subdirectories in GenerateToolsTool. Added validation and some polishing. Renaming of some variables. Updated config paths. (cherry picked from commit bef991f4c1cd804b6d08565a1bc548e6d91f9fa4) --- lib/proto/config/Config.py | 4 +- lib/proto/generictool.py | 12 +-- lib/proto/tools/GenerateToolsTool.py | 142 +++++++++++++++++++-------- lib/proto/tools/InstallToolsTool.py | 38 +++---- 4 files changed, 127 insertions(+), 69 deletions(-) diff --git a/lib/proto/config/Config.py b/lib/proto/config/Config.py index 795192b81ad1..0e401b6d0a49 100644 --- a/lib/proto/config/Config.py +++ b/lib/proto/config/Config.py @@ -21,8 +21,8 @@ def getUrlPrefix(config): OUTPUT_PRECISION = int(config.getWithDefault('output_precision', '4', 'galaxy_proto')) GALAXY_TOOL_CONFIG_FILE = GALAXY_BASE_DIR + '/' + GALAXY_REL_TOOL_CONFIG_FILE -GALAXY_TOOL_XML_PATH = GALAXY_BASE_DIR + '/tools/' -PROTO_TOOL_DIR = GALAXY_BASE_DIR + '/lib/proto/tools/' +GALAXY_TOOL_XML_PATH = GALAXY_BASE_DIR + '/tools' +PROTO_TOOL_DIR = GALAXY_BASE_DIR + '/lib/proto/tools' PROTO_TOOL_SHELVE_FN = GALAXY_BASE_DIR + '/database/proto-tool-cache.shelve' SOURCE_CODE_BASE_DIR = GALAXY_BASE_DIR + '/lib' STATIC_DIR = '/static/proto' diff --git a/lib/proto/generictool.py b/lib/proto/generictool.py index 6b64d4463a2e..f91393ceffa4 100644 --- a/lib/proto/generictool.py +++ b/lib/proto/generictool.py @@ -177,18 +177,18 @@ def _getOptionsBox(self, i, val = None): info = None if i > 0: ChoiceTuple = namedtuple('ChoiceTuple', self.inputIds[:(i+1)]) - prevchoices = ChoiceTuple._make(self.inputValues + [val]) - #self.choices = prevchoices + prevChoices = ChoiceTuple._make(self.inputValues + [val]) + #self.choices = prevChoices if id.startswith('Box'): - opts = getattr(self.prototype, 'getOptions' + id)(prevchoices) + opts = getattr(self.prototype, 'getOptions' + id)(prevChoices) try: - info = getattr(self.prototype, 'getInfoForOptions' + id)(prevchoices) + info = getattr(self.prototype, 'getInfoForOptions' + id)(prevChoices) except: pass else: - opts = getattr(self.prototype, 'getOptionsBox' + id)(prevchoices) + opts = getattr(self.prototype, 'getOptionsBox' + id)(prevChoices) try: - info = getattr(self.prototype, 'getInfoForOptionsBox' + id)(prevchoices) + info = getattr(self.prototype, 'getInfoForOptionsBox' + id)(prevChoices) except: pass else: diff --git a/lib/proto/tools/GenerateToolsTool.py b/lib/proto/tools/GenerateToolsTool.py index b320b6a82c1a..2f28c3621074 100644 --- a/lib/proto/tools/GenerateToolsTool.py +++ b/lib/proto/tools/GenerateToolsTool.py @@ -1,28 +1,42 @@ import os from urllib import quote -from proto.config.Config import URL_PREFIX, PROTO_TOOL_DIR +from proto.HtmlCore import HtmlCore +from proto.config.Config import URL_PREFIX, PROTO_TOOL_DIR, SOURCE_CODE_BASE_DIR from proto.tools.GeneralGuiTool import GeneralGuiTool class GenerateToolsTool(GeneralGuiTool): MAX_DIR_LEVELS = 5 NO_SELECTION = '--- Select a tool directory ---' - NEW_DIR = '-- Create a new directory --' + NEW_DIR = 'Create a new directory...' + + assert PROTO_TOOL_DIR.startswith(SOURCE_CODE_BASE_DIR) + PROTO_REL_TOOL_DIRS = PROTO_TOOL_DIR[len(SOURCE_CODE_BASE_DIR) + 1:].split(os.path.sep) @staticmethod def getToolName(): return "ProTo tool generator" + @classmethod + def _getDirSelectionInputBoxNames(cls): + inputBoxNames = [] + for i in range(cls.MAX_DIR_LEVELS): + dirSelText = '  ' * i + if i > 0: + dirSelText += '-> ' + dirSelText += 'Choose directory for new tool (level %s)' % (i + 1) + inputBoxNames += [(dirSelText, 'dirLevel%s' % i)] + + newDirText = '  ' * i + 'Name of new directory' + inputBoxNames += [(newDirText, 'newDir%s' % i)] + return inputBoxNames + @classmethod def getInputBoxNames(cls): return [('', 'hidden')] + \ - [(' ' * i + ('|_' if i > 0 else '') + - 'Choose directory for new tool (level %s)' % (i+1), 'dirLevel%s' % i) - for i in range(cls.MAX_DIR_LEVELS)] + \ - [(' ' * i + 'Name of new directory', 'newName%s' % i) - for i in range(cls.MAX_DIR_LEVELS)] + \ - [('Package name', 'packageName'), + cls._getDirSelectionInputBoxNames() +\ + [('Package name', 'packageNameInfo'), ('Module/class name', 'moduleName'), ('Tool name', 'toolName'), ('Use template with inline documentation', 'template')] @@ -33,37 +47,47 @@ def getInputBoxNames(cls): @staticmethod def getOptionsBoxHidden(): - return '__hidden__' + # Just to make sure that the variable input boxes always take prevChoices + return '__hidden__', '' @classmethod - def _getSelectedDir(cls, prevChoices, index=MAX_DIR_LEVELS): + def _getSelectedDirs(cls, prevChoices, index=MAX_DIR_LEVELS): dirs = [] for i in range(index): - dirSelection = getattr(prevChoices, 'dirLevel' + i) + dirSelection = getattr(prevChoices, 'dirLevel%s' % i) if dirSelection == cls.NEW_DIR: - dirSelection = getattr(prevChoices, 'newDir' + i).strip() + dirSelection = getattr(prevChoices, 'newDir%s' % i).strip() - if dirSelection != cls.NO_SELECTION: + if dirSelection and dirSelection != cls.NO_SELECTION: dirs.append(dirSelection) else: break - return os.path.sep.join([PROTO_TOOL_DIR] + dirs) + return dirs @classmethod def _getOptionsBoxDirLevel(cls, prevChoices, index): - from gold.application.LogSetup import logMessage - logMessage(repr(prevChoices)) - if index == 0 or getattr(prevChoices, 'dirLevel' + (index - 1)) != cls.NO_SELECTION: - selectedDir = cls._getSelectedDir(prevChoices, index) - subDirs = [x for x in os.listdir(selectedDir) if - os.path.isdir(os.sep.join(selectedDir, x))] + prevDirLevelKey = 'dirLevel%s' % (index - 1) + prevDirLevel = getattr(prevChoices, prevDirLevelKey) if \ + hasattr(prevChoices, prevDirLevelKey) else '' + if index == 0 or \ + (prevDirLevel and prevDirLevel != cls.NO_SELECTION and + not (prevDirLevel == cls.NEW_DIR and + not getattr(prevChoices, 'newDir%s' % (index - 1)))): + + selectedDir = os.path.sep.join([PROTO_TOOL_DIR] + + cls._getSelectedDirs(prevChoices, index)) + try: + subDirs = [x for x in os.listdir(selectedDir) if + os.path.isdir(os.sep.join([selectedDir, x]))] + except: + subDirs = [] return [cls.NO_SELECTION] + subDirs + [cls.NEW_DIR] @classmethod def _getOptionsBoxNewDir(cls, prevChoices, index): - curDirChoice = getattr(prevChoices, 'dirLevel' + index) + curDirChoice = getattr(prevChoices, 'dirLevel%s' % index) if curDirChoice == cls.NEW_DIR: return '', 1 @@ -78,41 +102,57 @@ def setupExtraBoxMethods(cls): from gold.application.LogSetup import logMessage - @staticmethod - def getOptionsBoxPackageName(): - return '' + @classmethod + def _getProtoToolPackageName(cls, prevChoices): + return '.'.join(cls.PROTO_REL_TOOL_DIRS + cls._getSelectedDirs(prevChoices)) + + @classmethod + def getOptionsBoxPackageNameInfo(cls, prevChoices): + core = HtmlCore() + core.divBegin(divClass='infomessagesmall') + core.append('Package name selected: ') + core.emphasize(cls._getProtoToolPackageName(prevChoices)) + core.divEnd() + return '__rawstr__', str(core) @staticmethod - def getOptionsBoxModuleName(prevchoices): + def getOptionsBoxModuleName(prevChoices): return 'ChangeMeTool' @staticmethod - def getOptionsBoxToolName(prevchoices): + def getOptionsBoxToolName(prevChoices): return 'Title of tool' @staticmethod - def getOptionsBoxTemplate(prevchoices): + def getOptionsBoxTemplate(prevChoices): return ['Yes', 'No'] - @staticmethod - def execute(choices, galaxyFn=None, username=''): - packagePath = choices.packageName.split('.') - packageDir = PROTO_TOOL_DIR + '/'.join(packagePath) + @classmethod + def _getPackageDir(cls, selectedDirs): + return os.path.sep.join([PROTO_TOOL_DIR] + selectedDirs) + + @classmethod + def _getPyName(cls, choices): + packageDir = cls._getPackageDir(cls._getSelectedDirs(choices)) + return packageDir + '/' + choices.moduleName + '.py' + + @classmethod + def execute(cls, choices, galaxyFn=None, username=''): + selectedDirs = cls._getSelectedDirs(choices) + packageDir = cls._getPackageDir(selectedDirs) if not os.path.exists(packageDir): os.makedirs(packageDir) - for i in range(len(packagePath)): - init_py = PROTO_TOOL_DIR + '/'.join(packagePath[0:i+1]) + '/__init__.py' + for i in range(len(selectedDirs)): + init_py = os.path.sep.join([PROTO_TOOL_DIR] + selectedDirs[0:i+1]) + '/__init__.py' if not os.path.exists(init_py): print 'creating ', init_py open(init_py, 'a').close() - pyname = packageDir + '/' + choices.moduleName + '.py' - if choices.template == 'Yes': - templatefn = PROTO_TOOL_DIR + 'ToolTemplate.py' + templatefn = os.path.join(PROTO_TOOL_DIR, 'ToolTemplate.py') else: - templatefn = PROTO_TOOL_DIR + 'ToolTemplateMinimal.py' + templatefn = os.path.join(PROTO_TOOL_DIR, 'ToolTemplateMinimal.py') with open(templatefn) as t: template = t.read() @@ -121,15 +161,29 @@ def execute(choices, galaxyFn=None, username=''): template = template.replace('ToolTemplate', choices.moduleName) template = template.replace('Tool not yet in use', choices.toolName) - with open(pyname, 'w') as p: + pyName = cls._getPyName(choices) + with open(pyName, 'w') as p: p.write(template) - explore_id = quote(choices.moduleName + ': ' + choices.toolName) - print 'Tool generated: %s: %s' % (URL_PREFIX, explore_id, choices.moduleName, choices.toolName) - print 'Tool source path: ', pyname + + explore_id = quote('.'.join(selectedDirs + [choices.moduleName]) + ': ' + choices.toolName) + print 'Tool generated: %s: %s' % \ + (URL_PREFIX, explore_id, choices.moduleName, choices.toolName) + print 'Tool source path:', pyName + + @classmethod + def validateAndReturnErrors(cls, choices): + packageName = cls._getProtoToolPackageName(choices) + if packageName and packageName != packageName.lower(): + return 'Please use all lowercase letters for the package name: ' + packageName + + pyName = cls._getPyName(choices) + if os.path.exists(pyName): + return 'Python module "%s" already exists. Please rename the module or ' % pyName + \ + 'select another package/directory.' @staticmethod def getToolDescription(): - from proto.HtmlCore import HtmlCore core = HtmlCore() core.smallHeader("General description") core.paragraph("This tool is used to dynamically generate a Python " @@ -174,4 +228,8 @@ def getToolDescription(): "and more readable.", emphasize=True) return str(core) + # @classmethod + # def getResetBoxes(cls): + # return flatten([('dirLevel%s' % i, 'newDir%s' % i) for i in range(cls.MAX_DIR_LEVELS)]) + GenerateToolsTool.setupExtraBoxMethods() diff --git a/lib/proto/tools/InstallToolsTool.py b/lib/proto/tools/InstallToolsTool.py index 5a0c7cf296fc..f4353e1c1f44 100644 --- a/lib/proto/tools/InstallToolsTool.py +++ b/lib/proto/tools/InstallToolsTool.py @@ -58,52 +58,52 @@ def getOptionsBoxTool(cls): return ['-- Select tool --'] + sorted(tool_list) @classmethod - def getOptionsBoxToolType(cls, prevchoices): + def getOptionsBoxToolType(cls, prevChoices): return ['proto'] @classmethod - def getOptionsBoxToolID(cls, prevchoices): + def getOptionsBoxToolID(cls, prevChoices): import inflection - if prevchoices.tool is None or prevchoices.tool.startswith('--'): + if prevChoices.tool is None or prevChoices.tool.startswith('--'): return '' tool_list = cls._getToolList() - module_name = tool_list[prevchoices.tool][2].__name__ - return prevchoices.toolType + '_' + inflection.underscore(module_name) + module_name = tool_list[prevChoices.tool][2].__name__ + return prevChoices.toolType + '_' + inflection.underscore(module_name) @classmethod - def getOptionsBoxName(cls, prevchoices): - prototype = cls._getProtoType(prevchoices.tool) + def getOptionsBoxName(cls, prevChoices): + prototype = cls._getProtoType(prevChoices.tool) if prototype is not None: return prototype.getToolName() @classmethod - def getOptionsBoxDescription(cls, prevchoices): + def getOptionsBoxDescription(cls, prevChoices): return '' @classmethod - def getOptionsBoxToolXMLPath(cls, prevchoices): - prototype = cls._getProtoType(prevchoices.tool) + def getOptionsBoxToolXMLPath(cls, prevChoices): + prototype = cls._getProtoType(prevChoices.tool) if prototype is not None: package = prototype.__module__.split('.') package_dir = '/'.join(package[2:-1]) + '/' if len(package) > 3 else '' return 'proto/' + package_dir + prototype.__class__.__name__ + '.xml' @classmethod - def getOptionsBoxSection(cls, prevchoices): + def getOptionsBoxSection(cls, prevChoices): toolConf = GalaxyToolConfig() return toolConf.getSections() #@classmethod - #def getOptionsBoxInfo(cls, prevchoices): + #def getOptionsBoxInfo(cls, prevChoices): # txt = '' - # if prevchoices.tool and prevchoices.section: - # txt = 'Install %s into %s' % (prevchoices.tool, prevchoices.section) - # tool_cls = prevchoices.tool + # if prevChoices.tool and prevChoices.section: + # txt = 'Install %s into %s' % (prevChoices.tool, prevChoices.section) + # tool_cls = prevChoices.tool # prototype = cls.prototype - # tool_file = prevchoices.toolXMLPath - # xml = cls.toolConf.addTool(prevchoices.section, tool_file) - # tool_xml = cls.toolConf.createToolXml(tool_file, prevchoices.toolID, prevchoices.name, prototype.__module__, prototype.__class__.__name__, prevchoices.description) + # tool_file = prevChoices.toolXMLPath + # xml = cls.toolConf.addTool(prevChoices.section, tool_file) + # tool_xml = cls.toolConf.createToolXml(tool_file, prevChoices.toolID, prevChoices.name, prototype.__module__, prototype.__class__.__name__, prevChoices.description) # return 'rawstr', '
' + escape(xml) + '
' + '
' + escape(tool_xml) + '
' @classmethod @@ -130,7 +130,7 @@ def execute(cls, choices, galaxyFn=None, username=''): prototype.__class__.__name__, choices.description) - abs_tool_xml_path = GALAXY_TOOL_XML_PATH + choices.toolXMLPath + abs_tool_xml_path = os.path.join(GALAXY_TOOL_XML_PATH, choices.toolXMLPath) try: os.makedirs(os.path.dirname(abs_tool_xml_path)) except: From 67f8277b21b22e31495d272a9dfa25752e959c9f Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 23 Feb 2017 19:42:20 +0100 Subject: [PATCH 059/123] ProTo: Updated help text. Improved directory naming validation. Increased maximum number of directory levels. (cherry picked from commit 95b733d8089954d2330c7c972fd12838c344a6bd) --- lib/proto/tools/GenerateToolsTool.py | 29 ++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/proto/tools/GenerateToolsTool.py b/lib/proto/tools/GenerateToolsTool.py index 2f28c3621074..770b49190c40 100644 --- a/lib/proto/tools/GenerateToolsTool.py +++ b/lib/proto/tools/GenerateToolsTool.py @@ -7,7 +7,7 @@ class GenerateToolsTool(GeneralGuiTool): - MAX_DIR_LEVELS = 5 + MAX_DIR_LEVELS = 6 NO_SELECTION = '--- Select a tool directory ---' NEW_DIR = 'Create a new directory...' @@ -173,9 +173,13 @@ def execute(cls, choices, galaxyFn=None, username=''): @classmethod def validateAndReturnErrors(cls, choices): - packageName = cls._getProtoToolPackageName(choices) - if packageName and packageName != packageName.lower(): - return 'Please use all lowercase letters for the package name: ' + packageName + for dirName in cls._getSelectedDirs(choices): + if dirName and dirName != dirName.lower(): + return 'Please use all lowercase letters for the directory name: ' + dirName + + if '.' in dirName: + return 'Period characters, i.e. ".", are not allowed in a directory name: ' \ + + dirName pyName = cls._getPyName(choices) if os.path.exists(pyName): @@ -192,14 +196,15 @@ def getToolDescription(): "'ProTo tool explorer' tool for development purposes.") core.divider() core.smallHeader("Parameters") - core.descriptionLine("Package name", - "The name of the package where the new tool " - "should be installed. The package path is " - "relative to 'proto.tools'. If, for instance, " - "the package is set to 'mypackage.core', the full" - "package hierarchy is 'proto.tools.mypackage." - "core'. Any non-existing directories will be " - "created as needed.", emphasize=True) + core.descriptionLine("Choose directory for new tool", + "Hierarchical selection of directory in which to " + "place the new tool. The directory structure defines " + "the Python package which is used if one needs to import " + "the tool. The package name is automatically shown in an info " + "box according to the selections. It is also possible " + "to create new directories. Note that the creation of " + "new directories happens at execution of this tool. ", + emphasize=True) core.descriptionLine("Module/class name", "The name of the Python module (filename) and " "class for the new tool. For historical reasons, " From 922988e2e90c81a5d6eac5c908f77e45e6f07e6b Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 23 Feb 2017 21:50:15 +0100 Subject: [PATCH 060/123] ProTo: Removed tool type selection box. Cleanup (cherry picked from commit 707aee3d0507fc077be7edb499e76f8a9617a1b2) --- lib/proto/tools/InstallToolsTool.py | 49 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/lib/proto/tools/InstallToolsTool.py b/lib/proto/tools/InstallToolsTool.py index f4353e1c1f44..8674e7bfb975 100644 --- a/lib/proto/tools/InstallToolsTool.py +++ b/lib/proto/tools/InstallToolsTool.py @@ -11,6 +11,7 @@ class InstallToolsTool(GeneralGuiTool): prototype = None + TOOL_TYPE = 'proto' @classmethod def _getToolList(cls): @@ -18,7 +19,7 @@ def _getToolList(cls): return getProtoToolList(installed_classes)[0] @classmethod - def _getProtoType(cls, tool): + def _getPrototype(cls, tool): try: prototype = cls._getToolList()[tool][2]() except: @@ -33,7 +34,6 @@ def getToolName(): @staticmethod def getInputBoxNames(): return [('Select tool', 'tool'), - ('Tool type', 'toolType'), ('Tool ID', 'toolID'), ('Tool name', 'name'), ('Tool description', 'description'), @@ -57,10 +57,6 @@ def getOptionsBoxTool(cls): tool_list = cls._getToolList() return ['-- Select tool --'] + sorted(tool_list) - @classmethod - def getOptionsBoxToolType(cls, prevChoices): - return ['proto'] - @classmethod def getOptionsBoxToolID(cls, prevChoices): import inflection @@ -68,12 +64,12 @@ def getOptionsBoxToolID(cls, prevChoices): return '' tool_list = cls._getToolList() module_name = tool_list[prevChoices.tool][2].__name__ - return prevChoices.toolType + '_' + inflection.underscore(module_name) + return cls.TOOL_TYPE + '_' + inflection.underscore(module_name) @classmethod def getOptionsBoxName(cls, prevChoices): - prototype = cls._getProtoType(prevChoices.tool) + prototype = cls._getPrototype(prevChoices.tool) if prototype is not None: return prototype.getToolName() @@ -83,7 +79,7 @@ def getOptionsBoxDescription(cls, prevChoices): @classmethod def getOptionsBoxToolXMLPath(cls, prevChoices): - prototype = cls._getProtoType(prevChoices.tool) + prototype = cls._getPrototype(prevChoices.tool) if prototype is not None: package = prototype.__module__.split('.') package_dir = '/'.join(package[2:-1]) + '/' if len(package) > 3 else '' @@ -119,22 +115,23 @@ def execute(cls, choices, galaxyFn=None, username=''): # txt = 'Install %s into %s' % (choices.tool, choices.section) # tool_cls = choices.tool - prototype = cls._getProtoType(choices.tool) + prototype = cls._getPrototype(choices.tool) tool_file = choices.toolXMLPath - tool_type = choices.toolType toolConf = GalaxyToolConfig() xml = toolConf.addTool(choices.section, tool_file) tool_xml = toolConf.createToolXml(choices.toolID, - choices.name, tool_type, + choices.name, cls.TOOL_TYPE, prototype.__module__, prototype.__class__.__name__, choices.description) abs_tool_xml_path = os.path.join(GALAXY_TOOL_XML_PATH, choices.toolXMLPath) + try: os.makedirs(os.path.dirname(abs_tool_xml_path)) except: pass + with open(abs_tool_xml_path, 'w') as tf: tf.write(tool_xml) @@ -144,20 +141,20 @@ def execute(cls, choices, galaxyFn=None, username=''): core = HtmlCore() extraJavaScriptCode = ''' - - ''' + +''' core.begin(extraJavaScriptCode=extraJavaScriptCode) core.link('Reload toolbox/menu', url='#', args='id="reload_toolbox"') core.preformatted(escape(xml)) From 9ca12c0ce899968e118f8bb629a50a4d8eb4682c Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 24 Feb 2017 04:49:51 +0100 Subject: [PATCH 061/123] ProTo: small refactorings needed for HB subclassing (cherry picked from commit 713648924a55beef9d48d602c0289616de29ceb4) --- lib/proto/tools/GenerateToolsTool.py | 48 +++++++++++++++++----------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/lib/proto/tools/GenerateToolsTool.py b/lib/proto/tools/GenerateToolsTool.py index 770b49190c40..21cb7c980c1d 100644 --- a/lib/proto/tools/GenerateToolsTool.py +++ b/lib/proto/tools/GenerateToolsTool.py @@ -11,8 +11,14 @@ class GenerateToolsTool(GeneralGuiTool): NO_SELECTION = '--- Select a tool directory ---' NEW_DIR = 'Create a new directory...' - assert PROTO_TOOL_DIR.startswith(SOURCE_CODE_BASE_DIR) - PROTO_REL_TOOL_DIRS = PROTO_TOOL_DIR[len(SOURCE_CODE_BASE_DIR) + 1:].split(os.path.sep) + # For subclass override + TOOL_DIR = PROTO_TOOL_DIR + WEB_CONTROLLER = 'proto' + EXPLORE_TOOL_ID = 'proto_explore_tools_tool' + + def __new__(cls, *args, **kwargs): + cls._setupExtraBoxMethods() + return GeneralGuiTool.__new__(cls, *args, **kwargs) @staticmethod def getToolName(): @@ -59,7 +65,7 @@ def _getSelectedDirs(cls, prevChoices, index=MAX_DIR_LEVELS): if dirSelection == cls.NEW_DIR: dirSelection = getattr(prevChoices, 'newDir%s' % i).strip() - if dirSelection and dirSelection != cls.NO_SELECTION: + if dirSelection is not None and dirSelection != cls.NO_SELECTION: dirs.append(dirSelection) else: break @@ -76,7 +82,7 @@ def _getOptionsBoxDirLevel(cls, prevChoices, index): not (prevDirLevel == cls.NEW_DIR and not getattr(prevChoices, 'newDir%s' % (index - 1)))): - selectedDir = os.path.sep.join([PROTO_TOOL_DIR] + + selectedDir = os.path.sep.join([cls.TOOL_DIR] + cls._getSelectedDirs(prevChoices, index)) try: subDirs = [x for x in os.listdir(selectedDir) if @@ -92,7 +98,7 @@ def _getOptionsBoxNewDir(cls, prevChoices, index): return '', 1 @classmethod - def setupExtraBoxMethods(cls): + def _setupExtraBoxMethods(cls): from functools import partial for i in xrange(cls.MAX_DIR_LEVELS): setattr(cls, 'getOptionsBoxDirLevel%s' % i, @@ -102,9 +108,14 @@ def setupExtraBoxMethods(cls): from gold.application.LogSetup import logMessage + @classmethod + def _getProtoRelToolDirs(cls): + assert cls.TOOL_DIR.startswith(SOURCE_CODE_BASE_DIR) + return cls.TOOL_DIR[len(SOURCE_CODE_BASE_DIR) + 1:].split(os.path.sep) + @classmethod def _getProtoToolPackageName(cls, prevChoices): - return '.'.join(cls.PROTO_REL_TOOL_DIRS + cls._getSelectedDirs(prevChoices)) + return '.'.join(cls._getProtoRelToolDirs() + cls._getSelectedDirs(prevChoices)) @classmethod def getOptionsBoxPackageNameInfo(cls, prevChoices): @@ -129,7 +140,7 @@ def getOptionsBoxTemplate(prevChoices): @classmethod def _getPackageDir(cls, selectedDirs): - return os.path.sep.join([PROTO_TOOL_DIR] + selectedDirs) + return os.path.sep.join([cls.TOOL_DIR] + selectedDirs) @classmethod def _getPyName(cls, choices): @@ -144,7 +155,7 @@ def execute(cls, choices, galaxyFn=None, username=''): os.makedirs(packageDir) for i in range(len(selectedDirs)): - init_py = os.path.sep.join([PROTO_TOOL_DIR] + selectedDirs[0:i+1]) + '/__init__.py' + init_py = os.path.sep.join([cls.TOOL_DIR] + selectedDirs[0:i+1]) + '/__init__.py' if not os.path.exists(init_py): print 'creating ', init_py open(init_py, 'a').close() @@ -165,15 +176,22 @@ def execute(cls, choices, galaxyFn=None, username=''): with open(pyName, 'w') as p: p.write(template) - explore_id = quote('.'.join(selectedDirs + [choices.moduleName]) + ': ' + choices.toolName) - print 'Tool generated: %s: %s' % \ - (URL_PREFIX, explore_id, choices.moduleName, choices.toolName) + numExcludedDirs = len(cls.TOOL_DIR.split(os.path.sep)) - \ + len(SOURCE_CODE_BASE_DIR.split(os.path.sep)) + exploreSubClassId = '.'.join(cls._getProtoRelToolDirs()[numExcludedDirs:] + + selectedDirs + [choices.moduleName]) + explore_id = quote(exploreSubClassId + ': ' + choices.toolName) + print 'Tool generated: %s: %s' % \ + (URL_PREFIX, cls.WEB_CONTROLLER, cls.EXPLORE_TOOL_ID, + explore_id, choices.moduleName, choices.toolName) print 'Tool source path:', pyName @classmethod def validateAndReturnErrors(cls, choices): for dirName in cls._getSelectedDirs(choices): + if not dirName: + return 'Please enter a directory name' + if dirName and dirName != dirName.lower(): return 'Please use all lowercase letters for the directory name: ' + dirName @@ -232,9 +250,3 @@ def getToolDescription(): "the latter to make the tool code itself shorter " "and more readable.", emphasize=True) return str(core) - - # @classmethod - # def getResetBoxes(cls): - # return flatten([('dirLevel%s' % i, 'newDir%s' % i) for i in range(cls.MAX_DIR_LEVELS)]) - -GenerateToolsTool.setupExtraBoxMethods() From 9f136b75f13437555a8749238805fb0938065d8c Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 24 Feb 2017 16:39:58 +0100 Subject: [PATCH 062/123] ProTo: Made tool root configurable (cherry picked from commit f69116a4ca94636d097aaddad30846122167fc1e) --- lib/proto/ProtoToolRegister.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/proto/ProtoToolRegister.py b/lib/proto/ProtoToolRegister.py index e377e6e45d31..c4689b989dea 100644 --- a/lib/proto/ProtoToolRegister.py +++ b/lib/proto/ProtoToolRegister.py @@ -25,13 +25,13 @@ def getInstalledProtoTools(): return installed_classes -def getProtoToolList(exceptClassNames=[]): +def getProtoToolList(exceptClassNames=[], toolDir=PROTO_TOOL_DIR): tmp_tools = {} tools = {} tool_classes = [] all_installed_sub_classes = set() pys = [] - for d in os.walk(PROTO_TOOL_DIR, followlinks=True): + for d in os.walk(toolDir, followlinks=True): if d[0].find('.svn') == -1: pys += [os.path.join(d[0], f) for f in d[2] if f.endswith('.py') and not any(f.startswith(x) for x in ['.', '#'])] @@ -63,7 +63,9 @@ def getProtoToolList(exceptClassNames=[]): all_installed_sub_classes.add(sub_cls) elif hasattr(prototype_cls, 'getToolName'): if class_name not in exceptClassNames: - tool_module = module_name.split('.')[2:] + toolDirLen = len(toolDir.split(os.path.sep)) - \ + len(SOURCE_CODE_BASE_DIR.split(os.path.sep)) + tool_module = module_name.split('.')[toolDirLen:] if class_name != tool_module[-1]: tool_selection_name = '.'.join(tool_module) + \ ' [' + class_name + ']' From 3c75ebec373570d0531424d80e2327102a3c5d68 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 24 Feb 2017 16:43:04 +0100 Subject: [PATCH 063/123] ProTo: refactored to allow HyperBrowser subclassing. Improved input validation. Now does not show hidden XML sections in options box. Removed unused config constant. Cleanup (cherry picked from commit 628458d27fb6d5aa7b8d8372f61205d57bc50df4) --- lib/proto/config/Config.py | 1 - lib/proto/tools/InstallToolsTool.py | 82 ++++++++++++++++++----------- 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/lib/proto/config/Config.py b/lib/proto/config/Config.py index 0e401b6d0a49..c1b9d3d0f9f0 100644 --- a/lib/proto/config/Config.py +++ b/lib/proto/config/Config.py @@ -21,7 +21,6 @@ def getUrlPrefix(config): OUTPUT_PRECISION = int(config.getWithDefault('output_precision', '4', 'galaxy_proto')) GALAXY_TOOL_CONFIG_FILE = GALAXY_BASE_DIR + '/' + GALAXY_REL_TOOL_CONFIG_FILE -GALAXY_TOOL_XML_PATH = GALAXY_BASE_DIR + '/tools' PROTO_TOOL_DIR = GALAXY_BASE_DIR + '/lib/proto/tools' PROTO_TOOL_SHELVE_FN = GALAXY_BASE_DIR + '/database/proto-tool-cache.shelve' SOURCE_CODE_BASE_DIR = GALAXY_BASE_DIR + '/lib' diff --git a/lib/proto/tools/InstallToolsTool.py b/lib/proto/tools/InstallToolsTool.py index 8674e7bfb975..8de3595f6834 100644 --- a/lib/proto/tools/InstallToolsTool.py +++ b/lib/proto/tools/InstallToolsTool.py @@ -4,19 +4,23 @@ from cgi import escape from proto.ProtoToolRegister import getProtoToolList, getInstalledProtoTools -from proto.config.Config import (GALAXY_TOOL_CONFIG_FILE, - GALAXY_TOOL_XML_PATH) +from proto.config.Config import (GALAXY_TOOL_CONFIG_FILE, PROTO_TOOL_DIR, SOURCE_CODE_BASE_DIR) +from proto.config.GalaxyConfigParser import GALAXY_BASE_DIR from proto.tools.GeneralGuiTool import GeneralGuiTool class InstallToolsTool(GeneralGuiTool): - prototype = None - TOOL_TYPE = 'proto' + SELECT_TOOL_STR = '--- Select tool ---' + + # For subclass override + TOOL_DIR = PROTO_TOOL_DIR + XML_TOOL_DIR = 'proto' + TOOL_ID_PREFIX = 'proto' @classmethod def _getToolList(cls): installed_classes = getInstalledProtoTools() - return getProtoToolList(installed_classes)[0] + return getProtoToolList(installed_classes, toolDir=cls.TOOL_DIR)[0] @classmethod def _getPrototype(cls, tool): @@ -26,7 +30,6 @@ def _getPrototype(cls, tool): prototype = None return prototype - @staticmethod def getToolName(): return "ProTo tool installer" @@ -55,17 +58,16 @@ def useSubToolPrefix(): @classmethod def getOptionsBoxTool(cls): tool_list = cls._getToolList() - return ['-- Select tool --'] + sorted(tool_list) + return [cls.SELECT_TOOL_STR] + sorted(tool_list) @classmethod def getOptionsBoxToolID(cls, prevChoices): import inflection - if prevChoices.tool is None or prevChoices.tool.startswith('--'): + if prevChoices.tool is None or prevChoices.tool == cls.SELECT_TOOL_STR: return '' tool_list = cls._getToolList() module_name = tool_list[prevChoices.tool][2].__name__ - return cls.TOOL_TYPE + '_' + inflection.underscore(module_name) - + return cls.TOOL_ID_PREFIX + '_' + inflection.underscore(module_name) @classmethod def getOptionsBoxName(cls, prevChoices): @@ -78,12 +80,22 @@ def getOptionsBoxDescription(cls, prevChoices): return '' @classmethod - def getOptionsBoxToolXMLPath(cls, prevChoices): + def _getProtoRelToolDirs(cls, fromBase=False): + cmpDir = GALAXY_BASE_DIR if fromBase else SOURCE_CODE_BASE_DIR + assert cls.TOOL_DIR.startswith(cmpDir) + return cls.TOOL_DIR[len(cmpDir) + 1:].split(os.path.sep) + + @classmethod + def _getToolXmlPath(cls, prevChoices): prototype = cls._getPrototype(prevChoices.tool) if prototype is not None: package = prototype.__module__.split('.') - package_dir = '/'.join(package[2:-1]) + '/' if len(package) > 3 else '' - return 'proto/' + package_dir + prototype.__class__.__name__ + '.xml' + package_dir = os.path.sep.join(package[len(cls._getProtoRelToolDirs()):-1]) + return package_dir + os.path.sep + prototype.__class__.__name__ + '.xml' + + @classmethod + def getOptionsBoxToolXMLPath(cls, prevChoices): + return cls._getToolXmlPath(prevChoices) @classmethod def getOptionsBoxSection(cls, prevChoices): @@ -102,12 +114,6 @@ def getOptionsBoxSection(cls, prevChoices): # tool_xml = cls.toolConf.createToolXml(tool_file, prevChoices.toolID, prevChoices.name, prototype.__module__, prototype.__class__.__name__, prevChoices.description) # return 'rawstr', '
' + escape(xml) + '
' + '
' + escape(tool_xml) + '
' - @classmethod - def validateAndReturnErrors(cls, choices): - if not choices.toolID or len(choices.toolID) < 6 or not re.match(r'^[a-zA-Z0-9_]+$', choices.toolID): - return 'Tool ID must be at least 6 characters and not contain special chars' - return None - @classmethod def execute(cls, choices, galaxyFn=None, username=''): # txt = '' @@ -116,16 +122,16 @@ def execute(cls, choices, galaxyFn=None, username=''): # tool_cls = choices.tool prototype = cls._getPrototype(choices.tool) - tool_file = choices.toolXMLPath + tool_file = os.path.join(cls.XML_TOOL_DIR, choices.toolXMLPath) toolConf = GalaxyToolConfig() xml = toolConf.addTool(choices.section, tool_file) tool_xml = toolConf.createToolXml(choices.toolID, - choices.name, cls.TOOL_TYPE, + choices.name, cls.TOOL_ID_PREFIX, prototype.__module__, prototype.__class__.__name__, choices.description) - abs_tool_xml_path = os.path.join(GALAXY_TOOL_XML_PATH, choices.toolXMLPath) + abs_tool_xml_path = os.path.join(cls.TOOL_DIR, choices.toolXMLPath) try: os.makedirs(os.path.dirname(abs_tool_xml_path)) @@ -162,8 +168,23 @@ def execute(cls, choices, galaxyFn=None, username=''): core.end() print>>open(galaxyFn, 'w'), core - @staticmethod - def getToolDescription(): + @classmethod + def validateAndReturnErrors(cls, choices): + if choices.tool == cls.SELECT_TOOL_STR: + return 'Please select a ProTo tool to install in the menu' + + if not choices.toolID or len(choices.toolID) < 6 or \ + not re.match(r'^[a-z0-9_]+$', choices.toolID): + return 'Tool ID must be at least 6 characters, ' \ + 'all lowercase characters or underscore, ' \ + 'and not contain special characters: ' + choices.toolID + + if not choices.toolID.startswith(cls.TOOL_ID_PREFIX + '_'): + return 'Tool ID must start with "%s": %s' % \ + (cls.TOOL_ID_PREFIX + '_', choices.toolID) + + @classmethod + def getToolDescription(cls): from proto.HtmlCore import HtmlCore core = HtmlCore() core.smallHeader("General description") @@ -201,10 +222,11 @@ def getToolDescription(): "appear directly after the tool name as " "normal text.", emphasize=True) core.descriptionLine("Tool XML file", - "The path (relative to 'tools/proto/') and name " + "The path (relative to '%s') and name " % + os.path.sep.join([''] + cls._getProtoRelToolDirs(fromBase=True)) + "of the Galaxy tool XML file to be created. " "The tool file can be named anything and be " - "placed anywhere (as the 'tool_conf.xml' file" + "placed anywhere (as the 'tool_conf.xml' file " "contains the path to the tool XML file). " "However, we encourage the practice of placing " "the Galaxy tool XML file together with the " @@ -238,7 +260,7 @@ def __init__(self, tool_conf_fn=GALAXY_TOOL_CONFIG_FILE): def getSections(self): self.sectionPos = {} section_names = [] - for m in re.finditer(r'
]+)>', self.tool_conf_data): + for m in re.finditer(r'\n[^!\n]+
]+)>', self.tool_conf_data): attrib = {} for a in re.findall(r'([^ =]+)="([^"]+)"', m.group(1)): attrib[a[0]] = a[1] @@ -248,7 +270,7 @@ def getSections(self): def addTool(self, section_name, tool_file): self.getSections() - tool_tag = '\n\t' % (tool_file,) + tool_tag = '\n ' % (tool_file,) pos = self.sectionPos[section_name] self.tool_conf_data = self.tool_conf_data[:pos] + tool_tag + self.tool_conf_data[pos:] return self.tool_conf_data @@ -258,7 +280,7 @@ def write(self): with open(self.tool_conf_fn, 'w') as f: f.write(self.tool_conf_data) - def createToolXml(self, tool_id, tool_name, tool_type, tool_module, tool_cls, tool_descr): - tool_xml = self.tool_xml_template % (tool_id, tool_name, tool_type, + def createToolXml(self, tool_id, tool_name, tool_id_prefix, tool_module, tool_cls, tool_descr): + tool_xml = self.tool_xml_template % (tool_id, tool_name, tool_id_prefix, tool_module, tool_cls, tool_descr) return tool_xml From 50b9fd91cc1faefa3e131a64127b852cd8c516f1 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 24 Feb 2017 21:58:14 +0100 Subject: [PATCH 064/123] ProTo: Refactored ProtoToolRegister in order to handle equal classnames for classes in different modules correctly. Some cleanup and small fixes (cherry picked from commit a024faf0bb7758cd08e70a27de4b2c5a2ad08c92) --- lib/proto/ProtoToolRegister.py | 73 ++++++++++++++++------------- lib/proto/tools/ExploreToolsTool.py | 9 ++-- lib/proto/tools/InstallToolsTool.py | 8 ++-- 3 files changed, 50 insertions(+), 40 deletions(-) diff --git a/lib/proto/ProtoToolRegister.py b/lib/proto/ProtoToolRegister.py index c4689b989dea..c96d786458f9 100644 --- a/lib/proto/ProtoToolRegister.py +++ b/lib/proto/ProtoToolRegister.py @@ -9,23 +9,26 @@ from proto.tools.GeneralGuiTool import GeneralGuiTool, MultiGeneralGuiTool -EXCEPT_MODULE_NAMES = ['proto.tools.ExploreToolsTool', - 'proto.tools.InstallToolsTool', - 'proto.tools.ToolTemplate', - 'proto.tools.ToolTemplateMinimal'] +DEFAULT_EXCEPT_CLASS_INFO = [('proto.tools.ToolTemplate', 'ToolTemplate'), + ('proto.tools.ToolTemplateMinimal', 'ToolTemplate')] def getInstalledProtoTools(): tool_shelve = shelve.open(PROTO_TOOL_SHELVE_FN, 'r') - installed_classes = [tool_shelve.get(t)[1] for t in tool_shelve.keys() if os.path.exists( - os.path.join(SOURCE_CODE_BASE_DIR, - tool_shelve.get(t)[0].replace('.', os.path.sep)) - + '.py')] + installed_class_info = [tool_shelve.get(t) for t in tool_shelve.keys() if os.path.exists( + os.path.join(SOURCE_CODE_BASE_DIR, tool_shelve.get(t)[0].replace('.', os.path.sep)) + + '.py')] tool_shelve.close() - return installed_classes + return installed_class_info -def getProtoToolList(exceptClassNames=[], toolDir=PROTO_TOOL_DIR): +def getUniqueKeyForClass(module, cls): + module_fn = os.path.join(SOURCE_CODE_BASE_DIR, module.replace('.', os.path.sep)) + module_fn = os.path.realpath(module_fn) + return os.path.join(module_fn, cls) + + +def getProtoToolList(toolDir=PROTO_TOOL_DIR): tmp_tools = {} tools = {} tool_classes = [] @@ -36,6 +39,8 @@ def getProtoToolList(exceptClassNames=[], toolDir=PROTO_TOOL_DIR): pys += [os.path.join(d[0], f) for f in d[2] if f.endswith('.py') and not any(f.startswith(x) for x in ['.', '#'])] + except_class_info = DEFAULT_EXCEPT_CLASS_INFO + getInstalledProtoTools() + except_classes = set([getUniqueKeyForClass(module, cls) for module, cls in except_class_info]) # To fix import issue if there are modules in /lib and /lib/proto with the # same name (e.g. 'config'). tmpSysPath = sys.path @@ -52,29 +57,31 @@ def getProtoToolList(exceptClassNames=[], toolDir=PROTO_TOOL_DIR): module_name = os.path.splitext(os.path.relpath( os.path.abspath(fn), SOURCE_CODE_BASE_DIR))[0].replace(os.path.sep, '.') try: - if module_name not in EXCEPT_MODULE_NAMES: - module = import_module(module_name) - prototype_cls = getattr(module, class_name) - if issubclass(prototype_cls, GeneralGuiTool): - if issubclass(prototype_cls, MultiGeneralGuiTool): - if class_name in exceptClassNames and \ - prototype_cls.getSubToolClasses(): - for sub_cls in prototype_cls.getSubToolClasses(): - all_installed_sub_classes.add(sub_cls) - elif hasattr(prototype_cls, 'getToolName'): - if class_name not in exceptClassNames: - toolDirLen = len(toolDir.split(os.path.sep)) - \ - len(SOURCE_CODE_BASE_DIR.split(os.path.sep)) - tool_module = module_name.split('.')[toolDirLen:] - if class_name != tool_module[-1]: - tool_selection_name = '.'.join(tool_module) + \ - ' [' + class_name + ']' - else: - tool_selection_name = '.'.join(tool_module) - - # print (fn, m.group(2), prototype_cls, module_name) - tmp_tools[tool_selection_name] = \ - (fn, m.group(2), prototype_cls, module_name) + module = import_module(module_name) + prototype_cls = getattr(module, class_name) + + if getUniqueKeyForClass(module_name, class_name) not in except_classes: + if issubclass(prototype_cls, GeneralGuiTool) \ + and not issubclass(prototype_cls, MultiGeneralGuiTool) \ + and hasattr(prototype_cls, 'getToolName'): + toolDirLen = len(toolDir.split(os.path.sep)) - \ + len(SOURCE_CODE_BASE_DIR.split(os.path.sep)) + tool_module = module_name.split('.')[toolDirLen:] + if class_name != tool_module[-1]: + tool_selection_name = '.'.join(tool_module) + \ + ' [' + class_name + ']' + else: + tool_selection_name = '.'.join(tool_module) + + # print (fn, m.group(2), prototype_cls, module_name) + tmp_tools[tool_selection_name] = \ + (fn, m.group(2), prototype_cls, module_name) + + elif issubclass(prototype_cls, MultiGeneralGuiTool) \ + and 'ExploreToolsTool' not in class_name \ + and prototype_cls.getSubToolClasses(): + for sub_cls in prototype_cls.getSubToolClasses(): + all_installed_sub_classes.add(sub_cls) except Exception as e: traceback.print_exc() # break diff --git a/lib/proto/tools/ExploreToolsTool.py b/lib/proto/tools/ExploreToolsTool.py index cf2a3cc4368e..f33e47ace82b 100644 --- a/lib/proto/tools/ExploreToolsTool.py +++ b/lib/proto/tools/ExploreToolsTool.py @@ -1,8 +1,12 @@ -from proto.ProtoToolRegister import getInstalledProtoTools, getProtoToolList +from proto.ProtoToolRegister import getProtoToolList +from proto.config.Config import PROTO_TOOL_DIR from proto.tools.GeneralGuiTool import MultiGeneralGuiTool class ExploreToolsTool(MultiGeneralGuiTool): + # For subclass override + TOOL_DIR = PROTO_TOOL_DIR + @staticmethod def getToolName(): return "ProTo tool explorer" @@ -17,8 +21,7 @@ def useSubToolPrefix(): @classmethod def getSubToolClasses(cls): - installed_classes = getInstalledProtoTools() - tool_list = getProtoToolList(installed_classes)[1] + tool_list = getProtoToolList(toolDir=cls.TOOL_DIR)[1] return sorted(tool_list, key=lambda c: c.__module__) @staticmethod diff --git a/lib/proto/tools/InstallToolsTool.py b/lib/proto/tools/InstallToolsTool.py index 8de3595f6834..3e47b6a0447f 100644 --- a/lib/proto/tools/InstallToolsTool.py +++ b/lib/proto/tools/InstallToolsTool.py @@ -3,7 +3,7 @@ import shutil from cgi import escape -from proto.ProtoToolRegister import getProtoToolList, getInstalledProtoTools +from proto.ProtoToolRegister import getProtoToolList from proto.config.Config import (GALAXY_TOOL_CONFIG_FILE, PROTO_TOOL_DIR, SOURCE_CODE_BASE_DIR) from proto.config.GalaxyConfigParser import GALAXY_BASE_DIR from proto.tools.GeneralGuiTool import GeneralGuiTool @@ -19,8 +19,7 @@ class InstallToolsTool(GeneralGuiTool): @classmethod def _getToolList(cls): - installed_classes = getInstalledProtoTools() - return getProtoToolList(installed_classes, toolDir=cls.TOOL_DIR)[0] + return getProtoToolList(toolDir=cls.TOOL_DIR)[0] @classmethod def _getPrototype(cls, tool): @@ -91,7 +90,8 @@ def _getToolXmlPath(cls, prevChoices): if prototype is not None: package = prototype.__module__.split('.') package_dir = os.path.sep.join(package[len(cls._getProtoRelToolDirs()):-1]) - return package_dir + os.path.sep + prototype.__class__.__name__ + '.xml' + return package_dir + os.path.sep + prototype.__class__.__name__ + '.xml' \ + if package_dir else prototype.__class__.__name__ + '.xml' @classmethod def getOptionsBoxToolXMLPath(cls, prevChoices): From 3aa0ec3ed6fd58137e9edbc989df1f41bbc0950c Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 3 Mar 2017 16:12:18 +0100 Subject: [PATCH 065/123] ProTo: Full refactor of ProtoToolRegister. Added tool for hiding modules in ExploreToolsTool. Added support for automatic hiding of non-tool classes in the webtool module in order to speed up ExploreToolsTool. Hidden tools and classes are stored in config files. (cherry picked from commit 706b9df67154f18731d9d39a663585354f55ce79) --- lib/proto/ProtoToolRegister.py | 296 +++++++++++++++++++------ lib/proto/config/Config.py | 1 + lib/proto/tools/ExploreToolsTool.py | 9 +- lib/proto/tools/ExploreToolsTool.xml | 2 +- lib/proto/tools/GenerateToolsTool.xml | 2 +- lib/proto/tools/HideInExplorerTool.py | 125 +++++++++++ lib/proto/tools/HideInExplorerTool.xml | 4 + lib/proto/tools/InstallToolsTool.py | 14 +- lib/proto/tools/InstallToolsTool.xml | 2 +- 9 files changed, 370 insertions(+), 85 deletions(-) create mode 100644 lib/proto/tools/HideInExplorerTool.py create mode 100644 lib/proto/tools/HideInExplorerTool.xml diff --git a/lib/proto/ProtoToolRegister.py b/lib/proto/ProtoToolRegister.py index c96d786458f9..37e279eaa9d7 100644 --- a/lib/proto/ProtoToolRegister.py +++ b/lib/proto/ProtoToolRegister.py @@ -3,15 +3,32 @@ import shelve import sys import traceback +from collections import namedtuple, OrderedDict from importlib import import_module -from proto.config.Config import SOURCE_CODE_BASE_DIR, PROTO_TOOL_DIR, PROTO_TOOL_SHELVE_FN +from proto.config.Config import SOURCE_CODE_BASE_DIR, PROTO_TOOL_DIR, PROTO_TOOL_SHELVE_FN, \ + CONFIG_DIR from proto.tools.GeneralGuiTool import GeneralGuiTool, MultiGeneralGuiTool +MULTI_GENERAL_GUI_TOOL = 'MultiGeneralGuiTool' + +EXPLORE_TOOLS_TOOL_CLS_NAME = 'ExploreToolsTool' DEFAULT_EXCEPT_CLASS_INFO = [('proto.tools.ToolTemplate', 'ToolTemplate'), ('proto.tools.ToolTemplateMinimal', 'ToolTemplate')] +HIDDEN_MODULES_CONFIG_FN = \ + os.path.join(CONFIG_DIR, 'proto_tool_explorer_hidden_modules.txt') + +HIDDEN_NONTOOL_MODULES_CONFIG_FN = \ + os.path.join(CONFIG_DIR, 'proto_tool_explorer_hidden_nontool_modules.txt') + + +ProtoToolInfo = namedtuple('ProtoToolInfo', ['prototype_cls', 'module_name']) +ProtoClassInfo = namedtuple('ProtoClassInfo', ['class_name', 'super_class_list']) + + +# Public functions def getInstalledProtoTools(): tool_shelve = shelve.open(PROTO_TOOL_SHELVE_FN, 'r') @@ -22,79 +39,37 @@ def getInstalledProtoTools(): return installed_class_info -def getUniqueKeyForClass(module, cls): - module_fn = os.path.join(SOURCE_CODE_BASE_DIR, module.replace('.', os.path.sep)) - module_fn = os.path.realpath(module_fn) - return os.path.join(module_fn, cls) +def getUniqueKeyForClass(module_name, class_name): + module_fn = getRelativeModulePath(module_name) + return os.path.join(module_fn, class_name) -def getProtoToolList(toolDir=PROTO_TOOL_DIR): - tmp_tools = {} - tools = {} - tool_classes = [] - all_installed_sub_classes = set() - pys = [] - for d in os.walk(toolDir, followlinks=True): - if d[0].find('.svn') == -1: - pys += [os.path.join(d[0], f) for f in d[2] if f.endswith('.py') and - not any(f.startswith(x) for x in ['.', '#'])] +def getRelativeModulePath(module_name): + module_fn = os.path.join(SOURCE_CODE_BASE_DIR, module_name.replace('.', os.path.sep)) + return os.path.realpath(module_fn)[len(os.path.realpath(SOURCE_CODE_BASE_DIR)):] - except_class_info = DEFAULT_EXCEPT_CLASS_INFO + getInstalledProtoTools() - except_classes = set([getUniqueKeyForClass(module, cls) for module, cls in except_class_info]) - # To fix import issue if there are modules in /lib and /lib/proto with the - # same name (e.g. 'config'). - tmpSysPath = sys.path - if sys.path[0].endswith('/proto'): - sys.path = tmpSysPath[1:] - # print 'Num py', len(wpys) - for fn in pys: - with open(fn) as f: - for line in f: - m = re.match(r'class +(\w+) *\(([\w ,]+)\)', line) - if m: - class_name = m.group(1) - module_name = os.path.splitext(os.path.relpath( - os.path.abspath(fn), SOURCE_CODE_BASE_DIR))[0].replace(os.path.sep, '.') - try: - module = import_module(module_name) - prototype_cls = getattr(module, class_name) - - if getUniqueKeyForClass(module_name, class_name) not in except_classes: - if issubclass(prototype_cls, GeneralGuiTool) \ - and not issubclass(prototype_cls, MultiGeneralGuiTool) \ - and hasattr(prototype_cls, 'getToolName'): - toolDirLen = len(toolDir.split(os.path.sep)) - \ - len(SOURCE_CODE_BASE_DIR.split(os.path.sep)) - tool_module = module_name.split('.')[toolDirLen:] - if class_name != tool_module[-1]: - tool_selection_name = '.'.join(tool_module) + \ - ' [' + class_name + ']' - else: - tool_selection_name = '.'.join(tool_module) - - # print (fn, m.group(2), prototype_cls, module_name) - tmp_tools[tool_selection_name] = \ - (fn, m.group(2), prototype_cls, module_name) - - elif issubclass(prototype_cls, MultiGeneralGuiTool) \ - and 'ExploreToolsTool' not in class_name \ - and prototype_cls.getSubToolClasses(): - for sub_cls in prototype_cls.getSubToolClasses(): - all_installed_sub_classes.add(sub_cls) - except Exception as e: - traceback.print_exc() - # break - # print 'Num protopy', len(tools) - - for tool_selection_name, tool_info in tmp_tools.iteritems(): - prototype_cls = tool_info[2] - if prototype_cls not in all_installed_sub_classes: - tools[tool_selection_name] = tool_info - tool_classes.append(prototype_cls) +def getProtoToolList(tool_dir=PROTO_TOOL_DIR): + return _commonGetProtoToolList(tool_dir) + + +def getNonHiddenProtoToolList(tool_dir=PROTO_TOOL_DIR): + except_modules_set = retrieveHiddenModulesSet(HIDDEN_MODULES_CONFIG_FN) + except_modules_set.update(retrieveHiddenModulesSet(HIDDEN_NONTOOL_MODULES_CONFIG_FN)) + return _commonGetProtoToolList(tool_dir, except_modules_set=except_modules_set) + + +def retrieveHiddenModulesSet(hidden_modules_fn): + if os.path.exists(hidden_modules_fn): + return set([line.strip() for line in open(hidden_modules_fn)]) + else: + return set() - sys.path = tmpSysPath - return tools, tool_classes + +def storeHiddenModules(hidden_modules_fn, hidden_module_paths, append=False): + with open(hidden_modules_fn, 'a' if append else 'w') as config_file: + for hidden_module_path in hidden_module_paths: + config_file.write(hidden_module_path + os.linesep) def getToolPrototype(toolId): @@ -103,12 +78,191 @@ def getToolPrototype(toolId): tool_shelve = shelve.open(PROTO_TOOL_SHELVE_FN, 'r') module_name, class_name = tool_shelve[str(toolId)] module = __import__(module_name, fromlist=[class_name]) - # print module, class_name, toolId prototype = getattr(module, class_name)(toolId) - # print "Loaded proto tool:", class_name #except KeyError: # prototype = None finally: if tool_shelve: tool_shelve.close() return prototype + + +# Private functions + +def _commonGetProtoToolList(tool_dir=PROTO_TOOL_DIR, except_modules_set=set()): + tmpSysPath = _fixSameNameImportIssues() + + pys = _findAllPythonFiles(tool_dir) + except_classes_set = _findExceptClassesSet() + + tool_info_dict = \ + _findAllUninstalledTools(pys, tool_dir, except_modules_set, except_classes_set) + + sys.path = tmpSysPath + + return tool_info_dict + + +def _findExceptClassesSet(): + except_class_info = DEFAULT_EXCEPT_CLASS_INFO + getInstalledProtoTools() + except_classes_set = set([getUniqueKeyForClass(module, class_name) for + module, class_name in except_class_info]) + return except_classes_set + + +def _fixSameNameImportIssues(): + # To fix import issue if there are modules in /lib and /lib/proto with the + # same name (e.g. 'config'). + tmpSysPath = sys.path + if sys.path[0].endswith('/proto'): + sys.path = tmpSysPath[1:] + return tmpSysPath + + +def _filterInstalledSubClassTools(tmp_tool_info_dict, all_installed_sub_classes): + tool_info_dict = OrderedDict() + for tool_selection_name, tool_info in tmp_tool_info_dict.iteritems(): + prototype_cls = tool_info.prototype_cls + if prototype_cls not in all_installed_sub_classes: + tool_info_dict[tool_selection_name] = tool_info + return tool_info_dict + + +def _findAllUninstalledTools(pys, tool_dir, except_modules_set, except_classes_set): + all_modules = _findAllModulesWithClasses(except_modules_set, pys) + + except_classes_set.update(_findInstalledSubClasses(all_modules, except_classes_set)) + tool_info_dict = _getInfoForAllUninstalledTools(all_modules, tool_dir, except_classes_set) + + return tool_info_dict + + +def _findAllModulesWithClasses(except_modules_set, pys): + all_modules = [] + + for fn in pys: + module_name = _getModuleNameFromPath(fn) + if except_modules_set and getRelativeModulePath(module_name) in except_modules_set: + continue + + class_info_list = _findInfoForAllClasses(fn) + all_modules.append((module_name, class_info_list)) + + return all_modules + + +def _findInstalledSubClasses(all_modules, except_classes_set): + all_installed_sub_classes_set = set() + + for module_name, class_info_list in all_modules: + for class_name, super_classes in class_info_list: + uniqueKey = getUniqueKeyForClass(module_name, class_name) + try: + if uniqueKey in except_classes_set and \ + MULTI_GENERAL_GUI_TOOL in super_classes and \ + class_name != EXPLORE_TOOLS_TOOL_CLS_NAME: + installed_sub_classes = _getSubClasses(class_name, module_name) + all_installed_sub_classes_set.update(installed_sub_classes) + except Exception as e: + pass + + return all_installed_sub_classes_set + + +def _getInfoForAllUninstalledTools(all_modules, tool_dir, except_classes_set): + tool_info_dict = OrderedDict() + + for module_name, class_info_list in all_modules: + for class_name, super_classes in class_info_list: + uniqueKey = getUniqueKeyForClass(module_name, class_name) + try: + if uniqueKey not in except_classes_set: + tool_selection_name, tool_info = \ + _extractToolInfo(class_name, module_name, tool_dir) + if tool_selection_name: + tool_info_dict[tool_selection_name] = tool_info + else: + if not MULTI_GENERAL_GUI_TOOL in super_classes: + _storeAsNonToolHiddenModule(module_name) + except Exception as e: + pass + # traceback.print_exc() + # break + + return tool_info_dict + + +def _storeAsNonToolHiddenModule(module_name): + rel_module_name = getRelativeModulePath(module_name) + if rel_module_name not in \ + retrieveHiddenModulesSet(HIDDEN_NONTOOL_MODULES_CONFIG_FN): + storeHiddenModules(HIDDEN_NONTOOL_MODULES_CONFIG_FN, + [rel_module_name], + append=True) + + +def _getModuleNameFromPath(fn): + return os.path.splitext(os.path.relpath( + os.path.abspath(fn), SOURCE_CODE_BASE_DIR))[0].replace(os.path.sep, '.') + + +def _findInfoForAllClasses(fn): + with open(fn) as f: + class_info_list = [] + for line in f: + match = re.match(r'class +(\w+) *\(([\w ,]+)\)', line) + if match: + class_name = match.group(1) + super_class_list = [super_cls.strip() for super_cls in match.group(2).split(',')] + class_info_list.append(ProtoClassInfo(class_name, super_class_list)) + return class_info_list + + +def _getSubClasses(class_name, module_name): + sub_classes = set() + + module = import_module(module_name) + prototype_cls = getattr(module, class_name) + + if prototype_cls.getSubToolClasses(): + for sub_cls in prototype_cls.getSubToolClasses(): + sub_classes.add(getUniqueKeyForClass(sub_cls.__module__, sub_cls.__name__)) + + return sub_classes + + +def _extractToolInfo(class_name, module_name, tool_dir): + module = import_module(module_name) + prototype_cls = getattr(module, class_name) + + if issubclass(prototype_cls, GeneralGuiTool) \ + and not issubclass(prototype_cls, MultiGeneralGuiTool) \ + and hasattr(prototype_cls, 'getToolName'): + tool_selection_name = \ + _createToolSelectionName(class_name, module_name, tool_dir) + tool_info = ProtoToolInfo(prototype_cls, module_name) + return tool_selection_name, tool_info + + return None, None + + +def _findAllPythonFiles(tool_dir): + pys = [] + for d in os.walk(tool_dir, followlinks=True): + if d[0].find('.svn') == -1: + pys += [os.path.join(d[0], f) for f in d[2] if f.endswith('.py') and + not any(f.startswith(x) for x in ['.', '#', '_'])] + return pys + + +def _createToolSelectionName(class_name, module_name, tool_dir): + tool_dirLen = len(tool_dir.split(os.path.sep)) - \ + len(SOURCE_CODE_BASE_DIR.split(os.path.sep)) + tool_module = module_name.split('.')[tool_dirLen:] + if class_name != tool_module[-1]: + tool_selection_name = '.'.join(tool_module) + \ + ' [' + class_name + ']' + else: + tool_selection_name = '.'.join(tool_module) + + return tool_selection_name diff --git a/lib/proto/config/Config.py b/lib/proto/config/Config.py index c1b9d3d0f9f0..11fcf576cc9c 100644 --- a/lib/proto/config/Config.py +++ b/lib/proto/config/Config.py @@ -23,6 +23,7 @@ def getUrlPrefix(config): GALAXY_TOOL_CONFIG_FILE = GALAXY_BASE_DIR + '/' + GALAXY_REL_TOOL_CONFIG_FILE PROTO_TOOL_DIR = GALAXY_BASE_DIR + '/lib/proto/tools' PROTO_TOOL_SHELVE_FN = GALAXY_BASE_DIR + '/database/proto-tool-cache.shelve' +CONFIG_DIR = GALAXY_BASE_DIR + '/config' SOURCE_CODE_BASE_DIR = GALAXY_BASE_DIR + '/lib' STATIC_DIR = '/static/proto' STATIC_PATH = GALAXY_BASE_DIR + STATIC_DIR diff --git a/lib/proto/tools/ExploreToolsTool.py b/lib/proto/tools/ExploreToolsTool.py index f33e47ace82b..60dfbb34a9de 100644 --- a/lib/proto/tools/ExploreToolsTool.py +++ b/lib/proto/tools/ExploreToolsTool.py @@ -1,4 +1,4 @@ -from proto.ProtoToolRegister import getProtoToolList +from proto.ProtoToolRegister import getNonHiddenProtoToolList from proto.config.Config import PROTO_TOOL_DIR from proto.tools.GeneralGuiTool import MultiGeneralGuiTool @@ -21,8 +21,9 @@ def useSubToolPrefix(): @classmethod def getSubToolClasses(cls): - tool_list = getProtoToolList(toolDir=cls.TOOL_DIR)[1] - return sorted(tool_list, key=lambda c: c.__module__) + tool_info_dict = getNonHiddenProtoToolList(tool_dir=cls.TOOL_DIR) + tool_classes = [tool_info.prototype_cls for tool_info in tool_info_dict.values()] + return sorted(tool_classes, key=lambda c: c.__module__) @staticmethod def getToolDescription(): @@ -32,7 +33,7 @@ def getToolDescription(): core.paragraph("This tool is used to try out ProTo tools that have " "not been installed as separate tools in the tool " "menu. This is typically used for development " - "purposes, so that one can polish the tool until it" + "purposes, so that one can polish the tool until it " "is finished for deployment in the tool menu. " "When a tool is installed into the menu, the tool " "disappears from the tool list in this tool." diff --git a/lib/proto/tools/ExploreToolsTool.xml b/lib/proto/tools/ExploreToolsTool.xml index 903d3df7f2a2..6730bcc179a5 100644 --- a/lib/proto/tools/ExploreToolsTool.xml +++ b/lib/proto/tools/ExploreToolsTool.xml @@ -1,4 +1,4 @@ - diff --git a/lib/proto/tools/GenerateToolsTool.xml b/lib/proto/tools/GenerateToolsTool.xml index f62838a9e1fc..a26bc30d7c49 100644 --- a/lib/proto/tools/GenerateToolsTool.xml +++ b/lib/proto/tools/GenerateToolsTool.xml @@ -1,4 +1,4 @@ - diff --git a/lib/proto/tools/HideInExplorerTool.py b/lib/proto/tools/HideInExplorerTool.py new file mode 100644 index 000000000000..37887903a1a3 --- /dev/null +++ b/lib/proto/tools/HideInExplorerTool.py @@ -0,0 +1,125 @@ +from collections import OrderedDict + +from proto.ProtoToolRegister import (getProtoToolList, getRelativeModulePath, + retrieveHiddenModulesSet, storeHiddenModules, + HIDDEN_MODULES_CONFIG_FN) +from proto.config.Config import PROTO_TOOL_DIR +from quick.webtools.GeneralGuiTool import GeneralGuiTool + + +class HideInExplorerTool(GeneralGuiTool): + TOOL_DIR = PROTO_TOOL_DIR + + @classmethod + def getToolName(cls): + return "Hide tool modules from ProTo tool explorer" + + @classmethod + def getInputBoxNames(cls): + return [('Select tool modules (files) to show in the ProTo tool explorer', 'tools')] + + # @classmethod + # def getInputBoxOrder(cls): + # return None + # + # @classmethod + # def getInputBoxGroups(cls, choices=None): + # return None + + @classmethod + def _getModuleToModulePathDict(cls): + tool_info_dict = getProtoToolList(tool_dir=cls.TOOL_DIR) + modules = sorted(tool_info.module_name for tool_info in tool_info_dict.values()) + return OrderedDict([(module_name, getRelativeModulePath(module_name)) + for module_name in modules]) + + @classmethod + def getOptionsBoxTools(cls): + module_to_module_path_dict = cls._getModuleToModulePathDict() + hidden_tools_set = retrieveHiddenModulesSet(HIDDEN_MODULES_CONFIG_FN) + return OrderedDict([(module_name, module_path not in hidden_tools_set) + for module_name, module_path in module_to_module_path_dict.iteritems()]) + # + # @classmethod + # def getOptionsBoxSecondKey(cls, prevChoices): + # return '' + + # @classmethod + # def getInfoForOptionsBoxKey(cls, prevChoices): + # return None + # + # @classmethod + # def getDemoSelections(cls): + # return ['testChoice1', '..'] + # + # @classmethod + # def getExtraHistElements(cls, choices): + # return None + + @classmethod + def execute(cls, choices, galaxyFn=None, username=''): + module_to_module_path_dict = cls._getModuleToModulePathDict() + storeHiddenModules(HIDDEN_MODULES_CONFIG_FN, + [module_to_module_path_dict[module_name] + for module_name, selected in choices.tools.iteritems() + if not selected]) + + @classmethod + def validateAndReturnErrors(cls, choices): + return None + + # @classmethod + # def getSubToolClasses(cls): + # return None + # + # @classmethod + # def isPublic(cls): + # return False + # + # @classmethod + # def isRedirectTool(cls): + # return False + # + # @classmethod + # def getRedirectURL(cls, choices): + # return '' + + @classmethod + def isHistoryTool(cls): + return False + + # @classmethod + # def isBatchTool(cls): + # return cls.isHistoryTool() + # + # @classmethod + # def isDynamic(cls): + # return True + # + # @classmethod + # def getResetBoxes(cls): + # return [] + # + # @classmethod + # def getToolDescription(cls): + # return '' + # + # @classmethod + # def getToolIllustration(cls): + # return None + # + # @classmethod + # def getFullExampleURL(cls): + # return None + # + # @classmethod + # def isDebugMode(cls): + # return False + # + # @classmethod + # def getOutputFormat(cls, choices): + # return 'html' + # + # @classmethod + # def getOutputName(cls, choices=None): + # return cls.getToolSelectionName() diff --git a/lib/proto/tools/HideInExplorerTool.xml b/lib/proto/tools/HideInExplorerTool.xml new file mode 100644 index 000000000000..9a258bdd79e4 --- /dev/null +++ b/lib/proto/tools/HideInExplorerTool.xml @@ -0,0 +1,4 @@ + + from 'Explore ProTo tools' + diff --git a/lib/proto/tools/InstallToolsTool.py b/lib/proto/tools/InstallToolsTool.py index 3e47b6a0447f..01ddc2557eb7 100644 --- a/lib/proto/tools/InstallToolsTool.py +++ b/lib/proto/tools/InstallToolsTool.py @@ -18,13 +18,13 @@ class InstallToolsTool(GeneralGuiTool): TOOL_ID_PREFIX = 'proto' @classmethod - def _getToolList(cls): - return getProtoToolList(toolDir=cls.TOOL_DIR)[0] + def _getToolInfoDict(cls): + return getProtoToolList(tool_dir=cls.TOOL_DIR) @classmethod def _getPrototype(cls, tool): try: - prototype = cls._getToolList()[tool][2]() + prototype = cls._getToolInfoDict()[tool].prototype_cls() except: prototype = None return prototype @@ -56,16 +56,16 @@ def useSubToolPrefix(): @classmethod def getOptionsBoxTool(cls): - tool_list = cls._getToolList() - return [cls.SELECT_TOOL_STR] + sorted(tool_list) + tool_info_dict = cls._getToolInfoDict() + return [cls.SELECT_TOOL_STR] + sorted(tool_info_dict.keys()) @classmethod def getOptionsBoxToolID(cls, prevChoices): import inflection if prevChoices.tool is None or prevChoices.tool == cls.SELECT_TOOL_STR: return '' - tool_list = cls._getToolList() - module_name = tool_list[prevChoices.tool][2].__name__ + tool_info_dict = cls._getToolInfoDict() + module_name = tool_info_dict[prevChoices.tool].prototype_cls.__name__ return cls.TOOL_ID_PREFIX + '_' + inflection.underscore(module_name) @classmethod diff --git a/lib/proto/tools/InstallToolsTool.xml b/lib/proto/tools/InstallToolsTool.xml index aa139380aa34..0953ef4e7a7d 100644 --- a/lib/proto/tools/InstallToolsTool.xml +++ b/lib/proto/tools/InstallToolsTool.xml @@ -1,4 +1,4 @@ - From 96d8e49701650239cec92d16792c8840aa6ec464 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 3 Mar 2017 16:47:31 +0100 Subject: [PATCH 066/123] ProTo: Added tool to debug exceptions in import of ProTo tools. (cherry picked from commit 8514cf2fe11329cbfe49f89a8ad204884ac3b7b4) --- lib/proto/ProtoToolRegister.py | 27 ++--- lib/proto/tools/DebugToolImportTool.py | 125 ++++++++++++++++++++++++ lib/proto/tools/DebugToolImportTool.xml | 4 + 3 files changed, 145 insertions(+), 11 deletions(-) create mode 100644 lib/proto/tools/DebugToolImportTool.py create mode 100644 lib/proto/tools/DebugToolImportTool.xml diff --git a/lib/proto/ProtoToolRegister.py b/lib/proto/ProtoToolRegister.py index 37e279eaa9d7..9baef7e53fc7 100644 --- a/lib/proto/ProtoToolRegister.py +++ b/lib/proto/ProtoToolRegister.py @@ -49,8 +49,8 @@ def getRelativeModulePath(module_name): return os.path.realpath(module_fn)[len(os.path.realpath(SOURCE_CODE_BASE_DIR)):] -def getProtoToolList(tool_dir=PROTO_TOOL_DIR): - return _commonGetProtoToolList(tool_dir) +def getProtoToolList(tool_dir=PROTO_TOOL_DIR, debug_imports=False): + return _commonGetProtoToolList(tool_dir, debug_imports=debug_imports) def getNonHiddenProtoToolList(tool_dir=PROTO_TOOL_DIR): @@ -89,14 +89,15 @@ def getToolPrototype(toolId): # Private functions -def _commonGetProtoToolList(tool_dir=PROTO_TOOL_DIR, except_modules_set=set()): +def _commonGetProtoToolList(tool_dir=PROTO_TOOL_DIR, except_modules_set=set(), debug_imports=False): tmpSysPath = _fixSameNameImportIssues() pys = _findAllPythonFiles(tool_dir) except_classes_set = _findExceptClassesSet() tool_info_dict = \ - _findAllUninstalledTools(pys, tool_dir, except_modules_set, except_classes_set) + _findAllUninstalledTools(pys, tool_dir, except_modules_set, + except_classes_set, debug_imports=debug_imports) sys.path = tmpSysPath @@ -128,11 +129,13 @@ def _filterInstalledSubClassTools(tmp_tool_info_dict, all_installed_sub_classes) return tool_info_dict -def _findAllUninstalledTools(pys, tool_dir, except_modules_set, except_classes_set): +def _findAllUninstalledTools(pys, tool_dir, except_modules_set, + except_classes_set, debug_imports=False): all_modules = _findAllModulesWithClasses(except_modules_set, pys) except_classes_set.update(_findInstalledSubClasses(all_modules, except_classes_set)) - tool_info_dict = _getInfoForAllUninstalledTools(all_modules, tool_dir, except_classes_set) + tool_info_dict = _getInfoForAllUninstalledTools( + all_modules, tool_dir, except_classes_set, debug_imports=debug_imports) return tool_info_dict @@ -169,7 +172,7 @@ def _findInstalledSubClasses(all_modules, except_classes_set): return all_installed_sub_classes_set -def _getInfoForAllUninstalledTools(all_modules, tool_dir, except_classes_set): +def _getInfoForAllUninstalledTools(all_modules, tool_dir, except_classes_set, debug_imports=False): tool_info_dict = OrderedDict() for module_name, class_info_list in all_modules: @@ -180,14 +183,16 @@ def _getInfoForAllUninstalledTools(all_modules, tool_dir, except_classes_set): tool_selection_name, tool_info = \ _extractToolInfo(class_name, module_name, tool_dir) if tool_selection_name: - tool_info_dict[tool_selection_name] = tool_info + if not debug_imports: + tool_info_dict[tool_selection_name] = tool_info else: if not MULTI_GENERAL_GUI_TOOL in super_classes: _storeAsNonToolHiddenModule(module_name) except Exception as e: - pass - # traceback.print_exc() - # break + if debug_imports: + tool_selection_name = \ + _createToolSelectionName(class_name, module_name, tool_dir) + tool_info_dict[tool_selection_name] = ProtoToolInfo(class_name, module_name) return tool_info_dict diff --git a/lib/proto/tools/DebugToolImportTool.py b/lib/proto/tools/DebugToolImportTool.py new file mode 100644 index 000000000000..d4530a599475 --- /dev/null +++ b/lib/proto/tools/DebugToolImportTool.py @@ -0,0 +1,125 @@ +import traceback +from importlib import import_module + +from proto.HtmlCore import HtmlCore +from proto.ProtoToolRegister import getProtoToolList +from proto.config.Config import PROTO_TOOL_DIR +from quick.webtools.GeneralGuiTool import GeneralGuiTool + + +class DebugToolImportTool(GeneralGuiTool): + TOOL_DIR = PROTO_TOOL_DIR + SELECT_TEXT = '--- Select module ---' + + @classmethod + def getToolName(cls): + return "Debug import of ProTo tools" + + @classmethod + def getInputBoxNames(cls): + return [('Select tool module with import errors', 'tool'), + ('Error message', 'error')] + + # @classmethod + # def getInputBoxOrder(cls): + # return None + # + # @classmethod + # def getInputBoxGroups(cls, choices=None): + # return None + + @classmethod + def getOptionsBoxTool(cls): + tool_info_dict = getProtoToolList(tool_dir=cls.TOOL_DIR, debug_imports=True) + return [cls.SELECT_TEXT] + \ + [x for x in sorted(set(tool_info.module_name for + tool_info in tool_info_dict.values()))] + @classmethod + def getOptionsBoxError(cls, prevChoices): + if prevChoices.tool != cls.SELECT_TEXT: + try: + import_module(prevChoices.tool) + except Exception: + core = HtmlCore() + core.divBegin(divClass='infomessagesmall') + core.smallHeader('Exception traceback:') + core.preformatted(traceback.format_exc()) + core.divEnd() + return '__rawstr__', str(core) + + # @classmethod + # def getInfoForOptionsBoxKey(cls, prevChoices): + # return None + # + # @classmethod + # def getDemoSelections(cls): + # return ['testChoice1', '..'] + # + # @classmethod + # def getExtraHistElements(cls, choices): + # return None + + @classmethod + def execute(cls, choices, galaxyFn=None, username=''): + import_module(choices.tool) + + @classmethod + def validateAndReturnErrors(cls, choices): + if choices.tool == cls.SELECT_TEXT: + return 'Please select a tool module with import errors' + + # @classmethod + # def getSubToolClasses(cls): + # return None + # + # @classmethod + # def isPublic(cls): + # return False + # + # @classmethod + # def isRedirectTool(cls): + # return False + # + # @classmethod + # def getRedirectURL(cls, choices): + # return '' + # + # @classmethod + # def isHistoryTool(cls): + # return True + # + # @classmethod + # def isBatchTool(cls): + # return cls.isHistoryTool() + # + # @classmethod + # def isDynamic(cls): + # return True + # + # @classmethod + # def getResetBoxes(cls): + # return [] + # + # @classmethod + # def getToolDescription(cls): + # return '' + # + # @classmethod + # def getToolIllustration(cls): + # return None + # + # @classmethod + # def getFullExampleURL(cls): + # return None + # + # @classmethod + # def isDebugMode(cls): + # return False + # + # @classmethod + # def getOutputFormat(cls, choices): + # return 'html' + # + # @classmethod + # def getOutputName(cls, choices=None): + # return cls.getToolSelectionName() diff --git a/lib/proto/tools/DebugToolImportTool.xml b/lib/proto/tools/DebugToolImportTool.xml new file mode 100644 index 000000000000..a1c71eb34e77 --- /dev/null +++ b/lib/proto/tools/DebugToolImportTool.xml @@ -0,0 +1,4 @@ + + + From 9200b219ea599a38c4d661b3c6445d38814a97df Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 3 Mar 2017 17:09:00 +0100 Subject: [PATCH 067/123] ProTo: Added and edited tool descriptions (cherry picked from commit 6b4bbb53e603625e7a62b028d8fd7e1e980f0a77) --- lib/proto/tools/DebugToolImportTool.py | 22 +++++++++++++++++----- lib/proto/tools/ExploreToolsTool.py | 11 ++++++++++- lib/proto/tools/HideInExplorerTool.py | 21 ++++++++++++++++----- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/lib/proto/tools/DebugToolImportTool.py b/lib/proto/tools/DebugToolImportTool.py index d4530a599475..e14abdd46838 100644 --- a/lib/proto/tools/DebugToolImportTool.py +++ b/lib/proto/tools/DebugToolImportTool.py @@ -99,11 +99,23 @@ def validateAndReturnErrors(cls, choices): # @classmethod # def getResetBoxes(cls): # return [] - # - # @classmethod - # def getToolDescription(cls): - # return '' - # + + @classmethod + def getToolDescription(cls): + from proto.HtmlCore import HtmlCore + core = HtmlCore() + core.smallHeader("General description") + core.paragraph("This tool is used for debugging when an uninstalled tool does not appear " + 'in the "Explore ProTo tools" tool. This is typically due to an Python ' + "exception being raised at the import the tool module. This tool " + "lists all modules under the tool directory that cannot be imported " + "due to an exception. When selecting the tool, a traceback of the " + 'exception is shown. One can also click "Execute" to store the exception ' + "within a history element (which will then become red). This is " + "useful if one wants to send a bug report for the exception, or if one " + "wants to keep a backlog of the error messages.") + return str(core) + # @classmethod # def getToolIllustration(cls): # return None diff --git a/lib/proto/tools/ExploreToolsTool.py b/lib/proto/tools/ExploreToolsTool.py index 60dfbb34a9de..c3367d5fcee3 100644 --- a/lib/proto/tools/ExploreToolsTool.py +++ b/lib/proto/tools/ExploreToolsTool.py @@ -1,4 +1,6 @@ -from proto.ProtoToolRegister import getNonHiddenProtoToolList +import os + +from proto.ProtoToolRegister import getNonHiddenProtoToolList, HIDDEN_NONTOOL_MODULES_CONFIG_FN from proto.config.Config import PROTO_TOOL_DIR from proto.tools.GeneralGuiTool import MultiGeneralGuiTool @@ -41,4 +43,11 @@ def getToolDescription(): "exists a Python module with a class that inherits " "from GeneralGuiTool, without there existing " "a Galaxy xml file for the tool.") + core.paragraph("Note: when the list of tools is generated, modules " + "under the tool directory that does not contain " + "ProTo tool classes are stored and excluded from " + "subsequent runs. This is done to improve loading " + "times. The hidden tool modules are stored in the " + '"%s" ' % os.path.basename(HIDDEN_NONTOOL_MODULES_CONFIG_FN) + + 'file in the "config" folder. This file can be manually edited if needed.') return str(core) diff --git a/lib/proto/tools/HideInExplorerTool.py b/lib/proto/tools/HideInExplorerTool.py index 37887903a1a3..9af3a2cf8974 100644 --- a/lib/proto/tools/HideInExplorerTool.py +++ b/lib/proto/tools/HideInExplorerTool.py @@ -1,3 +1,4 @@ +import os from collections import OrderedDict from proto.ProtoToolRegister import (getProtoToolList, getRelativeModulePath, @@ -99,11 +100,21 @@ def isHistoryTool(cls): # @classmethod # def getResetBoxes(cls): # return [] - # - # @classmethod - # def getToolDescription(cls): - # return '' - # + + @classmethod + def getToolDescription(cls): + from proto.HtmlCore import HtmlCore + core = HtmlCore() + core.smallHeader("General description") + core.paragraph("This tool is used to hide modules containing ProTo tools from " + 'the "Explore ProTo tools" tool. This is both useful in order to ' + "to reduce the list, and also to reduce the loading times of the " + "explore tool.") + core.paragraph("Hidden tool modules are stored in the " + '"%s" ' % os.path.basename(HIDDEN_MODULES_CONFIG_FN) + + 'file in the "config" folder. This file can be manually edited if needed.') + return str(core) + # @classmethod # def getToolIllustration(cls): # return None From 3282d838d4ae5ba355ce9e3c043564c65e2598e4 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 3 Mar 2017 20:32:05 +0100 Subject: [PATCH 068/123] ProTo: Reverted import in ToolTemplate. Relative import path to support HB subclassing (cherry picked from commit 0a7dc915507037f8c7682abc3e703cabc92f72f2) --- lib/proto/tools/GenerateToolsTool.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/proto/tools/GenerateToolsTool.py b/lib/proto/tools/GenerateToolsTool.py index 21cb7c980c1d..12ebee22b783 100644 --- a/lib/proto/tools/GenerateToolsTool.py +++ b/lib/proto/tools/GenerateToolsTool.py @@ -161,9 +161,9 @@ def execute(cls, choices, galaxyFn=None, username=''): open(init_py, 'a').close() if choices.template == 'Yes': - templatefn = os.path.join(PROTO_TOOL_DIR, 'ToolTemplate.py') + templatefn = os.path.join(cls.TOOL_DIR, 'ToolTemplate.py') else: - templatefn = os.path.join(PROTO_TOOL_DIR, 'ToolTemplateMinimal.py') + templatefn = os.path.join(cls.TOOL_DIR, 'ToolTemplateMinimal.py') with open(templatefn) as t: template = t.read() From cf018246c80c191d27d493be35054afdc866a102 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 3 Mar 2017 20:34:14 +0100 Subject: [PATCH 069/123] ProTo: Fixed imports (cherry picked from commit f8beb23cbdfacb5da8f84dc86901720982c97a1b) --- lib/proto/tools/DebugToolImportTool.py | 2 +- lib/proto/tools/HideInExplorerTool.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/proto/tools/DebugToolImportTool.py b/lib/proto/tools/DebugToolImportTool.py index e14abdd46838..dedbde8a2203 100644 --- a/lib/proto/tools/DebugToolImportTool.py +++ b/lib/proto/tools/DebugToolImportTool.py @@ -4,7 +4,7 @@ from proto.HtmlCore import HtmlCore from proto.ProtoToolRegister import getProtoToolList from proto.config.Config import PROTO_TOOL_DIR -from quick.webtools.GeneralGuiTool import GeneralGuiTool +from proto.tools.GeneralGuiTool import GeneralGuiTool class DebugToolImportTool(GeneralGuiTool): diff --git a/lib/proto/tools/HideInExplorerTool.py b/lib/proto/tools/HideInExplorerTool.py index 9af3a2cf8974..c6902a7d369b 100644 --- a/lib/proto/tools/HideInExplorerTool.py +++ b/lib/proto/tools/HideInExplorerTool.py @@ -5,7 +5,7 @@ retrieveHiddenModulesSet, storeHiddenModules, HIDDEN_MODULES_CONFIG_FN) from proto.config.Config import PROTO_TOOL_DIR -from quick.webtools.GeneralGuiTool import GeneralGuiTool +from proto.tools.GeneralGuiTool import GeneralGuiTool class HideInExplorerTool(GeneralGuiTool): @@ -13,7 +13,7 @@ class HideInExplorerTool(GeneralGuiTool): @classmethod def getToolName(cls): - return "Hide tool modules from ProTo tool explorer" + return "Hide ProTo tool modules from 'Explore ProTo tools'" @classmethod def getInputBoxNames(cls): From eadbe0607acead5513fb3bb2fd1eac7aba9352f2 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 3 Mar 2017 20:42:13 +0100 Subject: [PATCH 070/123] Now also excludes HB tool templates (cherry picked from commit e89166b38c5b2a5bf45fff01d692787bc8ac113e) --- lib/proto/ProtoToolRegister.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/proto/ProtoToolRegister.py b/lib/proto/ProtoToolRegister.py index 9baef7e53fc7..66d2cdf36191 100644 --- a/lib/proto/ProtoToolRegister.py +++ b/lib/proto/ProtoToolRegister.py @@ -15,7 +15,9 @@ EXPLORE_TOOLS_TOOL_CLS_NAME = 'ExploreToolsTool' DEFAULT_EXCEPT_CLASS_INFO = [('proto.tools.ToolTemplate', 'ToolTemplate'), - ('proto.tools.ToolTemplateMinimal', 'ToolTemplate')] + ('proto.tools.ToolTemplateMinimal', 'ToolTemplate'), + ('quick.webtools.ToolTemplate', 'ToolTemplate'), + ('quick.webtools.ToolTemplateMinimal', 'ToolTemplate')] HIDDEN_MODULES_CONFIG_FN = \ os.path.join(CONFIG_DIR, 'proto_tool_explorer_hidden_modules.txt') From 59dd0c6ed033cdb840c581fd3af62d5c4c8c7cd5 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 3 Mar 2017 22:52:56 +0100 Subject: [PATCH 071/123] ProTo: except_classes -> installed_classes. Fixed logic for filling out nontool hidden modules. (cherry picked from commit fab84c09985010b769657a04c8264322751d760d) --- lib/proto/ProtoToolRegister.py | 46 +++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/lib/proto/ProtoToolRegister.py b/lib/proto/ProtoToolRegister.py index 66d2cdf36191..d21c328c2984 100644 --- a/lib/proto/ProtoToolRegister.py +++ b/lib/proto/ProtoToolRegister.py @@ -14,10 +14,10 @@ EXPLORE_TOOLS_TOOL_CLS_NAME = 'ExploreToolsTool' -DEFAULT_EXCEPT_CLASS_INFO = [('proto.tools.ToolTemplate', 'ToolTemplate'), - ('proto.tools.ToolTemplateMinimal', 'ToolTemplate'), - ('quick.webtools.ToolTemplate', 'ToolTemplate'), - ('quick.webtools.ToolTemplateMinimal', 'ToolTemplate')] +DEFAULT_INSTALLED_CLASS_INFO = [('proto.tools.ToolTemplate', 'ToolTemplate'), + ('proto.tools.ToolTemplateMinimal', 'ToolTemplate'), + ('quick.webtools.ToolTemplate', 'ToolTemplate'), + ('quick.webtools.ToolTemplateMinimal', 'ToolTemplate')] HIDDEN_MODULES_CONFIG_FN = \ os.path.join(CONFIG_DIR, 'proto_tool_explorer_hidden_modules.txt') @@ -95,22 +95,22 @@ def _commonGetProtoToolList(tool_dir=PROTO_TOOL_DIR, except_modules_set=set(), d tmpSysPath = _fixSameNameImportIssues() pys = _findAllPythonFiles(tool_dir) - except_classes_set = _findExceptClassesSet() + installed_classes_set = _findInstalledClassesSet() tool_info_dict = \ _findAllUninstalledTools(pys, tool_dir, except_modules_set, - except_classes_set, debug_imports=debug_imports) + installed_classes_set, debug_imports=debug_imports) sys.path = tmpSysPath return tool_info_dict -def _findExceptClassesSet(): - except_class_info = DEFAULT_EXCEPT_CLASS_INFO + getInstalledProtoTools() - except_classes_set = set([getUniqueKeyForClass(module, class_name) for - module, class_name in except_class_info]) - return except_classes_set +def _findInstalledClassesSet(): + installed_class_info = DEFAULT_INSTALLED_CLASS_INFO + getInstalledProtoTools() + installed_classes_set = set([getUniqueKeyForClass(module, class_name) for + module, class_name in installed_class_info]) + return installed_classes_set def _fixSameNameImportIssues(): @@ -132,12 +132,12 @@ def _filterInstalledSubClassTools(tmp_tool_info_dict, all_installed_sub_classes) def _findAllUninstalledTools(pys, tool_dir, except_modules_set, - except_classes_set, debug_imports=False): + installed_classes_set, debug_imports=False): all_modules = _findAllModulesWithClasses(except_modules_set, pys) - except_classes_set.update(_findInstalledSubClasses(all_modules, except_classes_set)) + installed_classes_set.update(_findInstalledSubClasses(all_modules, installed_classes_set)) tool_info_dict = _getInfoForAllUninstalledTools( - all_modules, tool_dir, except_classes_set, debug_imports=debug_imports) + all_modules, tool_dir, installed_classes_set, debug_imports=debug_imports) return tool_info_dict @@ -174,28 +174,38 @@ def _findInstalledSubClasses(all_modules, except_classes_set): return all_installed_sub_classes_set -def _getInfoForAllUninstalledTools(all_modules, tool_dir, except_classes_set, debug_imports=False): +def _getInfoForAllUninstalledTools(all_modules, tool_dir, + installed_classes_set, debug_imports=False): tool_info_dict = OrderedDict() for module_name, class_info_list in all_modules: + module_has_nontool_cls = False + module_has_tool_cls = False + for class_name, super_classes in class_info_list: uniqueKey = getUniqueKeyForClass(module_name, class_name) try: - if uniqueKey not in except_classes_set: + if uniqueKey not in installed_classes_set: tool_selection_name, tool_info = \ _extractToolInfo(class_name, module_name, tool_dir) if tool_selection_name: + module_has_tool_cls = True if not debug_imports: tool_info_dict[tool_selection_name] = tool_info else: - if not MULTI_GENERAL_GUI_TOOL in super_classes: - _storeAsNonToolHiddenModule(module_name) + module_has_nontool_cls = True + else: + module_has_tool_cls = True except Exception as e: if debug_imports: tool_selection_name = \ _createToolSelectionName(class_name, module_name, tool_dir) tool_info_dict[tool_selection_name] = ProtoToolInfo(class_name, module_name) + if module_has_nontool_cls and not module_has_tool_cls \ + and not MULTI_GENERAL_GUI_TOOL in super_classes: + _storeAsNonToolHiddenModule(module_name) + return tool_info_dict From 8410615786b8a7354a8a579d59543e253fc9efc0 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 3 Mar 2017 22:53:28 +0100 Subject: [PATCH 072/123] ProTo: rename tools (cherry picked from commit eb12f7366045ab8eeab19a10d48dfae8a0a95cf4) --- lib/proto/tools/ExploreToolsTool.py | 2 +- lib/proto/tools/GenerateToolsTool.py | 2 +- lib/proto/tools/InstallToolsTool.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/proto/tools/ExploreToolsTool.py b/lib/proto/tools/ExploreToolsTool.py index c3367d5fcee3..22b72cd12ca3 100644 --- a/lib/proto/tools/ExploreToolsTool.py +++ b/lib/proto/tools/ExploreToolsTool.py @@ -11,7 +11,7 @@ class ExploreToolsTool(MultiGeneralGuiTool): @staticmethod def getToolName(): - return "ProTo tool explorer" + return "Explore ProTo tools" @staticmethod def getToolSelectionName(): diff --git a/lib/proto/tools/GenerateToolsTool.py b/lib/proto/tools/GenerateToolsTool.py index 12ebee22b783..d4fd9cf1baf9 100644 --- a/lib/proto/tools/GenerateToolsTool.py +++ b/lib/proto/tools/GenerateToolsTool.py @@ -22,7 +22,7 @@ def __new__(cls, *args, **kwargs): @staticmethod def getToolName(): - return "ProTo tool generator" + return "Generate ProTo tool" @classmethod def _getDirSelectionInputBoxNames(cls): diff --git a/lib/proto/tools/InstallToolsTool.py b/lib/proto/tools/InstallToolsTool.py index 01ddc2557eb7..bfd3ec3bd61d 100644 --- a/lib/proto/tools/InstallToolsTool.py +++ b/lib/proto/tools/InstallToolsTool.py @@ -31,7 +31,7 @@ def _getPrototype(cls, tool): @staticmethod def getToolName(): - return "ProTo tool installer" + return "Install ProTo tool" @staticmethod def getInputBoxNames(): From d5295e4be6cac14f577c965b8a8ac355f5579062 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 3 Mar 2017 22:56:55 +0100 Subject: [PATCH 073/123] Added initial "hidden nontool modules" config file (cherry picked from commit 583878b54e11663814a012159f3077a93dc4188f) --- .../proto_tool_explorer_hidden_nontool_modules.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 config/proto_tool_explorer_hidden_nontool_modules.txt diff --git a/config/proto_tool_explorer_hidden_nontool_modules.txt b/config/proto_tool_explorer_hidden_nontool_modules.txt new file mode 100644 index 000000000000..dcaa42ad6b68 --- /dev/null +++ b/config/proto_tool_explorer_hidden_nontool_modules.txt @@ -0,0 +1,13 @@ +/hb/quick/webtools/GeneralGuiTool +/hb/quick/webtools/imports/TrackSearchTool +/hb/quick/webtools/mixin/UserBinMixin +/hb/quick/webtools/mixin/GenomeMixin +/hb/quick/webtools/mixin/DebugMixin +/hb/quick/webtools/mixin/BasicModeAnalysisInfoMixin +/hb/quick/webtools/mixin/MultiTrackMixin +/hb/quick/webtools/mixin/GSuiteResultsTableMixin +/hb/quick/webtools/wizard/WizardFlowConfig +/hb/quick/webtools/restricted/DianasTool3 +/hb/quick/webtools/restricted/EncodeDatabase +/hb/quick/webtools/restricted/visualization/visualizationGraphs +/proto/tools/GeneralGuiTool From 23c2a5039249466f652ea44414eeabb577493f54 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Sat, 8 Apr 2017 15:44:21 +0200 Subject: [PATCH 074/123] ProTo: Removed GPL Copyright messages from code files (cherry picked from commit da796dd49b07960fef33641e5f50e4360612d877) --- lib/galaxy/webapps/galaxy/controllers/proto.py | 16 ---------------- lib/proto/BaseToolController.py | 17 ----------------- lib/proto/CommonFunctions.py | 16 ---------------- lib/proto/generictool.py | 15 --------------- lib/proto/hyper_gui.py | 16 ---------------- lib/proto/protoToolExecute.py | 16 ---------------- templates/proto/base.mako | 17 ----------------- templates/proto/functions.mako | 17 ----------------- templates/proto/generictool.mako | 16 ---------------- 9 files changed, 146 deletions(-) diff --git a/lib/galaxy/webapps/galaxy/controllers/proto.py b/lib/galaxy/webapps/galaxy/controllers/proto.py index 79e65900f350..b2208ecb757e 100644 --- a/lib/galaxy/webapps/galaxy/controllers/proto.py +++ b/lib/galaxy/webapps/galaxy/controllers/proto.py @@ -1,19 +1,3 @@ -# Copyright (C) 2009, Geir Kjetil Sandve, Sveinung Gundersen and Morten Johansen -# This file is part of The Genomic HyperBrowser. -# -# The Genomic HyperBrowser is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# The Genomic HyperBrowser is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with The Genomic HyperBrowser. If not, see . - from __future__ import absolute_import import logging diff --git a/lib/proto/BaseToolController.py b/lib/proto/BaseToolController.py index 4c3ba30debb6..12cc27a83ddf 100644 --- a/lib/proto/BaseToolController.py +++ b/lib/proto/BaseToolController.py @@ -1,20 +1,3 @@ -# Copyright (C) 2009, Geir Kjetil Sandve, Sveinung Gundersen and Morten Johansen -# This file is part of The Genomic HyperBrowser. -# -# The Genomic HyperBrowser is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# The Genomic HyperBrowser is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with The Genomic HyperBrowser. If not, see . -# - import sys from collections import OrderedDict from urllib import unquote diff --git a/lib/proto/CommonFunctions.py b/lib/proto/CommonFunctions.py index ce04b7aaba5d..27df723944d3 100644 --- a/lib/proto/CommonFunctions.py +++ b/lib/proto/CommonFunctions.py @@ -1,19 +1,3 @@ -# Copyright (C) 2009, Geir Kjetil Sandve, Sveinung Gundersen and Morten Johansen -# This file is part of The Genomic HyperBrowser. -# -# The Genomic HyperBrowser is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# The Genomic HyperBrowser is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with The Genomic HyperBrowser. If not, see . - import os import re import urllib diff --git a/lib/proto/generictool.py b/lib/proto/generictool.py index f91393ceffa4..8865888a7bbe 100644 --- a/lib/proto/generictool.py +++ b/lib/proto/generictool.py @@ -1,18 +1,3 @@ -# Copyright (C) 2009, Geir Kjetil Sandve, Sveinung Gundersen and Morten Johansen -# This file is part of The Genomic HyperBrowser. -# -# The Genomic HyperBrowser is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# The Genomic HyperBrowser is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with The Genomic HyperBrowser. If not, see . # # instance is dynamically imported into namespace of .mako template (see web/controllers/hyper.py) diff --git a/lib/proto/hyper_gui.py b/lib/proto/hyper_gui.py index 703d4ecb39d2..d7f1ce09a6a6 100644 --- a/lib/proto/hyper_gui.py +++ b/lib/proto/hyper_gui.py @@ -1,19 +1,3 @@ -# Copyright (C) 2009, Geir Kjetil Sandve, Sveinung Gundersen and Morten Johansen -# This file is part of The Genomic HyperBrowser. -# -# The Genomic HyperBrowser is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# The Genomic HyperBrowser is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with The Genomic HyperBrowser. If not, see . - #note cannot import HB.GalaxyInterface here due to rpy threading issue from urllib import quote, unquote diff --git a/lib/proto/protoToolExecute.py b/lib/proto/protoToolExecute.py index 8347c5e4c5c3..fb0c852d558f 100644 --- a/lib/proto/protoToolExecute.py +++ b/lib/proto/protoToolExecute.py @@ -1,19 +1,3 @@ -# Copyright (C) 2009, Geir Kjetil Sandve, Sveinung Gundersen and Morten Johansen -# This file is part of The Genomic HyperBrowser. -# -# The Genomic HyperBrowser is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# The Genomic HyperBrowser is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with The Genomic HyperBrowser. If not, see . - import sys from proto.generictool import getController diff --git a/templates/proto/base.mako b/templates/proto/base.mako index 62d7b91f3797..ca1fab682dbc 100644 --- a/templates/proto/base.mako +++ b/templates/proto/base.mako @@ -1,20 +1,3 @@ - <%inherit file="/base.mako"/> <%def name="title()"> diff --git a/templates/proto/functions.mako b/templates/proto/functions.mako index 19019753b053..33d237f1ae67 100644 --- a/templates/proto/functions.mako +++ b/templates/proto/functions.mako @@ -1,20 +1,3 @@ - <%! import sys from cgi import escape diff --git a/templates/proto/generictool.mako b/templates/proto/generictool.mako index db67ae0db739..2c8a1273c458 100644 --- a/templates/proto/generictool.mako +++ b/templates/proto/generictool.mako @@ -1,20 +1,4 @@ <%! -# Copyright (C) 2009, Geir Kjetil Sandve, Sveinung Gundersen and Morten Johansen -# This file is part of The Genomic HyperBrowser. -# -# The Genomic HyperBrowser is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# The Genomic HyperBrowser is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with The Genomic HyperBrowser. If not, see . - import sys, os, traceback, json from zlib import compress from base64 import urlsafe_b64encode From a5d7493c3e0e2436e26381e02be445bc02cbabdc Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Wed, 12 Apr 2017 20:00:42 +0200 Subject: [PATCH 075/123] Fixed bug in loading old HB analysis tool (cherry picked from commit 0f5f77e2a3e585968f71b03f275b3fcc752ab8ee) --- lib/galaxy/webapps/galaxy/controllers/proto.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/galaxy/webapps/galaxy/controllers/proto.py b/lib/galaxy/webapps/galaxy/controllers/proto.py index b2208ecb757e..9f1dd18e8f52 100644 --- a/lib/galaxy/webapps/galaxy/controllers/proto.py +++ b/lib/galaxy/webapps/galaxy/controllers/proto.py @@ -100,6 +100,9 @@ def run_tool(self, mako, trans): @web.expose def index(self, trans, mako='generictool', **kwd): + return self._index(trans, mako, **kwd) + + def _index(self, trans, mako, **kwd): if kwd.has_key('rerun_hda_id'): self._import_job_params(trans, kwd['rerun_hda_id']) From f7b9deb877ad538c7e9b6f11393b2e4c785a210f Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Sun, 7 May 2017 16:36:42 +0200 Subject: [PATCH 076/123] ProTo: Added standard excluded 'files' directory to gitignore (cherry picked from commit 62dd13659324931e7003b1a812410946ba648bf5) --- static/proto/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 static/proto/.gitignore diff --git a/static/proto/.gitignore b/static/proto/.gitignore new file mode 100644 index 000000000000..027271b9b22c --- /dev/null +++ b/static/proto/.gitignore @@ -0,0 +1 @@ +files From dbe827d79b375d4104136a0eaa12e76381a3c25d Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Sat, 20 May 2017 01:50:20 +0200 Subject: [PATCH 077/123] ProTo: now does not print names instead of label='' in various box types. (cherry picked from commit d806f1fc4fa76c63462b672d55e2ac4a366df5d0) --- templates/proto/functions.mako | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/proto/functions.mako b/templates/proto/functions.mako index 33d237f1ae67..6482da917b82 100644 --- a/templates/proto/functions.mako +++ b/templates/proto/functions.mako @@ -16,7 +16,7 @@ import proto.hyper_gui as gui <%def name="select(name, opts, value, label = None, info = None)"> -

From d87a4bf8c3d7cd018f49210e20adfe18fdd5197d Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Fri, 3 Mar 2017 22:56:06 +0100 Subject: [PATCH 085/123] Added new tools to tool_conf.xml sample files. (cherry picked from commit 4c35fa30e30d0fed972e66b0107391977d71f143) --- config/tool_conf.xml.sample | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/tool_conf.xml.sample b/config/tool_conf.xml.sample index 54911188ca9f..105004daf8e2 100644 --- a/config/tool_conf.xml.sample +++ b/config/tool_conf.xml.sample @@ -119,6 +119,8 @@
+ + From e095142cbd8d1ccf7b41cf5a7eba1bdbe4ccc83d Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Sun, 7 Jan 2018 21:10:25 +0100 Subject: [PATCH 086/123] Changed linebreaks to LF --- lib/proto/generictool.py | 1392 +++++++++++++++++++------------------- 1 file changed, 696 insertions(+), 696 deletions(-) diff --git a/lib/proto/generictool.py b/lib/proto/generictool.py index 8865888a7bbe..a3506a396540 100644 --- a/lib/proto/generictool.py +++ b/lib/proto/generictool.py @@ -1,696 +1,696 @@ -# -# instance is dynamically imported into namespace of .mako template (see web/controllers/hyper.py) - -import sys, os, json, shelve -import cPickle as pickle -from zlib import compress, decompress -from base64 import urlsafe_b64decode, urlsafe_b64encode, b64encode, b64decode -from collections import namedtuple, OrderedDict, defaultdict -from urllib import quote, unquote -from proto.tools.GeneralGuiTool import HistElement -from proto.HtmlCore import HtmlCore -from proto.config.Config import URL_PREFIX, GALAXY_BASE_DIR -from proto.config.Security import galaxySecureEncodeId, galaxySecureDecodeId, GALAXY_SECURITY_HELPER_OBJ -from BaseToolController import BaseToolController -from proto.ProtoToolRegister import getToolPrototype -from proto.StaticFile import StaticImage - - -def getClassName(obj): - return obj.__class__.__name__ - - -class GenericToolController(BaseToolController): - STATIC_IMAGE_CLS = StaticImage - initChoicesDict = None - - def __init__(self, trans, job): - BaseToolController.__init__(self, trans, job) - self.errorMessage = None - self.toolId = self.params.get('tool_id', 'default_tool_id') - - if self.params.has_key('old_values'): - self.oldValues = json.loads(unquote(self.params.get('old_values'))) - self.use_default = False - else: - # initial tool state, no manual selections made, not reloaded - self.oldValues = {} - self.use_default = True - - - self.subClassId = unquote(self.params.get('sub_class_id', '')) - - self.prototype = getToolPrototype(self.toolId) - - self._monkeyPatchAttr('userName', self.params.get('userEmail')) - - self.subClasses = OrderedDict() - subClasses = self.prototype.getSubToolClasses() - if subClasses: - self.subToolSelectionTitle = self.prototype.getSubToolSelectionTitle() - self.subClasses[self.prototype.getToolSelectionName()] = self.prototype.__class__ - for subcls in subClasses: - toolSelectionName = subcls.getToolSelectionName() - if self.prototype.useSubToolPrefix(): -# toolSelectionName = subcls.__class__.__name__ + ': ' + toolSelectionName - toolModule = subcls.__module__.split('.')[2:] - if subcls.__name__ != toolModule[-1]: - toolSelectionName = '.'.join(toolModule) + ' [' + subcls.__name__ + ']: ' + toolSelectionName - else: - toolSelectionName = '.'.join(toolModule) + ': ' + toolSelectionName - self.subClasses[toolSelectionName] = subcls - - self.resetAll = False - if self.subClassId and self.subClassId in self.subClasses: - self.prototype = self.subClasses[self.subClassId]() - if not self.oldValues.has_key('sub_class_id') or self.oldValues['sub_class_id'] != self.subClassId: - self.oldValues['sub_class_id'] = self.subClassId - # do not reset boxes/ignore params if we are called with parameters in the url (e.g redirect or demo link) - if not self.use_default: - self.resetAll = True - - self.inputTypes = [] - self.inputValues = [] - self.displayValues = [] - self.inputInfo = [] - self.inputIds = [] - self.inputNames = [] - self._getInputBoxNames() - self.inputOrder = self._getIdxList(self.prototype.getInputBoxOrder()) - self.resetBoxes = self._getIdxList(self.prototype.getResetBoxes()) - - self.extra_output = [] - - self._init() - - self._initCache() - if trans: - self.action() - - self.inputGroup = self._getInputGroup(self.prototype.getInputBoxGroups(self.choices)) - - if hasattr(self.prototype, 'getExtraHistElements'): - extra_output = self.prototype.getExtraHistElements(self.choices) - if extra_output: - for e in extra_output: - if isinstance(e, HistElement): - self.extra_output.append((e.name, e.format, e.label, e.hidden)) - else: - self.extra_output.append(e) - - def _init(self): - if hasattr(super(GenericToolController, self), '_init'): - super(GenericToolController, self)._init() - - def _getInputGroup(self, inputBoxGroups): - startGroupInfo = defaultdict(list) - endIdxs = list() - if inputBoxGroups: - for group in inputBoxGroups: - idxBegin = self._getIdxForBoxId(group[1]) - idxEnd = self._getIdxForBoxId(group[2]) - startGroupInfo[idxBegin].append(group[0]) - endIdxs.append(idxEnd) - return startGroupInfo, endIdxs - - def _getInputBoxNames(self): - names = self.prototype.getInputBoxNames() - for i in range(len(names)): - name = names[i] - if isinstance(name, tuple): - id = name[1] - name = name[0] - else: - id = 'box' + str(1 + i) - self.inputIds.append(id) - self.inputNames.append(name) - - def _getIdxList(self, inputList): - idxList = [] - if inputList == None: - idxList = range(len(self.inputIds)) - else: - for i in inputList: - if isinstance(i, basestring): - try: - idx = self.inputIds.index(i) - except ValueError: - if i.startswith('box'): - idx = int(i[3:]) - 1 - else: - idx = i - 1 - if idx < len(self.inputIds): - idxList.append(idx) - else: - raise IndexError('List index out of range: %d >= %d' % (idx, len(self.inputIds))) - return idxList - - def _getIdxForBoxId(self, i): - if isinstance(i, basestring): - try: - idx = self.inputIds.index(i) - except ValueError: - if i.startswith('box'): - idx = int(i[3:]) - 1 - else: - idx = i - 1 - return idx - - def _getOptionsBox(self, i, val = None): - id = self.inputIds[i] - id = id[0].upper() + id[1:] - info = None - if i > 0: - ChoiceTuple = namedtuple('ChoiceTuple', self.inputIds[:(i+1)]) - prevChoices = ChoiceTuple._make(self.inputValues + [val]) - #self.choices = prevChoices - if id.startswith('Box'): - opts = getattr(self.prototype, 'getOptions' + id)(prevChoices) - try: - info = getattr(self.prototype, 'getInfoForOptions' + id)(prevChoices) - except: - pass - else: - opts = getattr(self.prototype, 'getOptionsBox' + id)(prevChoices) - try: - info = getattr(self.prototype, 'getInfoForOptionsBox' + id)(prevChoices) - except: - pass - else: - if id.startswith('Box'): - opts = getattr(self.prototype, 'getOptions' + id)() - try: - info = getattr(self.prototype, 'getInfoForOptions' + id)() - except: - pass - else: - opts = getattr(self.prototype, 'getOptionsBox' + id)() - try: - info = getattr(self.prototype, 'getInfoForOptionsBox' + id)() - except: - pass - return opts, info - - - def _initCache(self): - self.input_changed = False - try: - self.cachedParams = self.decodeCache(self.params.get('cached_params')) - except Exception as e: - #print 'cached_params', e - self.cachedParams = {} - - try: - self.cachedOptions = self.decodeCache(self.params.get('cached_options')) - except Exception as e: - #print 'cached_options', e - self.cachedOptions = {} - - try: - self.cachedExtra = self.decodeCache(self.params.get('cached_extra')) - except Exception as e: - #print 'cached_extra', e - self.cachedExtra = {} - - def encodeCache(self, data): - if not data: - return '' - return GALAXY_SECURITY_HELPER_OBJ.encode_guid(json.dumps(data)) - #return urlsafe_b64encode(compress(json.dumps(data))) - - def decodeCache(self, data): - if not data: - raise Exception('Nothing to decode') - return json.loads(GALAXY_SECURITY_HELPER_OBJ.decode_guid(str(data))) - #return json.loads(decompress(urlsafe_b64decode(str(data)))) - - def putCacheData(self, id, data): - self.cachedExtra[id] = pickle.dumps(data) - - def getCacheData(self, id): - return pickle.loads(b64decode(str(self.cachedExtra[id]))) - - def putCachedOption(self, id, data): - self.cachedOptions[id] = b64encode(pickle.dumps(data)) - - def getCachedOption(self, id): - return pickle.loads(str(self.cachedOptions[id])) - - def getOptionsBox(self, id, i, val): - #print id, '=', val, 'cache=', self.cachedParams[id] if id in self.cachedParams else 'NOT' - - if self.input_changed or id not in self.cachedParams: - opts, info = self._getOptionsBox(i, val) - self.input_changed = True - else: - try: - opts, info = self.getCachedOption(id) - #print 'from cache:',id - self.input_changed = (val != self.cachedParams[id]) - except Exception as e: - # print 'cache load failed for id "%s": %s' % (id, e) - opts, info = self._getOptionsBox(i, val) - self.input_changed = True - - #print repr(opts) - self.cachedParams[id] = val - self.putCachedOption(id, (opts, info)) - self.inputInfo.append(info) - return opts - - - def action(self): - self.options = [] - reset = self.resetAll - - for i in range(len(self.inputNames)): - name = self.inputNames[i] - id = self.inputIds[i] - if self.initChoicesDict: - val = self.initChoicesDict[id] - else: - val = self.params.get(id) - - display_only = False - opts = self.getOptionsBox(id, i, val) - - if reset and not self.initChoicesDict: - val = None - - if opts == None: - self.inputTypes += [None] - val = None - - elif isinstance(opts, dict) or opts == '__genomes__': - self.inputTypes += ['multi'] - if opts == '__genomes__': - try: - opts = self.cachedExtra[id] - except: - opts = self.getDictOfAllGenomes() - self.cachedExtra[id] = opts - if not self.initChoicesDict: - values = type(opts)() - for k,v in opts.items(): - #values[k] = bool(self.params.get(id + '|' + k, False if val else v)) - values[k] = bool(self.params.get(id + '|' + k , False) if val else v) - val = values - - elif isinstance(opts, basestring): - if opts == '__genome__': - self.inputTypes += ['__genome__'] - try: - genomeCache = self.getCacheData(id) - except Exception as e: - #raise e - print 'genome cache empty', e - genomeCache = self._getAllGenomes() - #print genomeCache - self.putCacheData(id, genomeCache) - - opts = self.getGenomeElement(id, genomeCache) - val = self.getGenome(id) - - elif opts == '__track__': - self.inputTypes += ['__track__'] - val = self.getInputValueForTrack(id, name) - - elif opts == '__password__': - self.inputTypes += ['__password__'] - if val == None: - val = '' - - else: - self.inputTypes += ['text'] - if val == None: - val = opts - opts = (val, 1, False) - - elif isinstance(opts, tuple): - if opts[0] == '__history__': - self.inputTypes += opts[:1] - opts = self.galaxy.optionsFromHistoryFn(exts = opts[1:] if len(opts)>1 else None, select = val) - if val == None and opts and len(opts[1]) > 0: - val = opts[1][0] - #opts = self.galaxy.getHistory(GalaxyInterface.getSupportedGalaxyFileFormats()) - elif opts[0] == '__toolhistory__': - self.inputTypes += opts[:1] - opts = self.galaxy.optionsFromHistoryFn(tools = opts[1:] if len(opts)>1 else None, select = val) - if val == None and opts and len(opts[1]) > 0: - val = opts[1][0] - elif opts[0] == '__multihistory__': - self.inputTypes += opts[:1] - opts = self.galaxy.itemsFromHistoryFn(opts[1:] if len(opts)>1 else None) - if not self.initChoicesDict: - values = OrderedDict() - for k,v in opts.items(): - itemval = self.params.get(id + '|' + k, None) - #if itemval: - values[unicode(k)] = itemval - - val = values - - elif opts[0] == '__track__': - self.inputTypes += ['__track__'] - val = self.getInputValueForTrack(id, name) - - elif opts[0] == '__hidden__': - self.inputTypes += opts[:1] - if opts[1] != None: - val = opts[1] - #elif val: - # val = unquote(val) - elif len(opts) in [2, 3] and (isinstance(opts[0], basestring)): - if len(opts) == 2: - opts = opts + (False,) - if isinstance(opts[1], int): - if isinstance(opts[2], bool): - if opts[2]: - self.inputTypes += ['text_readonly'] - val = opts[0] - #display_only = True - else: - self.inputTypes += ['text'] - if val == None: - val = opts[0] - else: - self.inputTypes += ['rawStr'] - val = opts[1] - display_only = True - else: - self.inputTypes += [None] - val = None - - elif isinstance(opts, list): - if len(opts) > 0 and isinstance(opts[0], list): - self.inputTypes += ['table'] - core = HtmlCore() - core.tableHeader(opts[0], sortable=True) - if len(opts) > 1: - for r in range(1, len(opts)): - core.tableLine(opts[r]) - core.tableFooter() - val = unicode(core) - display_only = True - - else: - self.inputTypes += ['select'] - if len(opts) > 0 and (val == None or val not in opts): - val = opts[0] - - elif isinstance(opts, bool): - self.inputTypes += ['checkbox'] - val = True if val == "True" else opts if self.use_default else False - - #elif isinstance(opts, list) and len(opts) == 0: - # self.inputTypes += ['text'] - # if val == None: - # val = '' - - self.displayValues.append(val if isinstance(val, basestring) else repr(val)) - self.inputValues.append(None if display_only else val) - self.options.append(opts) - - oldval = self.oldValues[id] if id in self.oldValues else None - if i in self.resetBoxes: - self.oldValues[id] = val - if oldval == None or val != oldval: - reset = True - - ChoiceTuple = namedtuple('ChoiceTuple', self.inputIds) - self.choices = ChoiceTuple._make(self.inputValues) - self.validate() - - def _action(self): - pass - - - def decodeChoice(self, opts, id, choice): - if opts == '__genome__': - id = 'dbkey' - choice = str(self.params[id]) if self.params.has_key(id) else '' - - # if isinstance(opts, tuple): - # if opts[0] == '__hidden__': - # choice = unquote(choice) - - if opts == '__genomes__' or (isinstance(opts, tuple) and opts[0] == '__multihistory__'): - values = {} - for key in self.params.keys(): - if key.startswith(id + '|'): - values[key.split('|')[1]] = self.params[key] - choice = OrderedDict(sorted(values.items(), \ - key=lambda t: int(t[0]) if opts[0] == '__multihistory__' else t[0])) - - if isinstance(opts, dict): - values = type(opts)() - for k, v in opts.items(): - if self.params.has_key(id + '|' + k): - values[k] = self.params[id + '|' + k] - else: - values[k] = False - choice = values - - if isinstance(opts, bool): - choice = True if choice == "True" else False - - return choice - - @staticmethod - def _getStdOutToHistoryDatatypes(): - return ['html', 'customhtml'] - - def execute(self): - outputFormat = self.params['datatype'] if self.params.has_key('datatype') else 'html' - if outputFormat in self._getStdOutToHistoryDatatypes(): - self.stdoutToHistory() - - for i in range(len(self.inputIds)): - id = self.inputIds[i] - choice = self.params[id] if self.params.has_key(id) else '' - - opts = self.getOptionsBox(id, i, choice) - - choice = self.decodeChoice(opts, id, choice) - -# if opts == '__genome__': -# id = 'dbkey' -# choice = str(self.params[id]) if self.params.has_key(id) else '' -# -# # if isinstance(opts, tuple): -# # if opts[0] == '__hidden__': -# # choice = unquote(choice) -# -# if opts == '__track__' or (isinstance(opts, tuple) and opts[0] == '__track__'): -# #tn = choice.split(':') -# #GalaxyInterface.cleanUpTrackName(tn) -# #choice = ':'.join(tn) -# choice = self.decodeChoiceForTrack(choice) -# -# if opts == '__genomes__' or (isinstance(opts, tuple) and opts[0] == '__multihistory__'): -# values = {} -# for key in self.params.keys(): -# if key.startswith(id + '|'): -# values[key.split('|')[1]] = self.params[key] -# choice = OrderedDict(sorted(values.items(), \ -# key=lambda t: int(t[0]) if opts[0] == '__multihistory__' else t[0])) -# -# if isinstance(opts, dict): -# values = type(opts)() -# for k,v in opts.items(): -# if self.params.has_key(id + '|' + k): -# values[k] = self.params[id + '|' + k] -# else: -# values[k] = False -# choice = values -# -# if isinstance(opts, bool): -# choice = True if choice == "True" else False - - self.inputValues.append(choice) - - # if self.params.has_key('Track_state'): - # self.inputValues.append(unquote(self.params['Track_state'])) - - ChoiceTuple = namedtuple('ChoiceTuple', self.inputIds) - choices = ChoiceTuple._make(self.inputValues) - - #batchargs = '|'.join([';'.join(c.itervalues()) if not isinstance(c, basestring) else c for c in choices]) - #batchargs = '|'.join([repr(c.items()) if not isinstance(c, basestring) else c for c in choices]) - - #print choices - if outputFormat == 'html': - print ''' - - - - - - -

Toggle debug

-
-            ''' % {'prefix': URL_PREFIX}
-        #    print '
Corresponding batch run line:\n', '$Tool[%s](%s)
' % (self.toolId, batchargs) - - - self.extraGalaxyFn = {} - -# if hasattr(self.prototype, 'getExtraHistElements'): -# for output in self.prototype.getExtraHistElements(choices): -# if isinstance(output, HistElement): -# self.extraGalaxyFn[output.name] = self.params[output.name] -# else: -# self.extraGalaxyFn[output[0]] = self.params[output[0]] - - if hasattr(self.prototype, 'getExtraHistElements') and self.params.has_key('extra_output'): - extra_output = json.loads(unquote(self.params['extra_output'])) - if isinstance(extra_output, list): - for output in extra_output: - if isinstance(output, HistElement): - self.extraGalaxyFn[unicode(output.name)] = self.params[output.name] - else: - self.extraGalaxyFn[unicode(output[0])] = self.params[output[0]] - - - username = self.params['userEmail'] if 'userEmail' in self.params else '' - self._executeTool(getClassName(self.prototype), choices, galaxyFn=self.jobFile, username=username) - - if outputFormat == 'html': - print ''' -
- - - - ''' - - def _executeTool(self, toolClassName, choices, galaxyFn, username): - if hasattr(super(GenericToolController, self), '_executeTool'): - super(GenericToolController, self)._executeTool( - toolClassName, choices, galaxyFn, username) - - self._monkeyPatchAttr('extraGalaxyFn', self.extraGalaxyFn) - self._monkeyPatchAttr('runParams', self.json_params) - self.prototype.execute(choices, galaxyFn=galaxyFn, username=username) - - def _monkeyPatchAttr(self, name, value): - if type(self.prototype).__name__ == 'type': - setattr(self.prototype, name, value) - else: - setattr(self.prototype.__class__, name, value) - - def executeNoHistory(self): - html = self.prototype.execute(self.choices, None, self.galaxy.getUserName()) - if not html: - html = 'Finished executing tool.' - return html - - def isPublic(self): - try: - return self.prototype.isPublic() - except: - return False - - def isDebugging(self): - try: - return self.prototype.isDebugMode() - except: - return False - - def getIllustrationImage(self): - image = None - id = self.prototype.getToolIllustration() - if id: - image = self.STATIC_IMAGE_CLS(id) - return image - - def getDemoURL(self): - try: - demo = self.prototype.getDemoSelections() - url = '?mako=generictool&tool_id=' + self.toolId - for i, id in enumerate(self.inputIds): - if self.inputTypes[i] == '__genome__': - id = 'dbkey' - #else: - # id = self.inputIds[i] - try: - val = getattr(demo, id) - except: - val = demo[i] - url += '&' + id + '=' + val - except Exception, e: - from gold.application.LogSetup import logException - logException(e) - url = None - return url - - def hasDemoURL(self): - try: - demo = self.prototype.getDemoSelections() - if len(demo) > 0: - return True - except: - pass - return False - - def getFullExampleURL(self): - try: - url = self.prototype.getFullExampleURL() - except Exception, e: - from gold.application.LogSetup import logException - logException(e) - url = None - return url - - def hasFullExampleURL(self): - try: - url = self.prototype.getFullExampleURL() - if url is not None: - return True - except Exception, e: - from gold.application.LogSetup import logException - logException(e) - pass - return False - - def isRedirectTool(self): - try: - return self.prototype.isRedirectTool(self.choices) - except TypeError: - return self.prototype.isRedirectTool() - - def doRedirect(self): - return self.isRedirectTool() and self.getRedirectURL() and self.params.has_key('start') - - def getRedirectURL(self): - return self.prototype.getRedirectURL(self.choices) - - def validate(self): - #ChoiceTuple = namedtuple('ChoiceTuple', self.inputIds) - #self.choices = ChoiceTuple._make(self.inputValues) - self.errorMessage = self.prototype.validateAndReturnErrors(self.choices) - - def isValid(self): - return True if self.errorMessage is None else False - - def hasErrorMessage(self): - return False if self.errorMessage in [None, ''] else True - - #jsonMethods = ('ajaxValidate') - #def ajaxValidate(self): - # return self.prototype.validateAndReturnErrors(self.inputValues) - - - def getInputValueForTrack(self, id, name): - return None - - -def getController(transaction = None, job = None): - #from gold.util.Profiler import Profiler - #prof = Profiler() - #prof.start() - control = GenericToolController(transaction, job) - #prof.stop() - #prof.printStats() - return control +# +# instance is dynamically imported into namespace of .mako template (see web/controllers/hyper.py) + +import sys, os, json, shelve +import cPickle as pickle +from zlib import compress, decompress +from base64 import urlsafe_b64decode, urlsafe_b64encode, b64encode, b64decode +from collections import namedtuple, OrderedDict, defaultdict +from urllib import quote, unquote +from proto.tools.GeneralGuiTool import HistElement +from proto.HtmlCore import HtmlCore +from proto.config.Config import URL_PREFIX, GALAXY_BASE_DIR +from proto.config.Security import galaxySecureEncodeId, galaxySecureDecodeId, GALAXY_SECURITY_HELPER_OBJ +from BaseToolController import BaseToolController +from proto.ProtoToolRegister import getToolPrototype +from proto.StaticFile import StaticImage + + +def getClassName(obj): + return obj.__class__.__name__ + + +class GenericToolController(BaseToolController): + STATIC_IMAGE_CLS = StaticImage + initChoicesDict = None + + def __init__(self, trans, job): + BaseToolController.__init__(self, trans, job) + self.errorMessage = None + self.toolId = self.params.get('tool_id', 'default_tool_id') + + if self.params.has_key('old_values'): + self.oldValues = json.loads(unquote(self.params.get('old_values'))) + self.use_default = False + else: + # initial tool state, no manual selections made, not reloaded + self.oldValues = {} + self.use_default = True + + + self.subClassId = unquote(self.params.get('sub_class_id', '')) + + self.prototype = getToolPrototype(self.toolId) + + self._monkeyPatchAttr('userName', self.params.get('userEmail')) + + self.subClasses = OrderedDict() + subClasses = self.prototype.getSubToolClasses() + if subClasses: + self.subToolSelectionTitle = self.prototype.getSubToolSelectionTitle() + self.subClasses[self.prototype.getToolSelectionName()] = self.prototype.__class__ + for subcls in subClasses: + toolSelectionName = subcls.getToolSelectionName() + if self.prototype.useSubToolPrefix(): +# toolSelectionName = subcls.__class__.__name__ + ': ' + toolSelectionName + toolModule = subcls.__module__.split('.')[2:] + if subcls.__name__ != toolModule[-1]: + toolSelectionName = '.'.join(toolModule) + ' [' + subcls.__name__ + ']: ' + toolSelectionName + else: + toolSelectionName = '.'.join(toolModule) + ': ' + toolSelectionName + self.subClasses[toolSelectionName] = subcls + + self.resetAll = False + if self.subClassId and self.subClassId in self.subClasses: + self.prototype = self.subClasses[self.subClassId]() + if not self.oldValues.has_key('sub_class_id') or self.oldValues['sub_class_id'] != self.subClassId: + self.oldValues['sub_class_id'] = self.subClassId + # do not reset boxes/ignore params if we are called with parameters in the url (e.g redirect or demo link) + if not self.use_default: + self.resetAll = True + + self.inputTypes = [] + self.inputValues = [] + self.displayValues = [] + self.inputInfo = [] + self.inputIds = [] + self.inputNames = [] + self._getInputBoxNames() + self.inputOrder = self._getIdxList(self.prototype.getInputBoxOrder()) + self.resetBoxes = self._getIdxList(self.prototype.getResetBoxes()) + + self.extra_output = [] + + self._init() + + self._initCache() + if trans: + self.action() + + self.inputGroup = self._getInputGroup(self.prototype.getInputBoxGroups(self.choices)) + + if hasattr(self.prototype, 'getExtraHistElements'): + extra_output = self.prototype.getExtraHistElements(self.choices) + if extra_output: + for e in extra_output: + if isinstance(e, HistElement): + self.extra_output.append((e.name, e.format, e.label, e.hidden)) + else: + self.extra_output.append(e) + + def _init(self): + if hasattr(super(GenericToolController, self), '_init'): + super(GenericToolController, self)._init() + + def _getInputGroup(self, inputBoxGroups): + startGroupInfo = defaultdict(list) + endIdxs = list() + if inputBoxGroups: + for group in inputBoxGroups: + idxBegin = self._getIdxForBoxId(group[1]) + idxEnd = self._getIdxForBoxId(group[2]) + startGroupInfo[idxBegin].append(group[0]) + endIdxs.append(idxEnd) + return startGroupInfo, endIdxs + + def _getInputBoxNames(self): + names = self.prototype.getInputBoxNames() + for i in range(len(names)): + name = names[i] + if isinstance(name, tuple): + id = name[1] + name = name[0] + else: + id = 'box' + str(1 + i) + self.inputIds.append(id) + self.inputNames.append(name) + + def _getIdxList(self, inputList): + idxList = [] + if inputList == None: + idxList = range(len(self.inputIds)) + else: + for i in inputList: + if isinstance(i, basestring): + try: + idx = self.inputIds.index(i) + except ValueError: + if i.startswith('box'): + idx = int(i[3:]) - 1 + else: + idx = i - 1 + if idx < len(self.inputIds): + idxList.append(idx) + else: + raise IndexError('List index out of range: %d >= %d' % (idx, len(self.inputIds))) + return idxList + + def _getIdxForBoxId(self, i): + if isinstance(i, basestring): + try: + idx = self.inputIds.index(i) + except ValueError: + if i.startswith('box'): + idx = int(i[3:]) - 1 + else: + idx = i - 1 + return idx + + def _getOptionsBox(self, i, val = None): + id = self.inputIds[i] + id = id[0].upper() + id[1:] + info = None + if i > 0: + ChoiceTuple = namedtuple('ChoiceTuple', self.inputIds[:(i+1)]) + prevChoices = ChoiceTuple._make(self.inputValues + [val]) + #self.choices = prevChoices + if id.startswith('Box'): + opts = getattr(self.prototype, 'getOptions' + id)(prevChoices) + try: + info = getattr(self.prototype, 'getInfoForOptions' + id)(prevChoices) + except: + pass + else: + opts = getattr(self.prototype, 'getOptionsBox' + id)(prevChoices) + try: + info = getattr(self.prototype, 'getInfoForOptionsBox' + id)(prevChoices) + except: + pass + else: + if id.startswith('Box'): + opts = getattr(self.prototype, 'getOptions' + id)() + try: + info = getattr(self.prototype, 'getInfoForOptions' + id)() + except: + pass + else: + opts = getattr(self.prototype, 'getOptionsBox' + id)() + try: + info = getattr(self.prototype, 'getInfoForOptionsBox' + id)() + except: + pass + return opts, info + + + def _initCache(self): + self.input_changed = False + try: + self.cachedParams = self.decodeCache(self.params.get('cached_params')) + except Exception as e: + #print 'cached_params', e + self.cachedParams = {} + + try: + self.cachedOptions = self.decodeCache(self.params.get('cached_options')) + except Exception as e: + #print 'cached_options', e + self.cachedOptions = {} + + try: + self.cachedExtra = self.decodeCache(self.params.get('cached_extra')) + except Exception as e: + #print 'cached_extra', e + self.cachedExtra = {} + + def encodeCache(self, data): + if not data: + return '' + return GALAXY_SECURITY_HELPER_OBJ.encode_guid(json.dumps(data)) + #return urlsafe_b64encode(compress(json.dumps(data))) + + def decodeCache(self, data): + if not data: + raise Exception('Nothing to decode') + return json.loads(GALAXY_SECURITY_HELPER_OBJ.decode_guid(str(data))) + #return json.loads(decompress(urlsafe_b64decode(str(data)))) + + def putCacheData(self, id, data): + self.cachedExtra[id] = pickle.dumps(data) + + def getCacheData(self, id): + return pickle.loads(b64decode(str(self.cachedExtra[id]))) + + def putCachedOption(self, id, data): + self.cachedOptions[id] = b64encode(pickle.dumps(data)) + + def getCachedOption(self, id): + return pickle.loads(str(self.cachedOptions[id])) + + def getOptionsBox(self, id, i, val): + #print id, '=', val, 'cache=', self.cachedParams[id] if id in self.cachedParams else 'NOT' + + if self.input_changed or id not in self.cachedParams: + opts, info = self._getOptionsBox(i, val) + self.input_changed = True + else: + try: + opts, info = self.getCachedOption(id) + #print 'from cache:',id + self.input_changed = (val != self.cachedParams[id]) + except Exception as e: + # print 'cache load failed for id "%s": %s' % (id, e) + opts, info = self._getOptionsBox(i, val) + self.input_changed = True + + #print repr(opts) + self.cachedParams[id] = val + self.putCachedOption(id, (opts, info)) + self.inputInfo.append(info) + return opts + + + def action(self): + self.options = [] + reset = self.resetAll + + for i in range(len(self.inputNames)): + name = self.inputNames[i] + id = self.inputIds[i] + if self.initChoicesDict: + val = self.initChoicesDict[id] + else: + val = self.params.get(id) + + display_only = False + opts = self.getOptionsBox(id, i, val) + + if reset and not self.initChoicesDict: + val = None + + if opts == None: + self.inputTypes += [None] + val = None + + elif isinstance(opts, dict) or opts == '__genomes__': + self.inputTypes += ['multi'] + if opts == '__genomes__': + try: + opts = self.cachedExtra[id] + except: + opts = self.getDictOfAllGenomes() + self.cachedExtra[id] = opts + if not self.initChoicesDict: + values = type(opts)() + for k,v in opts.items(): + #values[k] = bool(self.params.get(id + '|' + k, False if val else v)) + values[k] = bool(self.params.get(id + '|' + k , False) if val else v) + val = values + + elif isinstance(opts, basestring): + if opts == '__genome__': + self.inputTypes += ['__genome__'] + try: + genomeCache = self.getCacheData(id) + except Exception as e: + #raise e + print 'genome cache empty', e + genomeCache = self._getAllGenomes() + #print genomeCache + self.putCacheData(id, genomeCache) + + opts = self.getGenomeElement(id, genomeCache) + val = self.getGenome(id) + + elif opts == '__track__': + self.inputTypes += ['__track__'] + val = self.getInputValueForTrack(id, name) + + elif opts == '__password__': + self.inputTypes += ['__password__'] + if val == None: + val = '' + + else: + self.inputTypes += ['text'] + if val == None: + val = opts + opts = (val, 1, False) + + elif isinstance(opts, tuple): + if opts[0] == '__history__': + self.inputTypes += opts[:1] + opts = self.galaxy.optionsFromHistoryFn(exts = opts[1:] if len(opts)>1 else None, select = val) + if val == None and opts and len(opts[1]) > 0: + val = opts[1][0] + #opts = self.galaxy.getHistory(GalaxyInterface.getSupportedGalaxyFileFormats()) + elif opts[0] == '__toolhistory__': + self.inputTypes += opts[:1] + opts = self.galaxy.optionsFromHistoryFn(tools = opts[1:] if len(opts)>1 else None, select = val) + if val == None and opts and len(opts[1]) > 0: + val = opts[1][0] + elif opts[0] == '__multihistory__': + self.inputTypes += opts[:1] + opts = self.galaxy.itemsFromHistoryFn(opts[1:] if len(opts)>1 else None) + if not self.initChoicesDict: + values = OrderedDict() + for k,v in opts.items(): + itemval = self.params.get(id + '|' + k, None) + #if itemval: + values[unicode(k)] = itemval + + val = values + + elif opts[0] == '__track__': + self.inputTypes += ['__track__'] + val = self.getInputValueForTrack(id, name) + + elif opts[0] == '__hidden__': + self.inputTypes += opts[:1] + if opts[1] != None: + val = opts[1] + #elif val: + # val = unquote(val) + elif len(opts) in [2, 3] and (isinstance(opts[0], basestring)): + if len(opts) == 2: + opts = opts + (False,) + if isinstance(opts[1], int): + if isinstance(opts[2], bool): + if opts[2]: + self.inputTypes += ['text_readonly'] + val = opts[0] + #display_only = True + else: + self.inputTypes += ['text'] + if val == None: + val = opts[0] + else: + self.inputTypes += ['rawStr'] + val = opts[1] + display_only = True + else: + self.inputTypes += [None] + val = None + + elif isinstance(opts, list): + if len(opts) > 0 and isinstance(opts[0], list): + self.inputTypes += ['table'] + core = HtmlCore() + core.tableHeader(opts[0], sortable=True) + if len(opts) > 1: + for r in range(1, len(opts)): + core.tableLine(opts[r]) + core.tableFooter() + val = unicode(core) + display_only = True + + else: + self.inputTypes += ['select'] + if len(opts) > 0 and (val == None or val not in opts): + val = opts[0] + + elif isinstance(opts, bool): + self.inputTypes += ['checkbox'] + val = True if val == "True" else opts if self.use_default else False + + #elif isinstance(opts, list) and len(opts) == 0: + # self.inputTypes += ['text'] + # if val == None: + # val = '' + + self.displayValues.append(val if isinstance(val, basestring) else repr(val)) + self.inputValues.append(None if display_only else val) + self.options.append(opts) + + oldval = self.oldValues[id] if id in self.oldValues else None + if i in self.resetBoxes: + self.oldValues[id] = val + if oldval == None or val != oldval: + reset = True + + ChoiceTuple = namedtuple('ChoiceTuple', self.inputIds) + self.choices = ChoiceTuple._make(self.inputValues) + self.validate() + + def _action(self): + pass + + + def decodeChoice(self, opts, id, choice): + if opts == '__genome__': + id = 'dbkey' + choice = str(self.params[id]) if self.params.has_key(id) else '' + + # if isinstance(opts, tuple): + # if opts[0] == '__hidden__': + # choice = unquote(choice) + + if opts == '__genomes__' or (isinstance(opts, tuple) and opts[0] == '__multihistory__'): + values = {} + for key in self.params.keys(): + if key.startswith(id + '|'): + values[key.split('|')[1]] = self.params[key] + choice = OrderedDict(sorted(values.items(), \ + key=lambda t: int(t[0]) if opts[0] == '__multihistory__' else t[0])) + + if isinstance(opts, dict): + values = type(opts)() + for k, v in opts.items(): + if self.params.has_key(id + '|' + k): + values[k] = self.params[id + '|' + k] + else: + values[k] = False + choice = values + + if isinstance(opts, bool): + choice = True if choice == "True" else False + + return choice + + @staticmethod + def _getStdOutToHistoryDatatypes(): + return ['html', 'customhtml'] + + def execute(self): + outputFormat = self.params['datatype'] if self.params.has_key('datatype') else 'html' + if outputFormat in self._getStdOutToHistoryDatatypes(): + self.stdoutToHistory() + + for i in range(len(self.inputIds)): + id = self.inputIds[i] + choice = self.params[id] if self.params.has_key(id) else '' + + opts = self.getOptionsBox(id, i, choice) + + choice = self.decodeChoice(opts, id, choice) + +# if opts == '__genome__': +# id = 'dbkey' +# choice = str(self.params[id]) if self.params.has_key(id) else '' +# +# # if isinstance(opts, tuple): +# # if opts[0] == '__hidden__': +# # choice = unquote(choice) +# +# if opts == '__track__' or (isinstance(opts, tuple) and opts[0] == '__track__'): +# #tn = choice.split(':') +# #GalaxyInterface.cleanUpTrackName(tn) +# #choice = ':'.join(tn) +# choice = self.decodeChoiceForTrack(choice) +# +# if opts == '__genomes__' or (isinstance(opts, tuple) and opts[0] == '__multihistory__'): +# values = {} +# for key in self.params.keys(): +# if key.startswith(id + '|'): +# values[key.split('|')[1]] = self.params[key] +# choice = OrderedDict(sorted(values.items(), \ +# key=lambda t: int(t[0]) if opts[0] == '__multihistory__' else t[0])) +# +# if isinstance(opts, dict): +# values = type(opts)() +# for k,v in opts.items(): +# if self.params.has_key(id + '|' + k): +# values[k] = self.params[id + '|' + k] +# else: +# values[k] = False +# choice = values +# +# if isinstance(opts, bool): +# choice = True if choice == "True" else False + + self.inputValues.append(choice) + + # if self.params.has_key('Track_state'): + # self.inputValues.append(unquote(self.params['Track_state'])) + + ChoiceTuple = namedtuple('ChoiceTuple', self.inputIds) + choices = ChoiceTuple._make(self.inputValues) + + #batchargs = '|'.join([';'.join(c.itervalues()) if not isinstance(c, basestring) else c for c in choices]) + #batchargs = '|'.join([repr(c.items()) if not isinstance(c, basestring) else c for c in choices]) + + #print choices + if outputFormat == 'html': + print ''' + + + + + + +

Toggle debug

+
+            ''' % {'prefix': URL_PREFIX}
+        #    print '
Corresponding batch run line:\n', '$Tool[%s](%s)
' % (self.toolId, batchargs) + + + self.extraGalaxyFn = {} + +# if hasattr(self.prototype, 'getExtraHistElements'): +# for output in self.prototype.getExtraHistElements(choices): +# if isinstance(output, HistElement): +# self.extraGalaxyFn[output.name] = self.params[output.name] +# else: +# self.extraGalaxyFn[output[0]] = self.params[output[0]] + + if hasattr(self.prototype, 'getExtraHistElements') and self.params.has_key('extra_output'): + extra_output = json.loads(unquote(self.params['extra_output'])) + if isinstance(extra_output, list): + for output in extra_output: + if isinstance(output, HistElement): + self.extraGalaxyFn[unicode(output.name)] = self.params[output.name] + else: + self.extraGalaxyFn[unicode(output[0])] = self.params[output[0]] + + + username = self.params['userEmail'] if 'userEmail' in self.params else '' + self._executeTool(getClassName(self.prototype), choices, galaxyFn=self.jobFile, username=username) + + if outputFormat == 'html': + print ''' +
+ + + + ''' + + def _executeTool(self, toolClassName, choices, galaxyFn, username): + if hasattr(super(GenericToolController, self), '_executeTool'): + super(GenericToolController, self)._executeTool( + toolClassName, choices, galaxyFn, username) + + self._monkeyPatchAttr('extraGalaxyFn', self.extraGalaxyFn) + self._monkeyPatchAttr('runParams', self.json_params) + self.prototype.execute(choices, galaxyFn=galaxyFn, username=username) + + def _monkeyPatchAttr(self, name, value): + if type(self.prototype).__name__ == 'type': + setattr(self.prototype, name, value) + else: + setattr(self.prototype.__class__, name, value) + + def executeNoHistory(self): + html = self.prototype.execute(self.choices, None, self.galaxy.getUserName()) + if not html: + html = 'Finished executing tool.' + return html + + def isPublic(self): + try: + return self.prototype.isPublic() + except: + return False + + def isDebugging(self): + try: + return self.prototype.isDebugMode() + except: + return False + + def getIllustrationImage(self): + image = None + id = self.prototype.getToolIllustration() + if id: + image = self.STATIC_IMAGE_CLS(id) + return image + + def getDemoURL(self): + try: + demo = self.prototype.getDemoSelections() + url = '?mako=generictool&tool_id=' + self.toolId + for i, id in enumerate(self.inputIds): + if self.inputTypes[i] == '__genome__': + id = 'dbkey' + #else: + # id = self.inputIds[i] + try: + val = getattr(demo, id) + except: + val = demo[i] + url += '&' + id + '=' + val + except Exception, e: + from gold.application.LogSetup import logException + logException(e) + url = None + return url + + def hasDemoURL(self): + try: + demo = self.prototype.getDemoSelections() + if len(demo) > 0: + return True + except: + pass + return False + + def getFullExampleURL(self): + try: + url = self.prototype.getFullExampleURL() + except Exception, e: + from gold.application.LogSetup import logException + logException(e) + url = None + return url + + def hasFullExampleURL(self): + try: + url = self.prototype.getFullExampleURL() + if url is not None: + return True + except Exception, e: + from gold.application.LogSetup import logException + logException(e) + pass + return False + + def isRedirectTool(self): + try: + return self.prototype.isRedirectTool(self.choices) + except TypeError: + return self.prototype.isRedirectTool() + + def doRedirect(self): + return self.isRedirectTool() and self.getRedirectURL() and self.params.has_key('start') + + def getRedirectURL(self): + return self.prototype.getRedirectURL(self.choices) + + def validate(self): + #ChoiceTuple = namedtuple('ChoiceTuple', self.inputIds) + #self.choices = ChoiceTuple._make(self.inputValues) + self.errorMessage = self.prototype.validateAndReturnErrors(self.choices) + + def isValid(self): + return True if self.errorMessage is None else False + + def hasErrorMessage(self): + return False if self.errorMessage in [None, ''] else True + + #jsonMethods = ('ajaxValidate') + #def ajaxValidate(self): + # return self.prototype.validateAndReturnErrors(self.inputValues) + + + def getInputValueForTrack(self, id, name): + return None + + +def getController(transaction = None, job = None): + #from gold.util.Profiler import Profiler + #prof = Profiler() + #prof.start() + control = GenericToolController(transaction, job) + #prof.stop() + #prof.printStats() + return control From 0f0eefc47cf92565fa1b61ce1caa54ff708164df Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Sun, 14 Jan 2018 13:28:02 +0100 Subject: [PATCH 087/123] Added syncthins ignores --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 70af659fff89..a3496f23a7b4 100644 --- a/.gitignore +++ b/.gitignore @@ -129,3 +129,6 @@ doc/build .idea .stversions .stfolder +.stglobalignore +.stignore +.syncthing* From ed98c48151f1e7b374aba20aa82b2989b9c46cf8 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Sun, 14 Jan 2018 20:25:50 +0100 Subject: [PATCH 088/123] Rebuild of client using node.js v9.4.0 and npm v5.6.0 --- client/package-lock.json | 4699 +++++++++++++++++ client/package.json | 1 + .../maps/mvc/dataset/dataset-li-edit.js.map | 2 +- static/scripts/mvc/dataset/dataset-li-edit.js | 2 +- 4 files changed, 4702 insertions(+), 2 deletions(-) create mode 100644 client/package-lock.json diff --git a/client/package-lock.json b/client/package-lock.json new file mode 100644 index 000000000000..4ce3d2daa90f --- /dev/null +++ b/client/package-lock.json @@ -0,0 +1,4699 @@ +{ + "name": "galaxy-client", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "accepts": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", + "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", + "requires": { + "mime-types": "2.1.17", + "negotiator": "0.6.1" + } + }, + "acorn": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.3.0.tgz", + "integrity": "sha512-Yej+zOJ1Dm/IMZzzj78OntP/r3zHEaKcyNoU2lAaxPtrseM6rF0xwqoz5Q5ysAiED9hTjI2hgtvLXitlCN1/Ug==" + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "requires": { + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "amdi18n-loader": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/amdi18n-loader/-/amdi18n-loader-0.2.0.tgz", + "integrity": "sha1-YQs1RbGzndSV6Kdt1Nds/t8Uod0=" + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "requires": { + "micromatch": "2.3.11", + "normalize-path": "2.1.1" + } + }, + "argparse": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "integrity": "sha1-z9AeD7uj1srtBJ+9dY1A9lGW9Xw=", + "requires": { + "underscore": "1.7.0", + "underscore.string": "2.4.0" + }, + "dependencies": { + "underscore.string": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz", + "integrity": "sha1-jN2PusTi0uoefi6Al8QvRCKA+Fs=" + } + } + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "requires": { + "arr-flatten": "1.1.0" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "optional": true + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "requires": { + "util": "0.10.3" + } + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" + }, + "async": { + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", + "integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=" + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base64-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", + "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==" + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==" + }, + "bin-pack": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bin-pack/-/bin-pack-1.0.2.tgz", + "integrity": "sha1-wqAU7b8L7XCjKSBi7UZXe5YSBnk=" + }, + "binary-extensions": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", + "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=" + }, + "body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "1.0.4", + "debug": "2.6.9", + "depd": "1.1.2", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "1.6.15" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + } + } + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "requires": { + "hoek": "2.16.3" + } + }, + "bower": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/bower/-/bower-1.8.2.tgz", + "integrity": "sha1-rfU1KcjUrwLvJPuNU0HBQZ0z4vc=" + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "requires": { + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" + } + }, + "browserify-aes": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-0.4.0.tgz", + "integrity": "sha1-BnFJtmjfMcS1hTPgLQHoBthgjiw=", + "requires": { + "inherits": "2.0.3" + } + }, + "browserify-zlib": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", + "requires": { + "pako": "0.2.9" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "requires": { + "base64-js": "1.2.1", + "ieee754": "1.1.8", + "isarray": "1.0.0" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "requires": { + "camelcase": "2.1.1", + "map-obj": "1.0.1" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "requires": { + "align-text": "0.1.4", + "lazy-cache": "1.0.4" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "chokidar": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "requires": { + "anymatch": "1.3.2", + "async-each": "1.0.1", + "fsevents": "1.1.3", + "glob-parent": "2.0.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "2.0.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0" + } + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "requires": { + "center-align": "0.1.3", + "right-align": "0.1.3", + "wordwrap": "0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + } + } + }, + "clone": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz", + "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "coffee-script": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz", + "integrity": "sha1-FQ1rTLUiiUNp7+1qIQHCC8f0pPQ=" + }, + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=" + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "requires": { + "delayed-stream": "1.0.0" + } + }, + "compressible": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.12.tgz", + "integrity": "sha1-xZpcmdt2dn6YdlAOJx72OzSTvWY=", + "requires": { + "mime-db": "1.30.0" + } + }, + "compression": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.1.tgz", + "integrity": "sha1-7/JgPvwuIs+G810uuTWJ+YdTc9s=", + "requires": { + "accepts": "1.3.4", + "bytes": "3.0.0", + "compressible": "2.0.12", + "debug": "2.6.9", + "on-headers": "1.0.1", + "safe-buffer": "5.1.1", + "vary": "1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "typedarray": "0.0.6" + } + }, + "connect-history-api-fallback": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", + "integrity": "sha1-sGhzk0vF40T+9hGhlqb6rgruAVo=" + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "requires": { + "date-now": "0.1.4" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "contentstream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/contentstream/-/contentstream-1.0.0.tgz", + "integrity": "sha1-C9z6RtowRkqGzo+n7OVlQQ3G+aU=", + "requires": { + "readable-stream": "1.0.34" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "requires": { + "boom": "2.10.1" + } + }, + "crypto-browserify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.3.0.tgz", + "integrity": "sha1-ufx1u0oO1h3PHNXa6W6zDJw+UGw=", + "requires": { + "browserify-aes": "0.4.0", + "pbkdf2-compat": "2.0.1", + "ripemd160": "0.2.0", + "sha.js": "2.2.6" + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "requires": { + "array-find-index": "1.0.2" + } + }, + "cwise": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/cwise/-/cwise-1.0.10.tgz", + "integrity": "sha1-JO7mBy69/WuMb12tsXCQtkmxK+8=", + "requires": { + "cwise-compiler": "1.1.3", + "cwise-parser": "1.0.3", + "static-module": "1.5.0", + "uglify-js": "2.8.29" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "requires": { + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" + } + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "requires": { + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", + "window-size": "0.1.0" + } + } + } + }, + "cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha1-9NZnQQ6FDToxOn0tt7HlBbsDTMU=", + "requires": { + "uniq": "1.0.1" + } + }, + "cwise-parser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cwise-parser/-/cwise-parser-1.0.3.tgz", + "integrity": "sha1-jkk8F9VPl8sDCp6YVLyGyd+zVP4=", + "requires": { + "esprima": "1.0.4", + "uniq": "1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "data-uri-to-buffer": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-0.0.3.tgz", + "integrity": "sha1-GK6XmmoMqZSwYlhTkW0mYruuCxo=" + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" + }, + "dateformat": { + "version": "1.0.2-1.2.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz", + "integrity": "sha1-sCIMAt6YYXQztyhRz0fePfLNvuk=" + }, + "debug": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz", + "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=" + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "domain-browser": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", + "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=" + }, + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "requires": { + "readable-stream": "1.1.14" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + }, + "encodeurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" + }, + "enhanced-resolve": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz", + "integrity": "sha1-TW5omzcl+GCQknzMhs2fFjW4ni4=", + "requires": { + "graceful-fs": "4.1.11", + "memory-fs": "0.2.0", + "tapable": "0.1.10" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "memory-fs": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", + "integrity": "sha1-8rslNovBIeORwlIN6Slpyu4KApA=" + } + } + }, + "errno": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.6.tgz", + "integrity": "sha512-IsORQDpaaSwcDP4ZZnHxgE85werpo34VYn1Ud3mq+eUsF593faR8oCZNXrROVkpFu2TsbrNhHin0aUrTsQ9vNw==", + "requires": { + "prr": "1.0.1" + } + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "requires": { + "is-arrayish": "0.2.1" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.3.3.tgz", + "integrity": "sha1-8CQBb1qI4Eb9EgBQVek5gC5sXyM=", + "requires": { + "esprima": "1.1.1", + "estraverse": "1.5.1", + "esutils": "1.0.0", + "source-map": "0.1.43" + }, + "dependencies": { + "esprima": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz", + "integrity": "sha1-W28VR/TRAuZw4UDFCb5ncdautUk=" + } + } + }, + "esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", + "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=" + }, + "estraverse": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz", + "integrity": "sha1-hno+jlip+EYYr7bC3bzZFrfLr3E=" + }, + "esutils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz", + "integrity": "sha1-gVHTWOIMisx/t0XnRywAJf5JZXA=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=" + }, + "eventemitter3": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", + "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=" + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, + "eventsource": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", + "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", + "requires": { + "original": "1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "requires": { + "is-posix-bracket": "0.1.1" + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "requires": { + "fill-range": "2.2.3" + } + }, + "express": { + "version": "4.16.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", + "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", + "requires": { + "accepts": "1.3.4", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "content-disposition": "0.5.2", + "content-type": "1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "1.1.2", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.8.1", + "finalhandler": "1.1.0", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "2.0.2", + "qs": "6.5.1", + "range-parser": "1.2.0", + "safe-buffer": "5.1.1", + "send": "0.16.1", + "serve-static": "1.13.1", + "setprototypeof": "1.1.0", + "statuses": "1.3.1", + "type-is": "1.6.15", + "utils-merge": "1.0.1", + "vary": "1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + } + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "requires": { + "is-extglob": "1.0.0" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "falafel": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.1.0.tgz", + "integrity": "sha1-lrsXdh2rqU9G0AFzizzt86Z/4Gw=", + "requires": { + "acorn": "5.3.0", + "foreach": "2.0.5", + "isarray": "0.0.1", + "object-keys": "1.0.11" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + } + } + }, + "fastparse": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", + "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=" + }, + "faye-websocket": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz", + "integrity": "sha1-wUxbO/FNdBf/v9mQwKdJXNnzN7w=" + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "requires": { + "escape-string-regexp": "1.0.5", + "object-assign": "4.1.1" + } + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=" + }, + "fill-range": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "requires": { + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "1.1.7", + "repeat-element": "1.1.2", + "repeat-string": "1.6.1" + } + }, + "finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "requires": { + "debug": "2.6.9", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.3.1", + "unpipe": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "findup-sync": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", + "integrity": "sha1-fz56l7gjksZTvwZYm9hRkOk8NoM=", + "requires": { + "glob": "3.2.11", + "lodash": "2.4.2" + }, + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "requires": { + "inherits": "2.0.3", + "minimatch": "0.3.0" + } + }, + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "requires": { + "for-in": "1.0.2" + } + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fsevents": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", + "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", + "optional": true, + "requires": { + "nan": "2.8.0", + "node-pre-gyp": "0.6.39" + }, + "dependencies": { + "abbrev": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "ajv": { + "version": "4.11.8", + "bundled": true, + "optional": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "aproba": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.2.9" + } + }, + "asn1": { + "version": "0.2.3", + "bundled": true, + "optional": true + }, + "assert-plus": { + "version": "0.2.0", + "bundled": true, + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "bundled": true, + "optional": true + }, + "aws4": { + "version": "1.6.0", + "bundled": true, + "optional": true + }, + "balanced-match": { + "version": "0.4.2", + "bundled": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "bundled": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "requires": { + "inherits": "2.0.3" + } + }, + "boom": { + "version": "2.10.1", + "bundled": true, + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.7", + "bundled": true, + "requires": { + "balanced-match": "0.4.2", + "concat-map": "0.0.1" + } + }, + "buffer-shims": { + "version": "1.0.0", + "bundled": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true, + "optional": true + }, + "co": { + "version": "4.6.0", + "bundled": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "combined-stream": { + "version": "1.0.5", + "bundled": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true, + "requires": { + "boom": "2.10.1" + } + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "debug": { + "version": "2.6.8", + "bundled": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "bundled": true, + "optional": true + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "extend": { + "version": "3.0.1", + "bundled": true, + "optional": true + }, + "extsprintf": { + "version": "1.0.2", + "bundled": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "optional": true + }, + "form-data": { + "version": "2.1.4", + "bundled": true, + "optional": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.15" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.1" + } + }, + "fstream-ignore": { + "version": "1.0.5", + "bundled": true, + "optional": true, + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "optional": true, + "requires": { + "aproba": "1.1.1", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true + }, + "har-schema": { + "version": "1.0.5", + "bundled": true, + "optional": true + }, + "har-validator": { + "version": "4.2.1", + "bundled": true, + "optional": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "hawk": { + "version": "3.1.3", + "bundled": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "bundled": true + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.0", + "sshpk": "1.13.0" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true + }, + "ini": { + "version": "1.3.4", + "bundled": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "optional": true + }, + "jodid25519": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "optional": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "bundled": true, + "optional": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "optional": true + }, + "jsonify": { + "version": "0.0.0", + "bundled": true, + "optional": true + }, + "jsprim": { + "version": "1.4.0", + "bundled": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.0.2", + "json-schema": "0.2.3", + "verror": "1.3.6" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "mime-db": { + "version": "1.27.0", + "bundled": true + }, + "mime-types": { + "version": "2.1.15", + "bundled": true, + "requires": { + "mime-db": "1.27.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "node-pre-gyp": { + "version": "0.6.39", + "bundled": true, + "optional": true, + "requires": { + "detect-libc": "1.0.2", + "hawk": "3.1.3", + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.0", + "rc": "1.2.1", + "request": "2.81.0", + "rimraf": "2.6.1", + "semver": "5.3.0", + "tar": "2.2.1", + "tar-pack": "3.4.0" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "optional": true, + "requires": { + "abbrev": "1.1.0", + "osenv": "0.1.4" + } + }, + "npmlog": { + "version": "4.1.0", + "bundled": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "osenv": { + "version": "0.1.4", + "bundled": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "performance-now": { + "version": "0.2.0", + "bundled": true, + "optional": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true + }, + "punycode": { + "version": "1.4.1", + "bundled": true, + "optional": true + }, + "qs": { + "version": "6.4.0", + "bundled": true, + "optional": true + }, + "rc": { + "version": "1.2.1", + "bundled": true, + "optional": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.2.9", + "bundled": true, + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "1.0.1", + "util-deprecate": "1.0.2" + } + }, + "request": { + "version": "2.81.0", + "bundled": true, + "optional": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.15", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.0.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.6.0", + "uuid": "3.0.1" + } + }, + "rimraf": { + "version": "2.6.1", + "bundled": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.0.1", + "bundled": true + }, + "semver": { + "version": "5.3.0", + "bundled": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "optional": true + }, + "sntp": { + "version": "1.0.9", + "bundled": true, + "requires": { + "hoek": "2.16.3" + } + }, + "sshpk": { + "version": "1.13.0", + "bundled": true, + "optional": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jodid25519": "1.0.2", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.0.1", + "bundled": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, + "stringstream": { + "version": "0.0.5", + "bundled": true, + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "tar-pack": { + "version": "3.4.0", + "bundled": true, + "optional": true, + "requires": { + "debug": "2.6.8", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.2.9", + "rimraf": "2.6.1", + "tar": "2.2.1", + "uid-number": "0.0.6" + } + }, + "tough-cookie": { + "version": "2.3.2", + "bundled": true, + "optional": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "uuid": { + "version": "3.0.1", + "bundled": true, + "optional": true + }, + "verror": { + "version": "1.3.6", + "bundled": true, + "optional": true, + "requires": { + "extsprintf": "1.0.2" + } + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "gaze": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz", + "integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", + "requires": { + "globule": "0.1.0" + } + }, + "get-pixels": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/get-pixels/-/get-pixels-3.1.1.tgz", + "integrity": "sha1-xZQ3fRF1d618Ar2bRZlBYddu03g=", + "requires": { + "data-uri-to-buffer": "0.0.3", + "jpeg-js": "0.1.2", + "mime-types": "2.1.17", + "ndarray": "1.0.18", + "ndarray-pack": "1.2.1", + "node-bitmap": "0.0.1", + "omggif": "1.0.9", + "parse-data-uri": "0.2.0", + "pngparse": "1.1.4", + "request": "2.81.0", + "through": "2.3.8" + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" + }, + "getobject": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz", + "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "gif-encoder": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/gif-encoder/-/gif-encoder-0.4.3.tgz", + "integrity": "sha1-iitP6MqJWkjjoLbLs0CgpqNXGJk=", + "requires": { + "readable-stream": "1.1.14" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", + "requires": { + "graceful-fs": "1.2.3", + "inherits": "1.0.2", + "minimatch": "0.2.14" + }, + "dependencies": { + "inherits": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", + "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=" + } + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "requires": { + "glob-parent": "2.0.0", + "is-glob": "2.0.1" + } + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "requires": { + "is-glob": "2.0.1" + } + }, + "globule": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", + "requires": { + "glob": "3.1.21", + "lodash": "1.0.2", + "minimatch": "0.2.14" + }, + "dependencies": { + "lodash": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", + "integrity": "sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE=" + } + } + }, + "graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", + "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=" + }, + "grunt": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", + "integrity": "sha1-VpN81RlDJK3/bSB2MYMqnWuk5/A=", + "requires": { + "async": "0.1.22", + "coffee-script": "1.3.3", + "colors": "0.6.2", + "dateformat": "1.0.2-1.2.3", + "eventemitter2": "0.4.14", + "exit": "0.1.2", + "findup-sync": "0.1.3", + "getobject": "0.1.0", + "glob": "3.1.21", + "grunt-legacy-log": "0.1.3", + "grunt-legacy-util": "0.2.0", + "hooker": "0.2.3", + "iconv-lite": "0.2.11", + "js-yaml": "2.0.5", + "lodash": "0.9.2", + "minimatch": "0.2.14", + "nopt": "1.0.10", + "rimraf": "2.2.8", + "underscore.string": "2.2.1", + "which": "1.0.9" + } + }, + "grunt-bower-install-simple": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/grunt-bower-install-simple/-/grunt-bower-install-simple-1.2.4.tgz", + "integrity": "sha1-7qib6AQh7vTZJigLBXOzMvAPP4U=", + "requires": { + "bower": "1.8.2", + "chalk": "1.1.3" + } + }, + "grunt-check-modules": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/grunt-check-modules/-/grunt-check-modules-1.1.0.tgz", + "integrity": "sha1-fBZB28ZlSGdqbVl5Ga35C3s11kQ=" + }, + "grunt-cli": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-0.1.13.tgz", + "integrity": "sha1-6evEBHYx9QEtkidww5N4EzytEPQ=", + "requires": { + "findup-sync": "0.1.3", + "nopt": "1.0.10", + "resolve": "0.3.1" + } + }, + "grunt-contrib-clean": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.6.0.tgz", + "integrity": "sha1-9TLbpLghJnTHwBPhRr2mY4uQSPY=", + "requires": { + "rimraf": "2.2.8" + } + }, + "grunt-contrib-copy": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-copy/-/grunt-contrib-copy-0.5.0.tgz", + "integrity": "sha1-QQB1rEWlhWuhkbHMclclRQ1KAhU=" + }, + "grunt-contrib-handlebars": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/grunt-contrib-handlebars/-/grunt-contrib-handlebars-0.10.2.tgz", + "integrity": "sha1-q3jE9ECUciytlui0DOscfNWDMMk=", + "requires": { + "chalk": "1.1.3", + "handlebars": "3.0.3", + "nsdeclare": "0.1.0" + }, + "dependencies": { + "handlebars": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-3.0.3.tgz", + "integrity": "sha1-DgllGi8Ps8lJFgWDcQ1VH5Lm0q0=", + "requires": { + "optimist": "0.6.1", + "source-map": "0.1.43", + "uglify-js": "2.3.6" + } + } + } + }, + "grunt-contrib-less": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/grunt-contrib-less/-/grunt-contrib-less-1.4.1.tgz", + "integrity": "sha1-O73sC3XRLOqlXWKUNiXAsIYc328=", + "requires": { + "async": "2.6.0", + "chalk": "1.1.3", + "less": "2.7.3", + "lodash": "4.17.4" + }, + "dependencies": { + "async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", + "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", + "requires": { + "lodash": "4.17.4" + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + } + } + }, + "grunt-contrib-uglify": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-0.8.1.tgz", + "integrity": "sha1-53Cv1Hsc0d6Nk/wZtvANmyrX6rc=", + "requires": { + "chalk": "1.1.3", + "lodash": "3.10.1", + "maxmin": "1.1.0", + "uglify-js": "2.4.17", + "uri-path": "0.0.2" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + }, + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + }, + "source-map": { + "version": "0.1.34", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz", + "integrity": "sha1-p8/omux7FoLDsZjQrPtH19CQVms=", + "requires": { + "amdefine": "1.0.1" + } + }, + "uglify-js": { + "version": "2.4.17", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.17.tgz", + "integrity": "sha1-AbmzjIKMtizPwlvt0d+r2QfEMaE=", + "requires": { + "async": "0.2.10", + "source-map": "0.1.34", + "uglify-to-browserify": "1.0.2", + "yargs": "1.3.3" + } + } + } + }, + "grunt-contrib-watch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.6.1.tgz", + "integrity": "sha1-ZP3LolpjX1tNobbOb5DaCutuPxU=", + "requires": { + "async": "0.2.10", + "gaze": "0.5.2", + "lodash": "2.4.2", + "tiny-lr-fork": "0.0.5" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + }, + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" + } + } + }, + "grunt-exec": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/grunt-exec/-/grunt-exec-0.4.7.tgz", + "integrity": "sha1-QAUf+k6wyWV+BTuV6I1ENSocLCU=" + }, + "grunt-legacy-log": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz", + "integrity": "sha1-7ClCboAwIa9ZAp+H0vnNczWgVTE=", + "requires": { + "colors": "0.6.2", + "grunt-legacy-log-utils": "0.1.1", + "hooker": "0.2.3", + "lodash": "2.4.2", + "underscore.string": "2.3.3" + }, + "dependencies": { + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" + }, + "underscore.string": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", + "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=" + } + } + }, + "grunt-legacy-log-utils": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz", + "integrity": "sha1-wHBrndkGThFvNvI/5OawSGcsD34=", + "requires": { + "colors": "0.6.2", + "lodash": "2.4.2", + "underscore.string": "2.3.3" + }, + "dependencies": { + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" + }, + "underscore.string": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", + "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=" + } + } + }, + "grunt-legacy-util": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz", + "integrity": "sha1-kzJIhNv343qf98Am3/RR2UqeVUs=", + "requires": { + "async": "0.1.22", + "exit": "0.1.2", + "getobject": "0.1.0", + "hooker": "0.2.3", + "lodash": "0.9.2", + "underscore.string": "2.2.1", + "which": "1.0.9" + } + }, + "grunt-spritesmith": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/grunt-spritesmith/-/grunt-spritesmith-4.7.1.tgz", + "integrity": "sha1-xM/u6cuysZf2suI2nNIcD4R3yOM=", + "requires": { + "async": "0.9.2", + "spritesheet-templates": "9.6.1", + "spritesmith": "1.3.2", + "underscore": "1.4.4", + "url2": "1.0.0" + }, + "dependencies": { + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=" + } + } + }, + "grunt-webpack": { + "version": "1.0.18", + "resolved": "https://registry.npmjs.org/grunt-webpack/-/grunt-webpack-1.0.18.tgz", + "integrity": "sha1-/ybEP/NbrmzKcHqTxLzdlQo+y7c=", + "requires": { + "lodash": "4.17.4" + }, + "dependencies": { + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + } + } + }, + "gzip-size": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-1.0.0.tgz", + "integrity": "sha1-Zs+LEBBHInuVus5uodoMF37Vwi8=", + "requires": { + "browserify-zlib": "0.1.4", + "concat-stream": "1.6.0" + } + }, + "handlebars": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", + "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", + "requires": { + "async": "1.5.2", + "optimist": "0.6.1", + "source-map": "0.4.4", + "uglify-js": "2.8.29" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "optional": true + }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": "1.0.1" + } + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "optional": true, + "requires": { + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "optional": true + } + } + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "optional": true, + "requires": { + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", + "window-size": "0.1.0" + } + } + } + }, + "handlebars-layouts": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/handlebars-layouts/-/handlebars-layouts-1.1.0.tgz", + "integrity": "sha1-JhK+Wu2PICaXN8cxHaFcnC11+7w=" + }, + "handlebars-loader": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/handlebars-loader/-/handlebars-loader-1.6.0.tgz", + "integrity": "sha512-FsM1ALql8+RNJuTgD1qPrVT8GpB209hPUKml/uIoR6aJIoeoLB+l/jt8xBnpfzN74aaQ9kMXaZajhIE6PNHusg==", + "requires": { + "async": "0.2.10", + "fastparse": "1.1.1", + "loader-utils": "1.0.4", + "object-assign": "4.1.1" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + } + } + }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "requires": { + "function-bind": "1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + }, + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=" + }, + "hosted-git-info": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", + "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==" + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.3.1" + }, + "dependencies": { + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + } + } + }, + "http-parser-js": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.9.tgz", + "integrity": "sha1-6hoE+2St/wJC6ZdPKX3Uw8rSceE=" + }, + "http-proxy": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", + "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", + "requires": { + "eventemitter3": "1.2.0", + "requires-port": "1.0.0" + } + }, + "http-proxy-middleware": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz", + "integrity": "sha1-ZC6ISIUdZvCdTxJJEoRtuutBuDM=", + "requires": { + "http-proxy": "1.16.2", + "is-glob": "3.1.0", + "lodash": "4.17.4", + "micromatch": "2.3.11" + }, + "dependencies": { + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "2.1.1" + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + } + } + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "https-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", + "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=" + }, + "i18n-webpack-plugin": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/i18n-webpack-plugin/-/i18n-webpack-plugin-0.2.7.tgz", + "integrity": "sha1-pDk31R1pB53/ZCfs2wP2mMDtuvg=" + }, + "iconv-lite": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", + "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=" + }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "optional": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "requires": { + "repeating": "2.0.1" + } + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "interpret": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-0.6.6.tgz", + "integrity": "sha1-/s16GOfOXKar+5U+H4YhOknxYls=" + }, + "iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha1-ge9X/l0FgUzVjCSDYyqZwwoOgIc=" + }, + "ipaddr.js": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", + "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "requires": { + "binary-extensions": "1.11.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "requires": { + "builtin-modules": "1.1.1" + } + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "requires": { + "is-primitive": "2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "requires": { + "is-extglob": "1.0.0" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "requires": { + "kind-of": "3.2.2" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=" + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jpeg-js": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.1.2.tgz", + "integrity": "sha1-E1uZLAV1yYXPoPSUoyJ+0jhYPs4=" + }, + "js-yaml": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz", + "integrity": "sha1-olrmUJmZ6X3yeMZxnaEb0Gh3Q6g=", + "requires": { + "argparse": "0.1.16", + "esprima": "1.0.4" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-content-demux": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/json-content-demux/-/json-content-demux-0.1.3.tgz", + "integrity": "sha1-XBJ3v387dRKoa3Mt3UGzLU38scw=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=" + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.6" + } + }, + "layout": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/layout/-/layout-2.2.0.tgz", + "integrity": "sha1-MeRL/BjdEBmz/7II5AKku/4uavQ=", + "requires": { + "bin-pack": "1.0.2" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" + }, + "less": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/less/-/less-2.7.3.tgz", + "integrity": "sha512-KPdIJKWcEAb02TuJtaLrhue0krtRLoRoo7x6BNJIBelO00t/CCdJQUnHW5V34OnHMWzIktSalJxRO+FvytQlCQ==", + "requires": { + "errno": "0.1.6", + "graceful-fs": "4.1.11", + "image-size": "0.5.5", + "mime": "1.6.0", + "mkdirp": "0.5.1", + "promise": "7.3.1", + "request": "2.81.0", + "source-map": "0.5.7" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "optional": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "optional": true + } + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + } + } + }, + "loader-utils": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.0.4.tgz", + "integrity": "sha1-E/Vhl/FSOjBYkSSLTHJEVAhIQmw=", + "requires": { + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1" + } + }, + "lodash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz", + "integrity": "sha1-jzSZxSRdNG1oLlsNO0B2fgnxqSw=" + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "requires": { + "currently-unhandled": "0.4.1", + "signal-exit": "3.0.2" + } + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + }, + "maxmin": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-1.1.0.tgz", + "integrity": "sha1-cTZehKmd2Piz99X94vANHn9zvmE=", + "requires": { + "chalk": "1.1.3", + "figures": "1.7.0", + "gzip-size": "1.0.0", + "pretty-bytes": "1.0.4" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "memory-fs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.3.0.tgz", + "integrity": "sha1-e8xrYp46Q+hx1+Kaymrop/FcuyA=", + "requires": { + "errno": "0.1.6", + "readable-stream": "2.3.3" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "requires": { + "camelcase-keys": "2.1.0", + "decamelize": "1.2.0", + "loud-rejection": "1.6.0", + "map-obj": "1.0.1", + "minimist": "1.2.0", + "normalize-package-data": "2.4.0", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "redent": "1.0.0", + "trim-newlines": "1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "requires": { + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" + }, + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "requires": { + "mime-db": "1.30.0" + } + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + }, + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nan": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", + "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", + "optional": true + }, + "ndarray": { + "version": "1.0.18", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.18.tgz", + "integrity": "sha1-tg06cyJOxVXQ+qeXEeUCRI/T95M=", + "requires": { + "iota-array": "1.0.0", + "is-buffer": "1.1.6" + } + }, + "ndarray-fill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ndarray-fill/-/ndarray-fill-1.0.2.tgz", + "integrity": "sha1-owpg9xiODJWC/N1YiWrNy1IqHtY=", + "requires": { + "cwise": "1.0.10" + } + }, + "ndarray-pack": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ndarray-pack/-/ndarray-pack-1.2.1.tgz", + "integrity": "sha1-jK6+qqJNXs9w/4YCBjeXfajuWFo=", + "requires": { + "cwise-compiler": "1.1.3", + "ndarray": "1.0.18" + } + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "node-bitmap": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/node-bitmap/-/node-bitmap-0.0.1.tgz", + "integrity": "sha1-GA6scAPgxwdhjvMTaPYvhLKmkJE=" + }, + "node-libs-browser": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-0.7.0.tgz", + "integrity": "sha1-PicsCBnjCJNeJmdECNevDhSRuDs=", + "requires": { + "assert": "1.4.1", + "browserify-zlib": "0.1.4", + "buffer": "4.9.1", + "console-browserify": "1.1.0", + "constants-browserify": "1.0.0", + "crypto-browserify": "3.3.0", + "domain-browser": "1.1.7", + "events": "1.1.1", + "https-browserify": "0.0.1", + "os-browserify": "0.2.1", + "path-browserify": "0.0.0", + "process": "0.11.10", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "readable-stream": "2.3.3", + "stream-browserify": "2.0.1", + "stream-http": "2.7.2", + "string_decoder": "0.10.31", + "timers-browserify": "2.0.4", + "tty-browserify": "0.0.0", + "url": "0.11.0", + "util": "0.10.3", + "vm-browserify": "0.0.4" + }, + "dependencies": { + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "requires": { + "abbrev": "1.1.1" + } + }, + "noptify": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz", + "integrity": "sha1-WPZUpz2XU98MUdlobckhBKZ/S7s=", + "requires": { + "nopt": "2.0.0" + }, + "dependencies": { + "nopt": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz", + "integrity": "sha1-ynQW8gpeP5w7hhgPlilfo9C1Lg0=", + "requires": { + "abbrev": "1.1.1" + } + } + } + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "requires": { + "hosted-git-info": "2.5.0", + "is-builtin-module": "1.0.0", + "semver": "5.4.1", + "validate-npm-package-license": "3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "1.1.0" + } + }, + "nsdeclare": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/nsdeclare/-/nsdeclare-0.1.0.tgz", + "integrity": "sha1-ENqhU2QjgtPPLAGpFvTrIKEosZ8=" + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "obj-extend": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/obj-extend/-/obj-extend-0.1.0.tgz", + "integrity": "sha1-u0SKR3X7les0p4H5CLusLfI9u1s=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-inspect": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-0.4.0.tgz", + "integrity": "sha1-9RV8EWwUVbJDsG7pdwM5LFrYn+w=" + }, + "object-keys": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "requires": { + "for-own": "0.1.5", + "is-extendable": "0.1.1" + } + }, + "omggif": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.9.tgz", + "integrity": "sha1-3LcCTazVDFK00wPwSALJHAV8dl8=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", + "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=" + }, + "open": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/open/-/open-0.0.5.tgz", + "integrity": "sha1-QsPhjslUZra/DcQvOilFw/DK2Pw=" + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "requires": { + "minimist": "0.0.10", + "wordwrap": "0.0.3" + } + }, + "original": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.0.tgz", + "integrity": "sha1-kUf5P6FpbQS+YeAb1QuurKZWvTs=", + "requires": { + "url-parse": "1.0.5" + }, + "dependencies": { + "url-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.0.5.tgz", + "integrity": "sha1-CFSGBCKv3P7+tsllxmLUgAFpkns=", + "requires": { + "querystringify": "0.0.4", + "requires-port": "1.0.0" + } + } + } + }, + "os-browserify": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz", + "integrity": "sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8=" + }, + "pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=" + }, + "parse-data-uri": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/parse-data-uri/-/parse-data-uri-0.2.0.tgz", + "integrity": "sha1-vwTYUd1ch7CrI45dAazklLYEtMk=", + "requires": { + "data-uri-to-buffer": "0.0.3" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "requires": { + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "1.3.1" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=" + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "2.0.1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + } + } + }, + "pbkdf2-compat": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz", + "integrity": "sha1-tuDI+plJTZTgURV1gCpZpcFC8og=" + }, + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "2.0.4" + } + }, + "pixelsmith": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pixelsmith/-/pixelsmith-1.1.2.tgz", + "integrity": "sha1-maixIATtNSLeb8XBOUgFjy552qM=", + "requires": { + "async": "0.9.2", + "concat-stream": "1.4.10", + "get-pixels": "3.1.1", + "ndarray": "1.0.18", + "ndarray-fill": "1.0.2", + "obj-extend": "0.1.0", + "save-pixels": "2.2.1" + }, + "dependencies": { + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" + }, + "concat-stream": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.4.10.tgz", + "integrity": "sha1-rMO79WAsuMyYDGrIQPp9hgPj7zY=", + "requires": { + "inherits": "2.0.3", + "readable-stream": "1.1.14", + "typedarray": "0.0.6" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "pngjs2": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pngjs2/-/pngjs2-1.2.0.tgz", + "integrity": "sha1-xi/0xMUdLJGUlLdhpvSZP01/5Wk=" + }, + "pngparse": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/pngparse/-/pngparse-1.1.4.tgz", + "integrity": "sha1-SDG+A17Szl/U+1OB5FEBz9YOqE4=" + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=" + }, + "pretty-bytes": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz", + "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=", + "requires": { + "get-stdin": "4.0.1", + "meow": "3.7.0" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "optional": true, + "requires": { + "asap": "2.0.6" + } + }, + "proxy-addr": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", + "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", + "requires": { + "forwarded": "0.1.2", + "ipaddr.js": "1.5.2" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "querystringify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-0.0.4.tgz", + "integrity": "sha1-DPf4T5Rj/wrlHExLFC2VvjdyTZw=" + }, + "quote-stream": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-0.0.0.tgz", + "integrity": "sha1-zeKelMQJsW4Z3HCYuJtmWPlyHTs=", + "requires": { + "minimist": "0.0.8", + "through2": "0.4.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, + "randomatic": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + } + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + } + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "readdirp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "requires": { + "graceful-fs": "4.1.11", + "minimatch": "3.0.4", + "readable-stream": "2.3.3", + "set-immediate-shim": "1.0.1" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "1.1.8" + } + } + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "requires": { + "indent-string": "2.1.0", + "strip-indent": "1.0.1" + } + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "requires": { + "is-equal-shallow": "0.1.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "requires": { + "is-finite": "1.0.2" + } + }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" + } + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "resolve": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.3.1.tgz", + "integrity": "sha1-NMY0R8ZkxwWY0cmxJvxDsqJDEKQ=" + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "requires": { + "align-text": "0.1.4" + } + }, + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" + }, + "ripemd160": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-0.2.0.tgz", + "integrity": "sha1-K/GYveFnys+lHAqSjoS2i74XH84=" + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "save-pixels": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/save-pixels/-/save-pixels-2.2.1.tgz", + "integrity": "sha1-n1mHDoQYBJjmziePWBmz40iXwZ8=", + "requires": { + "contentstream": "1.0.0", + "gif-encoder": "0.4.3", + "jpeg-js": "0.0.4", + "pngjs2": "1.2.0", + "through": "2.3.8" + }, + "dependencies": { + "jpeg-js": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.0.4.tgz", + "integrity": "sha1-Bqr0fv7HrwsZJKWc1pWm0rXthw4=" + } + } + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" + }, + "send": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", + "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", + "requires": { + "debug": "2.6.9", + "depd": "1.1.2", + "destroy": "1.0.4", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.8.1", + "fresh": "0.5.2", + "http-errors": "1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.3.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + } + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "requires": { + "accepts": "1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "1.0.3", + "http-errors": "1.6.2", + "mime-types": "2.1.17", + "parseurl": "1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "serve-static": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", + "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", + "requires": { + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "parseurl": "1.3.2", + "send": "0.16.1" + } + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "sha.js": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.2.6.tgz", + "integrity": "sha1-F93t3F9yL7ZlAWWIlUYZd4ZzFbo=" + }, + "shallow-copy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", + "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "requires": { + "hoek": "2.16.3" + } + }, + "sockjs": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "requires": { + "faye-websocket": "0.10.0", + "uuid": "3.1.0" + }, + "dependencies": { + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "requires": { + "websocket-driver": "0.7.0" + } + } + } + }, + "sockjs-client": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz", + "integrity": "sha1-W6vjhrd15M8U51IJEUUmVAFsixI=", + "requires": { + "debug": "2.6.9", + "eventsource": "0.1.6", + "faye-websocket": "0.11.1", + "inherits": "2.0.3", + "json3": "3.3.2", + "url-parse": "1.2.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "faye-websocket": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", + "requires": { + "websocket-driver": "0.7.0" + } + } + } + }, + "source-list-map": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", + "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=" + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "requires": { + "amdefine": "1.0.1" + } + }, + "spdx-correct": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", + "requires": { + "spdx-license-ids": "1.2.2" + } + }, + "spdx-expression-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", + "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=" + }, + "spdx-license-ids": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", + "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=" + }, + "spritesheet-templates": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/spritesheet-templates/-/spritesheet-templates-9.6.1.tgz", + "integrity": "sha1-Oj5G9x08qZf5foTxxzgU5WsyrZY=", + "requires": { + "handlebars": "3.0.3", + "handlebars-layouts": "1.1.0", + "json-content-demux": "0.1.3", + "underscore": "1.4.4", + "underscore.string": "3.0.3" + }, + "dependencies": { + "handlebars": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-3.0.3.tgz", + "integrity": "sha1-DgllGi8Ps8lJFgWDcQ1VH5Lm0q0=", + "requires": { + "optimist": "0.6.1", + "source-map": "0.1.43", + "uglify-js": "2.3.6" + } + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=" + }, + "underscore.string": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.0.3.tgz", + "integrity": "sha1-Rhe4waJQz25QZPu7Nj0PqWzxRVI=" + } + } + }, + "spritesmith": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/spritesmith/-/spritesmith-1.3.2.tgz", + "integrity": "sha1-KAYls/39cjmYemXAnEim7mb94Jg=", + "requires": { + "async": "0.2.10", + "layout": "2.2.0", + "pixelsmith": "1.1.2" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + } + } + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "static-eval": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-0.2.4.tgz", + "integrity": "sha1-t9NNg4k3uWn5ZBygfUj47eJj6ns=", + "requires": { + "escodegen": "0.0.28" + }, + "dependencies": { + "escodegen": { + "version": "0.0.28", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-0.0.28.tgz", + "integrity": "sha1-Dk/xcV8yh3XWyrUaxEpAbNer/9M=", + "requires": { + "esprima": "1.0.4", + "estraverse": "1.3.2", + "source-map": "0.1.43" + } + }, + "estraverse": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.3.2.tgz", + "integrity": "sha1-N8K4k+8T1yPydth41g2FNRUqbEI=" + } + } + }, + "static-module": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/static-module/-/static-module-1.5.0.tgz", + "integrity": "sha1-J9qYg8QajNCSNvhC8MHrxu32PYY=", + "requires": { + "concat-stream": "1.6.0", + "duplexer2": "0.0.2", + "escodegen": "1.3.3", + "falafel": "2.1.0", + "has": "1.0.1", + "object-inspect": "0.4.0", + "quote-stream": "0.0.0", + "readable-stream": "1.0.34", + "shallow-copy": "0.0.1", + "static-eval": "0.2.4", + "through2": "0.4.2" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" + }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3" + } + }, + "stream-cache": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stream-cache/-/stream-cache-0.0.2.tgz", + "integrity": "sha1-GsWtaDJCjKVWZ9ve45Xa1ObbEY8=" + }, + "stream-http": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", + "integrity": "sha512-c0yTD2rbQzXtSsFSVhtpvY/vS6u066PcXOX9kBB3mSO76RiUQzL340uJkGBWnlBg4/HZzqiUXtaVA7wcRcJgEw==", + "requires": { + "builtin-status-codes": "3.0.0", + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "to-arraybuffer": "1.0.1", + "xtend": "4.0.1" + }, + "dependencies": { + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "0.2.1" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "requires": { + "get-stdin": "4.0.1" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "tapable": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", + "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=" + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", + "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", + "requires": { + "readable-stream": "1.0.34", + "xtend": "2.1.2" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "time-stamp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-2.0.0.tgz", + "integrity": "sha1-lcakRTDhW6jW9KPsuMOj+sRto1c=" + }, + "timers-browserify": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.4.tgz", + "integrity": "sha512-uZYhyU3EX8O7HQP+J9fTVYwsq90Vr68xPEFo7yrVImIxYvHgukBEgOB/SgGoorWVTzGM/3Z+wUNnboA4M8jWrg==", + "requires": { + "setimmediate": "1.0.5" + } + }, + "tiny-lr-fork": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/tiny-lr-fork/-/tiny-lr-fork-0.0.5.tgz", + "integrity": "sha1-Hpnh4qhGm3NquX2X7vqYxx927Qo=", + "requires": { + "debug": "0.7.4", + "faye-websocket": "0.4.4", + "noptify": "0.0.3", + "qs": "0.5.6" + }, + "dependencies": { + "qs": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz", + "integrity": "sha1-MbGtBYVnZRxSaSFQa5qHk5EaA4Q=" + } + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" + }, + "tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "requires": { + "punycode": "1.4.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "type-is": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.1.17" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "uglify-js": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.3.6.tgz", + "integrity": "sha1-+gmEdwtCi3qbKoBY9GNV0U/vIRo=", + "optional": true, + "requires": { + "async": "0.2.10", + "optimist": "0.3.7", + "source-map": "0.1.43" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", + "optional": true + }, + "optimist": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=", + "optional": true, + "requires": { + "wordwrap": "0.0.3" + } + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=" + }, + "underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" + }, + "underscore.string": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz", + "integrity": "sha1-18D6KvXVoaZ/QlPa7pgTLnM/Dxk=" + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "uri-path": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/uri-path/-/uri-path-0.0.2.tgz", + "integrity": "sha1-gD6wHy/rF5J9zOD2GH5yt19T9VQ=" + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "url-parse": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.2.0.tgz", + "integrity": "sha512-DT1XbYAfmQP65M/mE6OALxmXzZ/z1+e5zk2TcSKe/KiYbNGZxgtttzC0mR/sjopbpOXcbniq7eIKmocJnUWlEw==", + "requires": { + "querystringify": "1.0.0", + "requires-port": "1.0.0" + }, + "dependencies": { + "querystringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz", + "integrity": "sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs=" + } + } + }, + "url2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url2/-/url2-1.0.0.tgz", + "integrity": "sha1-taKGYJmqIX49p/T5DJq+7Adxyug=" + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + }, + "validate-npm-package-license": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", + "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", + "requires": { + "spdx-correct": "1.0.2", + "spdx-expression-parse": "1.0.4" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "requires": { + "indexof": "0.0.1" + } + }, + "watchpack": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-0.2.9.tgz", + "integrity": "sha1-Yuqkq15bo1/fwBgnVibjwPXj+ws=", + "requires": { + "async": "0.9.2", + "chokidar": "1.7.0", + "graceful-fs": "4.1.11" + }, + "dependencies": { + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + } + } + }, + "webpack": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-1.15.0.tgz", + "integrity": "sha1-T/MfU9sDM55VFkqdRo7gMklo/pg=", + "requires": { + "acorn": "3.3.0", + "async": "1.5.2", + "clone": "1.0.3", + "enhanced-resolve": "0.9.1", + "interpret": "0.6.6", + "loader-utils": "0.2.17", + "memory-fs": "0.3.0", + "mkdirp": "0.5.1", + "node-libs-browser": "0.7.0", + "optimist": "0.6.1", + "supports-color": "3.2.3", + "tapable": "0.1.10", + "uglify-js": "2.7.5", + "watchpack": "0.2.9", + "webpack-core": "0.6.9" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=" + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "requires": { + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1", + "object-assign": "4.1.1" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + }, + "uglify-js": { + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.7.5.tgz", + "integrity": "sha1-RhLAx7qu4rp8SH3kkErhIgefLKg=", + "requires": { + "async": "0.2.10", + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + } + } + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "requires": { + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", + "window-size": "0.1.0" + } + } + } + }, + "webpack-core": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", + "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=", + "requires": { + "source-list-map": "0.1.8", + "source-map": "0.4.4" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": "1.0.1" + } + } + } + }, + "webpack-dev-middleware": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz", + "integrity": "sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==", + "requires": { + "memory-fs": "0.4.1", + "mime": "1.6.0", + "path-is-absolute": "1.0.1", + "range-parser": "1.2.0", + "time-stamp": "2.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "requires": { + "errno": "0.1.6", + "readable-stream": "2.3.3" + } + } + } + }, + "webpack-dev-server": { + "version": "1.16.5", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-1.16.5.tgz", + "integrity": "sha1-DL1fLSrI1OWTqs1clwLnu9XlmJI=", + "requires": { + "compression": "1.7.1", + "connect-history-api-fallback": "1.5.0", + "express": "4.16.2", + "http-proxy-middleware": "0.17.4", + "open": "0.0.5", + "optimist": "0.6.1", + "serve-index": "1.9.1", + "sockjs": "0.3.19", + "sockjs-client": "1.1.4", + "stream-cache": "0.0.2", + "strip-ansi": "3.0.1", + "supports-color": "3.2.3", + "webpack-dev-middleware": "1.12.2" + }, + "dependencies": { + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "websocket-driver": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", + "requires": { + "http-parser-js": "0.4.9", + "websocket-extensions": "0.1.3" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==" + }, + "which": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz", + "integrity": "sha1-RgwdoPgQED0DIam2M6+eV15kSG8=" + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + }, + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "requires": { + "object-keys": "0.4.0" + }, + "dependencies": { + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" + } + } + }, + "yargs": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-1.3.3.tgz", + "integrity": "sha1-BU3oth8i7v23IHBZ6u+da4P7kxo=" + } + } +} diff --git a/client/package.json b/client/package.json index 6c3c8a6198ca..6c1f7b09555c 100644 --- a/client/package.json +++ b/client/package.json @@ -23,6 +23,7 @@ "handlebars": "^4.0.4", "handlebars-loader": "^1.1.4", "i18n-webpack-plugin": "^0.2.7", + "readable-stream": "^2.3.3", "webpack": "^1.10.1", "webpack-dev-server": "^1.7.0" } diff --git a/static/maps/mvc/dataset/dataset-li-edit.js.map b/static/maps/mvc/dataset/dataset-li-edit.js.map index 1b35ee071d56..0ed8137967e3 100644 --- a/static/maps/mvc/dataset/dataset-li-edit.js.map +++ b/static/maps/mvc/dataset/dataset-li-edit.js.map @@ -1 +1 @@ -{"version":3,"file":"dataset-li-edit.js","sources":["../../../src/mvc/dataset/dataset-li-edit.js"],"names":["define","STATES","DATASET_LI","TAGS","ANNOTATIONS","faIconButton","BASE_MVC","_l","_super","DatasetListItemView","DatasetListItemEdit","extend","initialize","attributes","prototype","call","this","hasUser","purgeAllowed","tagsEditorShown","annotationEditorShown","_renderPrimaryActions","actions","model","get","NOT_VIEWABLE","concat","_renderEditButton","_renderDeleteButton","DISCARDED","purged","deleted","editBtnData","title","href","urls","edit","target","linkTarget","faIcon","classes","disabled","_","contains","UPLOAD","NEW","self","deletedAlready","isDeletedOrPurged","onclick","$el","find","trigger","_renderDetails","$details","state","OK","FAILED_METADATA","_renderTags","_renderAnnotation","_makeDbkeyEditLink","_setUpBehaviors","_renderSecondaryActions","ERROR","unshift","_renderErrButton","_renderRerunButton","_renderVisualizationsButton","report_error","creating_job","rerun_url","rerun","ev","preventDefault","require","ToolForm","form","View","job_id","deferred","execute","options","model_class","startsWith","galaxy_main","location","Galaxy","app","display","visualizations","hasData","isEmpty","isObject","warn","$visualizations","$","templates","attr","_addScratchBookFn","addBack","$links","click","frame","active","add","url","stopPropagation","$where","view","tagsEditor","TagsEditor","el","onshowFirstTime","render","onshow","onhide","$activator","appendTo","toggle","annotationEditor","AnnotationEditor","editableDbkey","replaceWith","events","clone","click .undelete-link","click .purge-link","click .edit-btn","click .delete-btn","click .rerun-btn","click .report-err-btn","click .visualization-btn","click .dbkey a","_clickUndeleteLink","undelete","_clickPurgeLink","purge","toString","modelString","warnings","failed_metadata","wrapTemplate","visualizationsTemplate"],"mappings":"AAAAA,QACI,qBACA,yBACA,UACA,iBACA,oBACA,eACA,sBACD,SAAUC,EAAQC,EAAYC,EAAMC,EAAaC,EAAcC,EAAUC,GAE5E,GAAIC,GAASN,EAAWO,oBAGpBC,EAAsBF,EAAOG,QAO7BC,WAAc,SAAUC,GACpBL,EAAOM,UAAUF,WAAWG,KAAMC,KAAMH,GAExCG,KAAKC,QAAUJ,EAAWI,QAG1BD,KAAKE,aAAeL,EAAWK,eAAgB,EAI/CF,KAAKG,gBAAyBN,EAAWM,kBAAmB,EAE5DH,KAAKI,sBAAyBP,EAAWO,wBAAyB,GAKtEC,sBAAwB,WACpB,GAAIC,GAAUd,EAAOM,UAAUO,sBAAsBN,KAAMC,KAC3D,OAAIA,MAAKO,MAAMC,IAAK,WAAcvB,EAAOwB,aAC9BH,EAGJd,EAAOM,UAAUO,sBAAsBN,KAAMC,MAAOU,QACvDV,KAAKW,oBACLX,KAAKY,yBAObD,kBAAoB,WAGhB,GAAMX,KAAKO,MAAMC,IAAK,WAAcvB,EAAO4B,YACpCb,KAAKO,MAAMC,IAAK,cACnB,MAAO,KAGX,IAAIM,GAASd,KAAKO,MAAMC,IAAK,UACzBO,EAAUf,KAAKO,MAAMC,IAAK,WAC1BQ,GACIC,MAAc1B,EAAI,mBAClB2B,KAAclB,KAAKO,MAAMY,KAAKC,KAC9BC,OAAcrB,KAAKsB,WACnBC,OAAc,YACdC,QAAc,WAiBtB,OAbIT,IAAWD,GACXE,EAAYS,UAAW,EACnBX,EACAE,EAAYC,MAAQ1B,EAAI,wDACjBwB,IACPC,EAAYC,MAAQ1B,EAAI,yCAIrBmC,EAAEC,UAAY1C,EAAO2C,OAAQ3C,EAAO4C,KAAO7B,KAAKO,MAAMC,IAAK,YAClEQ,EAAYS,UAAW,EACvBT,EAAYC,MAAQ1B,EAAI,qCAErBF,EAAc2B,IAIzBJ,oBAAsB,WAElB,IAAOZ,KAAKO,MAAMC,IAAK,cACnB,MAAO,KAGX,IAAIsB,GAAO9B,KACP+B,EAAiB/B,KAAKO,MAAMyB,mBAChC,OAAO3C,IACC4B,MAAgD1B,EAAjCwC,EAAqC,6BAAjB,UACnCN,SAAcM,EACdR,OAAc,WACdC,QAAc,aACdS,QAAc,WAEVH,EAAKI,IAAIC,KAAM,wBAAyBC,QAAS,YACjDN,EAAKvB,MAAgB,aAOrC8B,eAAiB,WAEb,GAAIC,GAAW9C,EAAOM,UAAUuC,eAAetC,KAAMC,MACjDuC,EAAQvC,KAAKO,MAAMC,IAAK,QAU5B,QARKR,KAAKO,MAAMyB,qBAAuBN,EAAEC,UAAW1C,EAAOuD,GAAIvD,EAAOwD,iBAAmBF,KACrFvC,KAAK0C,YAAaJ,GAClBtC,KAAK2C,kBAAmBL,GACxBtC,KAAK4C,mBAAoBN,IAI7BtC,KAAK6C,gBAAiBP,GACfA,GAIXQ,wBAA0B,WACtB,GAAIxC,GAAUd,EAAOM,UAAUgD,wBAAwB/C,KAAMC,KAC7D,QAAQA,KAAKO,MAAMC,IAAK,UACpB,IAAKvB,GAAO2C,OACZ,IAAK3C,GAAOwB,aACR,MAAOH,EACX,KAAKrB,GAAO8D,MAGR,MADAzC,GAAQ0C,QAAShD,KAAKiD,oBACf3C,EAAQI,QAASV,KAAKkD,sBACjC,KAAKjE,GAAOuD,GACZ,IAAKvD,GAAOwD,gBACR,MAAOnC,GAAQI,QAASV,KAAKkD,qBAAsBlD,KAAKmD,gCAEhE,MAAO7C,GAAQI,QAASV,KAAKkD,wBAIjCD,iBAAmB,WACf,MAAO5D,IACH4B,MAAc1B,EAAI,6BAClB2B,KAAclB,KAAKO,MAAMY,KAAKiC,aAC9B5B,QAAc,mBACdH,OAAcrB,KAAKsB,WACnBC,OAAc,YAKtB2B,mBAAqB,WACjB,GAAIG,GAAerD,KAAKO,MAAMC,IAAK,gBAC/B8C,EAAYtD,KAAKO,MAAMY,KAAKoC,KAChC,OAAIvD,MAAKO,MAAMC,IAAK,cACTnB,GACH4B,MAAc1B,EAAI,sBAClB2B,KAAclB,KAAKO,MAAMY,KAAKoC,MAC9B/B,QAAc,YACdH,OAAcrB,KAAKsB,WACnBC,OAAc,aACdU,QAAc,SAAUuB,GACpBA,EAAGC,iBAGHC,SAAU,sBAAwB,SAAUC,GACxC,GAAIC,GAAO,GAAID,GAASE,MAAOC,OAAWT,GAC1CO,GAAKG,SAASC,QAAS,WAEfJ,EAAKK,QAAQC,YAAYC,WAAW,gBACpCC,YAAYC,SAAWf,EAEvBgB,OAAOC,IAAIC,QAASZ,UAlB5C,QA2BJT,4BAA8B,WAE1B,GAAIsB,GAAiBzE,KAAKO,MAAMC,IAAK,iBACrC,IAAMR,KAAKO,MAAMyB,sBACVhC,KAAKC,UACLD,KAAKO,MAAMmE,WACZhD,EAAEiD,QAASF,GACb,MAAO,KAEX,KAAK/C,EAAEkD,SAAUH,EAAe,IAE5B,MADAzE,MAAK6E,KAAM,yCACJ,IAGX,IAAIC,GAAkBC,EAAG/E,KAAKgF,UAAUP,eAAgBA,EAAgBzE,MAKxE,OAHA8E,GAAgB3C,KAAM,0BAA0B8C,KAAM,SAAUjF,KAAKsB,YAErEtB,KAAKkF,kBAAmBJ,EAAgB3C,KAAM,uBAAwBgD,QAAS,wBACxEL,GAIXI,kBAAoB,SAAUE,GAE1BA,EAAOC,MAAO,SAAU7B,GAChBc,OAAOgB,OAAShB,OAAOgB,MAAMC,SAC7BjB,OAAOgB,MAAME,KACTvE,MAAc,gBACdwE,IAAcV,EAAG/E,MAAOiF,KAAM,UAElCzB,EAAGC,iBACHD,EAAGkC,sBAOfhD,YAAc,SAAUiD,GACpB,GAAK3F,KAAKC,QAAV,CACA,GAAI2F,GAAO5F,IACXA,MAAK6F,WAAa,GAAI1G,GAAK2G,YACvBvF,MAAkBP,KAAKO,MACvBwF,GAAkBJ,EAAOxD,KAAM,iBAC/B6D,gBAAkB,WAAYhG,KAAKiG,UAEnCC,OAAkB,WAAYN,EAAKzF,iBAAkB,GACrDgG,OAAkB,WAAYP,EAAKzF,iBAAkB,GACrDiG,WAAkB/G,GACd4B,MAAU1B,EAAI,qBACdiC,QAAU,UACVD,OAAU,YACX8E,SAAUV,EAAOxD,KAAM,sBAE1BnC,KAAKG,iBAAmBH,KAAK6F,WAAWS,QAAQ,KAIxD3D,kBAAoB,SAAUgD,GAC1B,GAAK3F,KAAKC,QAAV,CACA,GAAI2F,GAAO5F,IACXA,MAAKuG,iBAAmB,GAAInH,GAAYoH,kBACpCjG,MAAkBP,KAAKO,MACvBwF,GAAkBJ,EAAOxD,KAAM,uBAC/B6D,gBAAkB,WAAYhG,KAAKiG,UAEnCC,OAAkB,WAAYN,EAAKxF,uBAAwB,GAC3D+F,OAAkB,WAAYP,EAAKxF,uBAAwB,GAC3DgG,WAAkB/G,GACd4B,MAAU1B,EAAI,2BACdiC,QAAU,eACVD,OAAU,eACX8E,SAAUV,EAAOxD,KAAM,sBAE1BnC,KAAKI,uBAAyBJ,KAAKuG,iBAAiBD,QAAQ,KAIpE1D,mBAAqB,SAAUN,GAE3B,GAA2C,MAAvCtC,KAAKO,MAAMC,IAAK,oBACfR,KAAKO,MAAMyB,oBAAqB,CACjC,GAAIyE,GAAgB1B,EAAG,0BAClBE,KAAM,OAAQjF,KAAKO,MAAMY,KAAKC,MAC9B6D,KAAM,SAAUjF,KAAKsB,WAC1BgB,GAASH,KAAM,iBAAkBuE,YAAaD,KAMtDE,OAASjF,EAAE/B,OAAQ+B,EAAEkF,MAAOpH,EAAOM,UAAU6G,SACzCE,uBAA0B,qBAC1BC,oBAA0B,kBAE1BC,kBAA0B,SAAUvD,GAAMxD,KAAKoC,QAAS,OAAQpC,KAAMwD,IACtEwD,oBAA0B,SAAUxD,GAAMxD,KAAKoC,QAAS,SAAUpC,KAAMwD,IACxEyD,mBAA0B,SAAUzD,GAAMxD,KAAKoC,QAAS,QAASpC,KAAMwD,IACvE0D,wBAA0B,SAAU1D,GAAMxD,KAAKoC,QAAS,aAAcpC,KAAMwD,IAC5E2D,2BAA6B,SAAU3D,GAAMxD,KAAKoC,QAAS,YAAapC,KAAMwD,IAC9E4D,iBAA0B,SAAU5D,GAAMxD,KAAKoC,QAAS,OAAQpC,KAAMwD,MAK1E6D,mBAAqB,WAEjB,MADArH,MAAKO,MAAM+G,YACJ,GAIXC,gBAAkB,WAGd,MADAvH,MAAKO,MAAMiH,SACJ,GAKXC,SAAW,WACP,GAAIC,GAAgB1H,KAAW,MAAIA,KAAKO,MAAQ,GAAK,YACrD,OAAO,eAAiBmH,EAAc,MAyE1C,OAlEJhI,GAAoBI,UAAUkF,UAAa,WAGvC,GAAI2C,GAAWjG,EAAE/B,UAAYH,EAAOM,UAAUkF,UAAU2C,UACpDC,gBAAkBtI,EAASuI,cAEvB,mDACI,4DACItI,EAAI,2DACJ,4EACIA,EAAI,2CACR,OACJ,SACJ,WACD,WAEHwB,QAAUzB,EAASuI,cAEf,kDAEI,gDACItI,EAAI,iCACJ,6DAA8DA,EAAI,eAAiB,OACnF,iCACI,0DACIA,EAAI,mCACR,OACJ,UACJ,SACJ,WACD,aAGHuI,EAAyBxI,EAASuI,cAClC,2CACI,gGACQ,oDAAqDtI,EAAI,gBACzD,mCACJ,0CACJ,OAEJ,iBACI,iDACI,uEAAwEA,EAAI,aAAe,KACvF,0CACJ,OACA,yCACI,2DACI,qEACQ,yCACJ,4BACJ,YACJ,YACJ,QACJ,SACJ,WACD,iBAEH,OAAOmC,GAAE/B,UAAYH,EAAOM,UAAUkF,WAClC2C,SAAWA,EACXlD,eAAiBqD,QAOjBpI,oBAAsBA"} \ No newline at end of file +{"version":3,"file":"dataset-li-edit.js","sources":["../../../src/mvc/dataset/dataset-li-edit.js"],"names":["define","STATES","DATASET_LI","TAGS","ANNOTATIONS","faIconButton","BASE_MVC","_l","_super","DatasetListItemView","DatasetListItemEdit","extend","initialize","attributes","prototype","call","this","hasUser","purgeAllowed","tagsEditorShown","annotationEditorShown","_renderPrimaryActions","actions","model","get","NOT_VIEWABLE","concat","_renderEditButton","_renderDeleteButton","DISCARDED","purged","deleted","editBtnData","title","href","urls","edit","target","linkTarget","faIcon","classes","disabled","_","contains","UPLOAD","NEW","self","deletedAlready","isDeletedOrPurged","onclick","$el","find","trigger","_renderDetails","$details","state","OK","FAILED_METADATA","_renderTags","_renderAnnotation","_makeDbkeyEditLink","_setUpBehaviors","_renderSecondaryActions","ERROR","unshift","_renderErrButton","_renderRerunButton","_renderVisualizationsButton","report_error","creating_job","rerun_url","rerun","ev","preventDefault","require","ToolForm","form","View","job_id","deferred","execute","options","model_class","lastIndexOf","galaxy_main","location","Galaxy","app","display","visualizations","hasData","isEmpty","isObject","warn","$visualizations","$","templates","attr","_addScratchBookFn","addBack","$links","click","frame","active","add","url","stopPropagation","$where","view","tagsEditor","TagsEditor","el","onshowFirstTime","render","onshow","onhide","$activator","appendTo","toggle","annotationEditor","AnnotationEditor","editableDbkey","replaceWith","events","clone","click .undelete-link","click .purge-link","click .edit-btn","click .delete-btn","click .rerun-btn","click .report-err-btn","click .visualization-btn","click .dbkey a","_clickUndeleteLink","undelete","_clickPurgeLink","purge","toString","modelString","warnings","failed_metadata","wrapTemplate","visualizationsTemplate"],"mappings":"AAAAA,QACI,qBACA,yBACA,UACA,iBACA,oBACA,eACA,sBACD,SAAUC,EAAQC,EAAYC,EAAMC,EAAaC,EAAcC,EAAUC,GAE5E,GAAIC,GAASN,EAAWO,oBAGpBC,EAAsBF,EAAOG,QAO7BC,WAAc,SAAUC,GACpBL,EAAOM,UAAUF,WAAWG,KAAMC,KAAMH,GAExCG,KAAKC,QAAUJ,EAAWI,QAG1BD,KAAKE,aAAeL,EAAWK,eAAgB,EAI/CF,KAAKG,gBAAyBN,EAAWM,kBAAmB,EAE5DH,KAAKI,sBAAyBP,EAAWO,wBAAyB,GAKtEC,sBAAwB,WACpB,GAAIC,GAAUd,EAAOM,UAAUO,sBAAsBN,KAAMC,KAC3D,OAAIA,MAAKO,MAAMC,IAAK,WAAcvB,EAAOwB,aAC9BH,EAGJd,EAAOM,UAAUO,sBAAsBN,KAAMC,MAAOU,QACvDV,KAAKW,oBACLX,KAAKY,yBAObD,kBAAoB,WAGhB,GAAMX,KAAKO,MAAMC,IAAK,WAAcvB,EAAO4B,YACpCb,KAAKO,MAAMC,IAAK,cACnB,MAAO,KAGX,IAAIM,GAASd,KAAKO,MAAMC,IAAK,UACzBO,EAAUf,KAAKO,MAAMC,IAAK,WAC1BQ,GACIC,MAAc1B,EAAI,mBAClB2B,KAAclB,KAAKO,MAAMY,KAAKC,KAC9BC,OAAcrB,KAAKsB,WACnBC,OAAc,YACdC,QAAc,WAiBtB,OAbIT,IAAWD,GACXE,EAAYS,UAAW,EACnBX,EACAE,EAAYC,MAAQ1B,EAAI,wDACjBwB,IACPC,EAAYC,MAAQ1B,EAAI,yCAIrBmC,EAAEC,UAAY1C,EAAO2C,OAAQ3C,EAAO4C,KAAO7B,KAAKO,MAAMC,IAAK,YAClEQ,EAAYS,UAAW,EACvBT,EAAYC,MAAQ1B,EAAI,qCAErBF,EAAc2B,IAIzBJ,oBAAsB,WAElB,IAAOZ,KAAKO,MAAMC,IAAK,cACnB,MAAO,KAGX,IAAIsB,GAAO9B,KACP+B,EAAiB/B,KAAKO,MAAMyB,mBAChC,OAAO3C,IACC4B,MAAgD1B,EAAjCwC,EAAqC,6BAAjB,UACnCN,SAAcM,EACdR,OAAc,WACdC,QAAc,aACdS,QAAc,WAEVH,EAAKI,IAAIC,KAAM,wBAAyBC,QAAS,YACjDN,EAAKvB,MAAgB,aAOrC8B,eAAiB,WAEb,GAAIC,GAAW9C,EAAOM,UAAUuC,eAAetC,KAAMC,MACjDuC,EAAQvC,KAAKO,MAAMC,IAAK,QAU5B,QARKR,KAAKO,MAAMyB,qBAAuBN,EAAEC,UAAW1C,EAAOuD,GAAIvD,EAAOwD,iBAAmBF,KACrFvC,KAAK0C,YAAaJ,GAClBtC,KAAK2C,kBAAmBL,GACxBtC,KAAK4C,mBAAoBN,IAI7BtC,KAAK6C,gBAAiBP,GACfA,GAIXQ,wBAA0B,WACtB,GAAIxC,GAAUd,EAAOM,UAAUgD,wBAAwB/C,KAAMC,KAC7D,QAAQA,KAAKO,MAAMC,IAAK,UACpB,IAAKvB,GAAO2C,OACZ,IAAK3C,GAAOwB,aACR,MAAOH,EACX,KAAKrB,GAAO8D,MAGR,MADAzC,GAAQ0C,QAAShD,KAAKiD,oBACf3C,EAAQI,QAASV,KAAKkD,sBACjC,KAAKjE,GAAOuD,GACZ,IAAKvD,GAAOwD,gBACR,MAAOnC,GAAQI,QAASV,KAAKkD,qBAAsBlD,KAAKmD,gCAEhE,MAAO7C,GAAQI,QAASV,KAAKkD,wBAIjCD,iBAAmB,WACf,MAAO5D,IACH4B,MAAc1B,EAAI,6BAClB2B,KAAclB,KAAKO,MAAMY,KAAKiC,aAC9B5B,QAAc,mBACdH,OAAcrB,KAAKsB,WACnBC,OAAc,YAKtB2B,mBAAqB,WACjB,GAAIG,GAAerD,KAAKO,MAAMC,IAAK,gBAC/B8C,EAAYtD,KAAKO,MAAMY,KAAKoC,KAChC,OAAIvD,MAAKO,MAAMC,IAAK,cACTnB,GACH4B,MAAc1B,EAAI,sBAClB2B,KAAclB,KAAKO,MAAMY,KAAKoC,MAC9B/B,QAAc,YACdH,OAAcrB,KAAKsB,WACnBC,OAAc,aACdU,QAAc,SAAUuB,GACpBA,EAAGC,iBAGHC,SAAU,sBAAwB,SAAUC,GACxC,GAAIC,GAAO,GAAID,GAASE,MAAOC,OAAWT,GAC1CO,GAAKG,SAASC,QAAS,WAEsC,IAArDJ,EAAKK,QAAQC,YAAYC,YAAY,QAAS,GAC9CC,YAAYC,SAAWf,EAEvBgB,OAAOC,IAAIC,QAASZ,UAlB5C,QA2BJT,4BAA8B,WAE1B,GAAIsB,GAAiBzE,KAAKO,MAAMC,IAAK,iBACrC,IAAMR,KAAKO,MAAMyB,sBACVhC,KAAKC,UACLD,KAAKO,MAAMmE,WACZhD,EAAEiD,QAASF,GACb,MAAO,KAEX,KAAK/C,EAAEkD,SAAUH,EAAe,IAE5B,MADAzE,MAAK6E,KAAM,yCACJ,IAGX,IAAIC,GAAkBC,EAAG/E,KAAKgF,UAAUP,eAAgBA,EAAgBzE,MAKxE,OAHA8E,GAAgB3C,KAAM,0BAA0B8C,KAAM,SAAUjF,KAAKsB,YAErEtB,KAAKkF,kBAAmBJ,EAAgB3C,KAAM,uBAAwBgD,QAAS,wBACxEL,GAIXI,kBAAoB,SAAUE,GAE1BA,EAAOC,MAAO,SAAU7B,GAChBc,OAAOgB,OAAShB,OAAOgB,MAAMC,SAC7BjB,OAAOgB,MAAME,KACTvE,MAAc,gBACdwE,IAAcV,EAAG/E,MAAOiF,KAAM,UAElCzB,EAAGC,iBACHD,EAAGkC,sBAOfhD,YAAc,SAAUiD,GACpB,GAAK3F,KAAKC,QAAV,CACA,GAAI2F,GAAO5F,IACXA,MAAK6F,WAAa,GAAI1G,GAAK2G,YACvBvF,MAAkBP,KAAKO,MACvBwF,GAAkBJ,EAAOxD,KAAM,iBAC/B6D,gBAAkB,WAAYhG,KAAKiG,UAEnCC,OAAkB,WAAYN,EAAKzF,iBAAkB,GACrDgG,OAAkB,WAAYP,EAAKzF,iBAAkB,GACrDiG,WAAkB/G,GACd4B,MAAU1B,EAAI,qBACdiC,QAAU,UACVD,OAAU,YACX8E,SAAUV,EAAOxD,KAAM,sBAE1BnC,KAAKG,iBAAmBH,KAAK6F,WAAWS,QAAQ,KAIxD3D,kBAAoB,SAAUgD,GAC1B,GAAK3F,KAAKC,QAAV,CACA,GAAI2F,GAAO5F,IACXA,MAAKuG,iBAAmB,GAAInH,GAAYoH,kBACpCjG,MAAkBP,KAAKO,MACvBwF,GAAkBJ,EAAOxD,KAAM,uBAC/B6D,gBAAkB,WAAYhG,KAAKiG,UAEnCC,OAAkB,WAAYN,EAAKxF,uBAAwB,GAC3D+F,OAAkB,WAAYP,EAAKxF,uBAAwB,GAC3DgG,WAAkB/G,GACd4B,MAAU1B,EAAI,2BACdiC,QAAU,eACVD,OAAU,eACX8E,SAAUV,EAAOxD,KAAM,sBAE1BnC,KAAKI,uBAAyBJ,KAAKuG,iBAAiBD,QAAQ,KAIpE1D,mBAAqB,SAAUN,GAE3B,GAA2C,MAAvCtC,KAAKO,MAAMC,IAAK,oBACfR,KAAKO,MAAMyB,oBAAqB,CACjC,GAAIyE,GAAgB1B,EAAG,0BAClBE,KAAM,OAAQjF,KAAKO,MAAMY,KAAKC,MAC9B6D,KAAM,SAAUjF,KAAKsB,WAC1BgB,GAASH,KAAM,iBAAkBuE,YAAaD,KAMtDE,OAASjF,EAAE/B,OAAQ+B,EAAEkF,MAAOpH,EAAOM,UAAU6G,SACzCE,uBAA0B,qBAC1BC,oBAA0B,kBAE1BC,kBAA0B,SAAUvD,GAAMxD,KAAKoC,QAAS,OAAQpC,KAAMwD,IACtEwD,oBAA0B,SAAUxD,GAAMxD,KAAKoC,QAAS,SAAUpC,KAAMwD,IACxEyD,mBAA0B,SAAUzD,GAAMxD,KAAKoC,QAAS,QAASpC,KAAMwD,IACvE0D,wBAA0B,SAAU1D,GAAMxD,KAAKoC,QAAS,aAAcpC,KAAMwD,IAC5E2D,2BAA6B,SAAU3D,GAAMxD,KAAKoC,QAAS,YAAapC,KAAMwD,IAC9E4D,iBAA0B,SAAU5D,GAAMxD,KAAKoC,QAAS,OAAQpC,KAAMwD,MAK1E6D,mBAAqB,WAEjB,MADArH,MAAKO,MAAM+G,YACJ,GAIXC,gBAAkB,WAGd,MADAvH,MAAKO,MAAMiH,SACJ,GAKXC,SAAW,WACP,GAAIC,GAAgB1H,KAAW,MAAIA,KAAKO,MAAQ,GAAK,YACrD,OAAO,eAAiBmH,EAAc,MAyE1C,OAlEJhI,GAAoBI,UAAUkF,UAAa,WAGvC,GAAI2C,GAAWjG,EAAE/B,UAAYH,EAAOM,UAAUkF,UAAU2C,UACpDC,gBAAkBtI,EAASuI,cAEvB,mDACI,4DACItI,EAAI,2DACJ,4EACIA,EAAI,2CACR,OACJ,SACJ,WACD,WAEHwB,QAAUzB,EAASuI,cAEf,kDAEI,gDACItI,EAAI,iCACJ,6DAA8DA,EAAI,eAAiB,OACnF,iCACI,0DACIA,EAAI,mCACR,OACJ,UACJ,SACJ,WACD,aAGHuI,EAAyBxI,EAASuI,cAClC,2CACI,gGACQ,oDAAqDtI,EAAI,gBACzD,mCACJ,0CACJ,OAEJ,iBACI,iDACI,uEAAwEA,EAAI,aAAe,KACvF,0CACJ,OACA,yCACI,2DACI,qEACQ,yCACJ,4BACJ,YACJ,YACJ,QACJ,SACJ,WACD,iBAEH,OAAOmC,GAAE/B,UAAYH,EAAOM,UAAUkF,WAClC2C,SAAWA,EACXlD,eAAiBqD,QAOjBpI,oBAAsBA"} \ No newline at end of file diff --git a/static/scripts/mvc/dataset/dataset-li-edit.js b/static/scripts/mvc/dataset/dataset-li-edit.js index 4082bf513bc1..9a24b126eb8f 100644 --- a/static/scripts/mvc/dataset/dataset-li-edit.js +++ b/static/scripts/mvc/dataset/dataset-li-edit.js @@ -1,2 +1,2 @@ -define(["mvc/dataset/states","mvc/dataset/dataset-li","mvc/tag","mvc/annotation","ui/fa-icon-button","mvc/base-mvc","utils/localization"],function(a,b,c,d,e,f,g){var h=b.DatasetListItemView,i=h.extend({initialize:function(a){h.prototype.initialize.call(this,a),this.hasUser=a.hasUser,this.purgeAllowed=a.purgeAllowed||!1,this.tagsEditorShown=a.tagsEditorShown||!1,this.annotationEditorShown=a.annotationEditorShown||!1},_renderPrimaryActions:function(){var b=h.prototype._renderPrimaryActions.call(this);return this.model.get("state")===a.NOT_VIEWABLE?b:h.prototype._renderPrimaryActions.call(this).concat([this._renderEditButton(),this._renderDeleteButton()])},_renderEditButton:function(){if(this.model.get("state")===a.DISCARDED||!this.model.get("accessible"))return null;var b=this.model.get("purged"),c=this.model.get("deleted"),d={title:g("Edit attributes"),href:this.model.urls.edit,target:this.linkTarget,faIcon:"fa-pencil",classes:"edit-btn"};return c||b?(d.disabled=!0,b?d.title=g("Cannot edit attributes of datasets removed from disk"):c&&(d.title=g("Undelete dataset to edit attributes"))):_.contains([a.UPLOAD,a.NEW],this.model.get("state"))&&(d.disabled=!0,d.title=g("This dataset is not yet editable")),e(d)},_renderDeleteButton:function(){if(!this.model.get("accessible"))return null;var a=this,b=this.model.isDeletedOrPurged();return e({title:g(b?"Dataset is already deleted":"Delete"),disabled:b,faIcon:"fa-times",classes:"delete-btn",onclick:function(){a.$el.find(".icon-btn.delete-btn").trigger("mouseout"),a.model.delete()}})},_renderDetails:function(){var b=h.prototype._renderDetails.call(this),c=this.model.get("state");return!this.model.isDeletedOrPurged()&&_.contains([a.OK,a.FAILED_METADATA],c)&&(this._renderTags(b),this._renderAnnotation(b),this._makeDbkeyEditLink(b)),this._setUpBehaviors(b),b},_renderSecondaryActions:function(){var b=h.prototype._renderSecondaryActions.call(this);switch(this.model.get("state")){case a.UPLOAD:case a.NOT_VIEWABLE:return b;case a.ERROR:return b.unshift(this._renderErrButton()),b.concat([this._renderRerunButton()]);case a.OK:case a.FAILED_METADATA:return b.concat([this._renderRerunButton(),this._renderVisualizationsButton()])}return b.concat([this._renderRerunButton()])},_renderErrButton:function(){return e({title:g("View or report this error"),href:this.model.urls.report_error,classes:"report-error-btn",target:this.linkTarget,faIcon:"fa-bug"})},_renderRerunButton:function(){var a=this.model.get("creating_job"),b=this.model.urls.rerun;return this.model.get("rerunnable")?e({title:g("Run this job again"),href:this.model.urls.rerun,classes:"rerun-btn",target:this.linkTarget,faIcon:"fa-refresh",onclick:function(c){c.preventDefault(),require(["mvc/tool/tool-form"],function(c){var d=new c.View({job_id:a});d.deferred.execute(function(){d.options.model_class.startsWith("HyperBrowser")?galaxy_main.location=b:Galaxy.app.display(d)})})}}):void 0},_renderVisualizationsButton:function(){var a=this.model.get("visualizations");if(this.model.isDeletedOrPurged()||!this.hasUser||!this.model.hasData()||_.isEmpty(a))return null;if(!_.isObject(a[0]))return this.warn("Visualizations have been switched off"),null;var b=$(this.templates.visualizations(a,this));return b.find('[target="galaxy_main"]').attr("target",this.linkTarget),this._addScratchBookFn(b.find(".visualization-link").addBack(".visualization-link")),b},_addScratchBookFn:function(a){a.click(function(a){Galaxy.frame&&Galaxy.frame.active&&(Galaxy.frame.add({title:"Visualization",url:$(this).attr("href")}),a.preventDefault(),a.stopPropagation())})},_renderTags:function(a){if(this.hasUser){var b=this;this.tagsEditor=new c.TagsEditor({model:this.model,el:a.find(".tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){b.tagsEditorShown=!0},onhide:function(){b.tagsEditorShown=!1},$activator:e({title:g("Edit dataset tags"),classes:"tag-btn",faIcon:"fa-tags"}).appendTo(a.find(".actions .right"))}),this.tagsEditorShown&&this.tagsEditor.toggle(!0)}},_renderAnnotation:function(a){if(this.hasUser){var b=this;this.annotationEditor=new d.AnnotationEditor({model:this.model,el:a.find(".annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){b.annotationEditorShown=!0},onhide:function(){b.annotationEditorShown=!1},$activator:e({title:g("Edit dataset annotation"),classes:"annotate-btn",faIcon:"fa-comment"}).appendTo(a.find(".actions .right"))}),this.annotationEditorShown&&this.annotationEditor.toggle(!0)}},_makeDbkeyEditLink:function(a){if("?"===this.model.get("metadata_dbkey")&&!this.model.isDeletedOrPurged()){var b=$('?').attr("href",this.model.urls.edit).attr("target",this.linkTarget);a.find(".dbkey .value").replaceWith(b)}},events:_.extend(_.clone(h.prototype.events),{"click .undelete-link":"_clickUndeleteLink","click .purge-link":"_clickPurgeLink","click .edit-btn":function(a){this.trigger("edit",this,a)},"click .delete-btn":function(a){this.trigger("delete",this,a)},"click .rerun-btn":function(a){this.trigger("rerun",this,a)},"click .report-err-btn":function(a){this.trigger("report-err",this,a)},"click .visualization-btn":function(a){this.trigger("visualize",this,a)},"click .dbkey a":function(a){this.trigger("edit",this,a)}}),_clickUndeleteLink:function(){return this.model.undelete(),!1},_clickPurgeLink:function(){return this.model.purge(),!1},toString:function(){var a=this.model?this.model+"":"(no model)";return"HDAEditView("+a+")"}});return i.prototype.templates=function(){var a=_.extend({},h.prototype.templates.warnings,{failed_metadata:f.wrapTemplate(['<% if( dataset.state === "failed_metadata" ){ %>','","<% } %>"],"dataset"),deleted:f.wrapTemplate(["<% if( dataset.deleted && !dataset.purged ){ %>",'
',g("This dataset has been deleted"),'
',g("Undelete it"),"","<% if( view.purgeAllowed ){ %>",'
',g("Permanently remove it from disk"),"","<% } %>","
","<% } %>"],"dataset")}),b=f.wrapTemplate(["<% if( visualizations.length === 1 ){ %>",'">','',"","<% } else { %>",'","<% } %>"],"visualizations");return _.extend({},h.prototype.templates,{warnings:a,visualizations:b})}(),{DatasetListItemEdit:i}}); +define(["mvc/dataset/states","mvc/dataset/dataset-li","mvc/tag","mvc/annotation","ui/fa-icon-button","mvc/base-mvc","utils/localization"],function(a,b,c,d,e,f,g){var h=b.DatasetListItemView,i=h.extend({initialize:function(a){h.prototype.initialize.call(this,a),this.hasUser=a.hasUser,this.purgeAllowed=a.purgeAllowed||!1,this.tagsEditorShown=a.tagsEditorShown||!1,this.annotationEditorShown=a.annotationEditorShown||!1},_renderPrimaryActions:function(){var b=h.prototype._renderPrimaryActions.call(this);return this.model.get("state")===a.NOT_VIEWABLE?b:h.prototype._renderPrimaryActions.call(this).concat([this._renderEditButton(),this._renderDeleteButton()])},_renderEditButton:function(){if(this.model.get("state")===a.DISCARDED||!this.model.get("accessible"))return null;var b=this.model.get("purged"),c=this.model.get("deleted"),d={title:g("Edit attributes"),href:this.model.urls.edit,target:this.linkTarget,faIcon:"fa-pencil",classes:"edit-btn"};return c||b?(d.disabled=!0,b?d.title=g("Cannot edit attributes of datasets removed from disk"):c&&(d.title=g("Undelete dataset to edit attributes"))):_.contains([a.UPLOAD,a.NEW],this.model.get("state"))&&(d.disabled=!0,d.title=g("This dataset is not yet editable")),e(d)},_renderDeleteButton:function(){if(!this.model.get("accessible"))return null;var a=this,b=this.model.isDeletedOrPurged();return e({title:g(b?"Dataset is already deleted":"Delete"),disabled:b,faIcon:"fa-times",classes:"delete-btn",onclick:function(){a.$el.find(".icon-btn.delete-btn").trigger("mouseout"),a.model.delete()}})},_renderDetails:function(){var b=h.prototype._renderDetails.call(this),c=this.model.get("state");return!this.model.isDeletedOrPurged()&&_.contains([a.OK,a.FAILED_METADATA],c)&&(this._renderTags(b),this._renderAnnotation(b),this._makeDbkeyEditLink(b)),this._setUpBehaviors(b),b},_renderSecondaryActions:function(){var b=h.prototype._renderSecondaryActions.call(this);switch(this.model.get("state")){case a.UPLOAD:case a.NOT_VIEWABLE:return b;case a.ERROR:return b.unshift(this._renderErrButton()),b.concat([this._renderRerunButton()]);case a.OK:case a.FAILED_METADATA:return b.concat([this._renderRerunButton(),this._renderVisualizationsButton()])}return b.concat([this._renderRerunButton()])},_renderErrButton:function(){return e({title:g("View or report this error"),href:this.model.urls.report_error,classes:"report-error-btn",target:this.linkTarget,faIcon:"fa-bug"})},_renderRerunButton:function(){var a=this.model.get("creating_job"),b=this.model.urls.rerun;return this.model.get("rerunnable")?e({title:g("Run this job again"),href:this.model.urls.rerun,classes:"rerun-btn",target:this.linkTarget,faIcon:"fa-refresh",onclick:function(c){c.preventDefault(),require(["mvc/tool/tool-form"],function(c){var d=new c.View({job_id:a});d.deferred.execute(function(){0===d.options.model_class.lastIndexOf("Proto",0)?galaxy_main.location=b:Galaxy.app.display(d)})})}}):void 0},_renderVisualizationsButton:function(){var a=this.model.get("visualizations");if(this.model.isDeletedOrPurged()||!this.hasUser||!this.model.hasData()||_.isEmpty(a))return null;if(!_.isObject(a[0]))return this.warn("Visualizations have been switched off"),null;var b=$(this.templates.visualizations(a,this));return b.find('[target="galaxy_main"]').attr("target",this.linkTarget),this._addScratchBookFn(b.find(".visualization-link").addBack(".visualization-link")),b},_addScratchBookFn:function(a){a.click(function(a){Galaxy.frame&&Galaxy.frame.active&&(Galaxy.frame.add({title:"Visualization",url:$(this).attr("href")}),a.preventDefault(),a.stopPropagation())})},_renderTags:function(a){if(this.hasUser){var b=this;this.tagsEditor=new c.TagsEditor({model:this.model,el:a.find(".tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){b.tagsEditorShown=!0},onhide:function(){b.tagsEditorShown=!1},$activator:e({title:g("Edit dataset tags"),classes:"tag-btn",faIcon:"fa-tags"}).appendTo(a.find(".actions .right"))}),this.tagsEditorShown&&this.tagsEditor.toggle(!0)}},_renderAnnotation:function(a){if(this.hasUser){var b=this;this.annotationEditor=new d.AnnotationEditor({model:this.model,el:a.find(".annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){b.annotationEditorShown=!0},onhide:function(){b.annotationEditorShown=!1},$activator:e({title:g("Edit dataset annotation"),classes:"annotate-btn",faIcon:"fa-comment"}).appendTo(a.find(".actions .right"))}),this.annotationEditorShown&&this.annotationEditor.toggle(!0)}},_makeDbkeyEditLink:function(a){if("?"===this.model.get("metadata_dbkey")&&!this.model.isDeletedOrPurged()){var b=$('?').attr("href",this.model.urls.edit).attr("target",this.linkTarget);a.find(".dbkey .value").replaceWith(b)}},events:_.extend(_.clone(h.prototype.events),{"click .undelete-link":"_clickUndeleteLink","click .purge-link":"_clickPurgeLink","click .edit-btn":function(a){this.trigger("edit",this,a)},"click .delete-btn":function(a){this.trigger("delete",this,a)},"click .rerun-btn":function(a){this.trigger("rerun",this,a)},"click .report-err-btn":function(a){this.trigger("report-err",this,a)},"click .visualization-btn":function(a){this.trigger("visualize",this,a)},"click .dbkey a":function(a){this.trigger("edit",this,a)}}),_clickUndeleteLink:function(){return this.model.undelete(),!1},_clickPurgeLink:function(){return this.model.purge(),!1},toString:function(){var a=this.model?this.model+"":"(no model)";return"HDAEditView("+a+")"}});return i.prototype.templates=function(){var a=_.extend({},h.prototype.templates.warnings,{failed_metadata:f.wrapTemplate(['<% if( dataset.state === "failed_metadata" ){ %>','","<% } %>"],"dataset"),deleted:f.wrapTemplate(["<% if( dataset.deleted && !dataset.purged ){ %>",'
',g("This dataset has been deleted"),'
',g("Undelete it"),"","<% if( view.purgeAllowed ){ %>",'
',g("Permanently remove it from disk"),"","<% } %>","
","<% } %>"],"dataset")}),b=f.wrapTemplate(["<% if( visualizations.length === 1 ){ %>",'">','',"","<% } else { %>",'","<% } %>"],"visualizations");return _.extend({},h.prototype.templates,{warnings:a,visualizations:b})}(),{DatasetListItemEdit:i}}); //# sourceMappingURL=../../../maps/mvc/dataset/dataset-li-edit.js.map \ No newline at end of file From 1537a17fd510a5fd3265c8a494aa8df2b5c673c8 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Sun, 14 Jan 2018 20:38:50 +0100 Subject: [PATCH 089/123] Bugfix --- scripts/common_startup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/common_startup.sh b/scripts/common_startup.sh index a3318ddecc1b..b25c347716c3 100755 --- a/scripts/common_startup.sh +++ b/scripts/common_startup.sh @@ -127,7 +127,7 @@ if [ $SET_VENV -eq 1 ]; then then printf "Setting up R in virtualenv at $GALAXY_VIRTUAL_ENV\n" - echo '\nexport R_LIBS=$VIRTUAL_ENV/R/library' >>$GALAXY_VIRTUAL_ENV/bin/activate + printf '\nexport R_LIBS=$VIRTUAL_ENV/R/library' >>$GALAXY_VIRTUAL_ENV/bin/activate for LIB in $GALAXY_VIRTUAL_ENV/lib/python* do echo 'import os,sys; os.environ["R_LIBS"]=sys.prefix+"/R/library"' >$LIB/sitecustomize.py From 4729030a76217f0621ba1d38a751d8fbec3bd6d0 Mon Sep 17 00:00:00 2001 From: Sveinung Gundersen Date: Thu, 18 Jan 2018 13:50:42 +0100 Subject: [PATCH 090/123] Ran webpack using Node.js v9.4.0 and npm v5.6.0 --- static/scripts/bundled/analysis.bundled.js | 24 ++++++----- .../scripts/bundled/analysis.bundled.js.map | 2 +- static/scripts/bundled/libs.bundled.js | 40 +++++++++---------- static/scripts/bundled/libs.bundled.js.map | 2 +- static/scripts/bundled/login.bundled.js.map | 2 +- 5 files changed, 36 insertions(+), 34 deletions(-) diff --git a/static/scripts/bundled/analysis.bundled.js b/static/scripts/bundled/analysis.bundled.js index c241a3a09add..554823f35155 100644 --- a/static/scripts/bundled/analysis.bundled.js +++ b/static/scripts/bundled/analysis.bundled.js @@ -1,12 +1,14 @@ -webpackJsonp([3,1],[function(t,e,i){(function(t,e){var n=i(1),s=n,o=i(82).GalaxyApp,a=i(61),r=i(13),l=i(113),c=i(112),d=i(84),h=i(22);window.app=function(i,n){window.Galaxy=new o(i,n),Galaxy.debug("analysis app");var u=i.config,p=new l({el:"#left",userIsAnonymous:Galaxy.user.isAnonymous(),spinner_url:u.spinner_url,search_url:u.search_url,toolbox:u.toolbox,toolbox_in_panel:u.toolbox_in_panel,stored_workflow_menu_entries:u.stored_workflow_menu_entries,nginx_upload_path:u.nginx_upload_path,ftp_upload_site:u.ftp_upload_site,default_genome:u.default_genome,default_extension:u.default_extension}),f=new r.CenterPanel({el:"#center"}),g=new c({el:"#right",galaxyRoot:Galaxy.root,userIsAnonymous:Galaxy.user.isAnonymous(),allow_user_dataset_purge:u.allow_user_dataset_purge}),m=new d.PageLayoutView(t.extend(i,{el:"body",left:p,center:f,right:g}));Galaxy.page=m,Galaxy.params=Galaxy.config.params,Galaxy.toolPanel=p.tool_panel,Galaxy.upload=p.uploadButton,Galaxy.currHistoryPanel=g.historyView,Galaxy.currHistoryPanel.listenToGalaxy(Galaxy),Galaxy.app={display:function(t,e){s(".select2-hidden-accessible").remove(),f.display(t)}};new(e.Router.extend({initialize:function(t){this.options=t},execute:function(t,e,i){Galaxy.debug("router execute:",t,e,i);var n=a.parse(e.pop());e.push(n),t&&t.apply(this,e)},routes:{"(/)":"home","(/)root*":"home"},home:function(t){(t.tool_id||t.job_id)&&"upload1"!==t.tool_id?this._loadToolForm(t):t.workflow_id?this._loadCenterIframe("workflow/run?id="+t.workflow_id):t.m_c?this._loadCenterIframe(t.m_c+"/"+t.m_a):this._loadCenterIframe("welcome")},_loadToolForm:function(t){t.id=t.tool_id,f.display(new h.View(t))},_loadCenterIframe:function(t,e){e=e||Galaxy.root,t=e+t,f.$("#galaxy_main").prop("src",t)}}))(i);s(function(){m.render().right.historyView.loadCurrentHistory(),Galaxy.listenTo(m.right.historyView,"history-size-change",function(){Galaxy.user.fetch({url:Galaxy.user.urlRoot()+"/"+(Galaxy.user.id||"current")})}),m.right.historyView.connectToQuotaMeter(m.masthead.quotaMeter),e.history.start({root:Galaxy.root,pushState:!0})})}}).call(e,i(3),i(2))},,,,,,,function(t,e,i){var n,s;(function(o,a){n=[i(4),i(54),i(57),i(25),i(53),i(16),i(10)],s=function(t,e,i,n,s,r,l){var c=o.View.extend({initialize:function(e){this.options=t.merge(e,{url:"",cls:""}),this.setElement(this._template(this.options))},_template:function(t){return''}}),d=o.View.extend({initialize:function(e){this.options=t.merge(e,{title:"",cls:"",tagname:"label"}),this.setElement(this._template(this.options))},title:function(t){this.$el.html(t)},value:function(){return options.title},_template:function(t){return a("<"+t.tagname+"/>").addClass("ui-label").addClass(t.cls).html(t.title)}}),h=o.View.extend({initialize:function(e){this.options=t.merge(e,{floating:"right",icon:"",tooltip:"",placement:"bottom",title:"",cls:""}),this.setElement(this._template(this.options)),a(this.el).tooltip({title:e.tooltip,placement:"bottom"})},_template:function(t){return'
 '+t.title+"
"}}),u=o.View.extend({initialize:function(e){this.options=t.merge(e,{title:"",cls:""}),this.setElement(this._template(this.options)),a(this.el).on("click",e.onclick)},_template:function(t){return'"}}),p=o.View.extend({initialize:function(e){this.options=t.merge(e,{message:null,status:"info",cls:"",persistent:!1}),this.setElement('
'),this.options.message&&this.update(this.options)},update:function(e){if(this.options=t.merge(e,this.options),""!=e.message){if(this.$el.html(this._template(this.options)),this.$el.fadeIn(),this.timeout&&window.clearTimeout(this.timeout),!e.persistent){var i=this;this.timeout=window.setTimeout(function(){i.$el.is(":visible")?i.$el.fadeOut():i.$el.hide()},3e3)}}else this.$el.fadeOut()},_template:function(t){var e="ui-message alert alert-"+t.status;return t.large&&(e=("success"==t.status&&"done"||"danger"==t.status&&"error"||t.status)+"messagelarge"),'
'+t.message+"
"}}),f=o.View.extend({initialize:function(e){this.options=t.merge(e,{onclick:null,searchword:""}),this.setElement(this._template(this.options));var i=this;this.options.onclick&&this.$el.on("submit",function(t){var e=i.$el.find("#search");i.options.onclick(e.val())})},_template:function(t){return''}}),g=o.View.extend({initialize:function(e){this.options=t.merge(e,{type:"text",placeholder:"",disabled:!1,visible:!0,cls:"",area:!1}),this.setElement(this._template(this.options)),void 0!==this.options.value&&this.value(this.options.value),this.options.disabled&&this.$el.prop("disabled",!0),this.options.visible||this.$el.hide();var i=this;this.$el.on("input",function(){i.options.onchange&&i.options.onchange(i.$el.val())})},value:function(t){return void 0!==t&&this.$el.val("string"==typeof t&&t||""),this.$el.val()},_template:function(t){return t.area?'':''}}),m=o.View.extend({initialize:function(t){this.options=t,this.setElement(this._template(this.options)),void 0!==this.options.value&&this.value(this.options.value)},value:function(t){return void 0!==t&&this.$("hidden").val(t),this.$("hidden").val()},_template:function(t){var e='
';return t.info&&(e+="
"+t.info+"
"),e+='
'}});return{Anchor:u,Button:r.ButtonDefault,ButtonIcon:r.ButtonIcon,ButtonCheck:r.ButtonCheck,ButtonMenu:r.ButtonMenu,ButtonLink:r.ButtonLink,Icon:h,Image:c,Input:g,Label:d,Message:p,Modal:l,RadioButton:n.RadioButton,Checkbox:n.Checkbox,Radio:n.Radio,Searchbox:f,Select:e,Hidden:m,Slider:i,Drilldown:s}}.apply(e,n),!(void 0!==s&&(t.exports=s))}).call(e,i(2),i(1))},function(t,e){"use strict";function i(t){return d[t]}function n(t){for(var e=1;ei;i++)if(t[i]===e)return i;return-1}function o(t){if("string"!=typeof t){if(t&&t.toHTML)return t.toHTML();if(null==t)return"";if(!t)return t+"";t=""+t}return u.test(t)?t.replace(h,i):t}function a(t){return t||0===t?!(!g(t)||0!==t.length):!0}function r(t){var e=n({},t);return e._parent=t,e}function l(t,e){return t.path=e,t}function c(t,e){return(t?t+".":"")+e}e.__esModule=!0,e.extend=n,e.indexOf=s,e.escapeExpression=o,e.isEmpty=a,e.createFrame=r,e.blockParams=l,e.appendContextPath=c;var d={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`","=":"="},h=/[&<>"'`=]/g,u=/[&<>"'`=]/,p=Object.prototype.toString;e.toString=p;var f=function(t){return"function"==typeof t};f(/x/)&&(e.isFunction=f=function(t){return"function"==typeof t&&"[object Function]"===p.call(t)}),e.isFunction=f;var g=Array.isArray||function(t){return t&&"object"==typeof t?"[object Array]"===p.call(t):!1};e.isArray=g},function(t,e){"use strict";function i(t,e){var s=e&&e.loc,o=void 0,a=void 0;s&&(o=s.start.line,a=s.start.column,t+=" - "+o+":"+a);for(var r=Error.prototype.constructor.call(this,t),l=0;l';return t.title&&(e+='
',t.icon&&(e+=' '),e+=''+t.title+"
"),e+='
',"top"==t.placement&&(e+='
'),e+='
',"bottom"==t.placement&&(e+='
'),e+='