Merge pull request #5103 from guerler/remove_sample_tracking

Remove sample tracking backend support, preserve data tables
This commit is contained in:
Martin Cech
2017-12-01 13:46:30 -05:00
committed by GitHub
34 changed files with 8 additions and 3428 deletions
@@ -1,5 +0,0 @@
<?xml version="1.0"?>
<external_service_types>
<external_service_type file="simple_unknown_sequencer.xml" visible="False"/>
<external_service_type file="applied_biosystems_solid.xml" visible="True"/>
</external_service_types>
-9
View File
@@ -425,15 +425,6 @@ paste.app_factory = galaxy.web.buildapp:app_factory
#citation_cache_data_dir = database/citations/data
#citation_cache_lock_dir = database/citations/lock
# External service types config file, defining what types of external_services
# configurations are available in Galaxy (.sample is used if default does not
# exist).
#external_service_type_config_file = config/external_service_types_conf.xml
# Path to the directory containing the external_service_types defined in the
# config.
#external_service_type_path = external_service_types
# Tools with a number of outputs not known until runtime can write these
# outputs to a directory for collection by Galaxy when the job is done.
# Previously, this directory was new_file_path, but using one global directory
@@ -1,26 +0,0 @@
<sequencer_type id="454_life_sciences" name="454 Life Sciences" version="1.0.0">
<description></description>
<version>1</version>
<data_transfer_settings>
<data_transfer protocol='scp' automatic_transfer='False' host='host' user_name='user_name' password='password' data_location='data_location' />
</data_transfer_settings>
<run_details>
<results>
<dataset name="dataset1_name" datatype="dataset1_datatype" />
</results>
</run_details>
<form type="external_service" name="Applied Biosystems SOLiD Form" description="">
<fields>
<field name="host" type="text" label="Hostname or IP address" description="" value="" required="True" />
<field name="user_name" type="text" label="User name" description="User name to log into the sequencer." value="" required="True" />
<field name="password" type="password" label="Password" description="" value="" required="True" />
<field name="data_location" type="text" label="Data directory" description="" value="" required="False" />
<field name="dataset1_name" type="text" label="Sample run output 1" description="" value="SFF file" required="True" />
<field name="dataset1_datatype" type="text" label="Sample run datatype 1" description="" value="sff" required="True" />
</fields>
</form>
</sequencer_type>
@@ -1,38 +0,0 @@
<sequencer_type id="applied_biosystems_solid" name="Applied Biosystems SOLiD" version="1.0.0">
<description></description>
<version>3</version>
<data_transfer_settings>
<data_transfer protocol='scp' automatic_transfer='False' host='host' user_name='user_name' password='password' data_location='data_location' rename_dataset='rename_dataset' />
</data_transfer_settings>
<run_details>
<results>
<dataset name="dataset1_name" datatype="dataset1_datatype" />
<dataset name="dataset2_name" datatype="dataset2_datatype" />
<dataset name="dataset3_name" datatype="dataset3_datatype" />
</results>
</run_details>
<form type="external_service" name="Applied Biosystems SOLiD Form" description="">
<fields>
<field name="host" type="text" label="Hostname or IP address" description="" value="" required="True" />
<field name="user_name" type="text" label="User name" description="User name to log into the sequencer." value="" required="True" />
<field name="password" type="password" label="Password" description="" value="" required="True" />
<field name="data_location" type="text" label="Data directory" description="" value="" required="False" />
<field name="rename_dataset" type="select" label="Prepend the experiment name and sample name to the dataset name?" description="Galaxy datasets are renamed by prepending the experiment name and sample name to the dataset name, ensuring dataset names remain unique in Galaxy even when multiple datasets have the same name on the sequencer." value="">
<option value="Do not rename" />
<option value="Preprend sample name" />
<option value="Prepend experiment name" />
<option value="Prepend experiment and sample name" />
</field>
<field name="dataset1_name" type="text" label="Sample run output 1" description="" value="Colorspace FASTA file" required="True" />
<field name="dataset1_datatype" type="text" label="Sample run datatype 1" description="" value="csfasta" required="True" />
<field name="dataset2_name" type="text" label="Sample run output 2" description="" value="Quality file" required="True" />
<field name="dataset2_datatype" type="text" label="Sample run datatype 2" description="" value="qual" required="True" />
<field name="dataset3_name" type="text" label="Sample run output 3" description="" value="STATS file" required="True" />
<field name="dataset3_datatype" type="text" label="Sample run datatype 3" description="" value="txt" required="True" />
</fields>
</form>
</sequencer_type>
@@ -1,134 +0,0 @@
<external_service id="pacific_biosciences_smrt_portal" name="Pacific Biosciences SMRT Portal" version="1.1.0">
<description></description>
<version>1</version>
<data_transfer_settings>
<data_transfer protocol='http' automatic_transfer='True' />
</data_transfer_settings>
<run_details>
<results>
<dataset name="dataset2_name" datatype="dataset2_datatype"
url="http://${host}/smrtportal/api/Jobs/${secondary_analysis_job_id}/Contents/data/filtered_subreads.fa" />
<dataset name="dataset3_name" datatype="dataset3_datatype"
url="http://${host}/smrtportal/api/Jobs/${secondary_analysis_job_id}/Contents/data/aligned_reads.bam" />
<dataset name="dataset4_name" datatype="dataset4_datatype"
url="http://${host}/smrtportal/api/Jobs/${secondary_analysis_job_id}/Contents/data/alignment_summary.gff" />
<dataset name="dataset5_name" datatype="dataset5_datatype"
url="http://${host}/smrtportal/api/Jobs/${secondary_analysis_job_id}/Contents/data/coverage.bed" />
<dataset name="dataset6_name" datatype="dataset6_datatype"
url="http://${host}/smrtportal/api/Jobs/${secondary_analysis_job_id}/Contents/data/variants.bed" />
<dataset name="dataset7_name" datatype="dataset7_datatype"
url="http://${host}/smrtportal/api/Jobs/${secondary_analysis_job_id}/Contents/data/variants.gff.gz" />
</results>
</run_details>
<form type="external_service" name="Pacific Biosciences SMRT Portal Form" description="">
<fields>
<field name="host" type="text" label="Hostname or IP address" description="" value="192.168.56.101" required="True" />
<field name="user_name" type="text" label="User name" description="User name to log into the sequencer." value="administrator" required="True" />
<field name="password" type="password" label="Password" description="" value="galaxy" required="True" />
<field name="data_location" type="text" label="Data directory" description="" value="" required="False" />
<field name="dataset2_name" type="text" label="Filtered reads" description="" value="Filtered reads" required="True" />
<field name="dataset2_datatype" type="text" label="Format" description="" value="fasta" required="True" />
<field name="dataset3_name" type="text" label="Aligned reads bam" description="" value="Aligned reads" required="True" />
<field name="dataset3_datatype" type="text" label="Format" description="" value="bam" required="True" />
<field name="dataset4_name" type="text" label="Coverage gff" description="" value="Coverage (gff)" required="True" />
<field name="dataset4_datatype" type="text" label="Format" description="" value="gff" required="True" />
<field name="dataset5_name" type="text" label="Coverage bed" description="" value="Coverage (bed)" required="True" />
<field name="dataset5_datatype" type="text" label="Format" description="" value="bed" required="True" />
<field name="dataset6_name" type="text" label="Variants bed" description="" value="Variants (bed)" required="True" />
<field name="dataset6_datatype" type="text" label="Format" description="" value="bed" required="True" />
<field name="dataset7_name" type="text" label="Variants gff" description="" value="Variants (gff)" required="True" />
<field name="dataset7_datatype" type="text" label="Format" description="" value="gff" required="True" />
</fields>
</form>
<actions>
<param name="api_url" type="template">http://${fields.host}/smrtportal/api</param>
<param name="web_url" type="template">http://${fields.host}/smrtportal/#</param>
<section name="jobs" label="Job Service">
<param name="jobs_url" type="template">${api_url}/Jobs</param>
<param name="monitor_jobs_url" type="template">${web_url}/MonitorJobs</param>
<!--
<action type="web_api" name="jobs_list" label="List Jobs" description="">
<request target="galaxy_main" method="post">
<url>${jobs.jobs_url}</url>
</request>
<result_handler type="jquery_grid"></result_handler>
</action>
-->
<conditional name="job_sample" label="Sample to Job Service" ref="item">
<when type="item_type" name="sample" value="sample">
<param name="secondary_analysis_job_id" type="template">${item.run_details.run.info.content.get( 'secondary_analysis_job_id' )}</param>
<conditional name="valid_job_sample" ref="jobs.job_sample.sample.secondary_analysis_job_id">
<when type="boolean" name="valid">
<param name="job_sample_url" type="template">${jobs.jobs_url}/${jobs.job_sample.sample.secondary_analysis_job_id}</param>
<param name="monitor_job_url" type="template">${jobs.monitor_jobs_url}/DetailsOfJob/${jobs.job_sample.sample.secondary_analysis_job_id}</param>
<action type="web" name="monitor_job" label="View Job in SMRT Portal" description="">
<request target="_blank" method="get">
<url>${jobs.job_sample.sample.valid_job_sample.valid.monitor_job_url}</url>
</request>
</action>
<action type="web_api" name="job_status" label="Job status" description="">
<request target="galaxy_main" method="post">
<url>${jobs.job_sample.sample.valid_job_sample.valid.job_sample_url}/Status</url>
</request>
<result_handler type="json_display"></result_handler>
</action>
<action type="web_api" name="job_history" label="Job history" description="">
<request target="galaxy_main" method="post">
<url>${jobs.job_sample.sample.valid_job_sample.valid.job_sample_url}/History</url>
</request>
<result_handler type="jquery_grid"></result_handler>
</action>
<action type="web_api" name="job_log" label="Job log" description="">
<request target="galaxy_main" method="post">
<url>${jobs.job_sample.sample.valid_job_sample.valid.job_sample_url}/Log</url>
</request>
<result_handler type="display"></result_handler>
</action>
<action type="web_api" name="job_contents" label="Job contents" description="">
<request target="galaxy_main" method="post">
<url>${jobs.job_sample.sample.valid_job_sample.valid.job_sample_url}/Contents</url>
</request>
<result_handler type="json_display"></result_handler>
</action>
<!--
<action type="web_api" name="job_protocol" label="Job protocol" description="">
<request target="galaxy_main" method="post">
<url>${jobs.job_sample.sample.valid_job_sample.valid.job_sample_url}/Protocol</url>
</request>
<result_handler type="display"></result_handler>
</action>
-->
<action type="web_api" name="job_inputs" label="Job inputs" description="">
<request target="galaxy_main" method="post">
<url>${jobs.job_sample.sample.valid_job_sample.valid.job_sample_url}/Inputs</url>
</request>
<result_handler type="jquery_grid"></result_handler>
</action>
<action type="template" name="job_smrt_view" label="Job SMRT View" description="">
<template>${jobs.job_sample.sample.valid_job_sample.valid.job_sample_url}/Contents/vis.jnlp</template>
<result_handler type="web_redirect"></result_handler>
</action>
</when>
</conditional>
</when>
</conditional>
</section>
</actions>
</external_service>
@@ -1,26 +0,0 @@
<!--
DEPRECATED
This sequencer type should not be used to create new sequencers.
This is only for backward compatibility
-->
<sequencer_type id="simple_unknown_sequencer" name="Simple unknown sequencer" version="1.0.0">
<description></description>
<version></version>
<data_transfer_settings>
<data_transfer protocol='scp' automatic_transfer='False' host='host' user_name='user_name' password='password' data_location='data_location'/>
</data_transfer_settings>
<form type="external_service" name="Simple unknown sequencer form" description="">
<fields>
<field name="host" type="text" label="Hostname or IP address" description="" value="" required="True" />
<field name="user_name" type="text" label="User name" description="User name to log into the sequencer." value="" required="True" />
<field name="password" type="password" label="Password" description="" value="" required="True" />
<field name="data_location" type="text" label="Data directory" description="" value="" required="False" />
<field name="rename_dataset" type="select" label="Prepend the experiment name and sample name to the dataset name?" description="Galaxy datasets are renamed by prepending the experiment name and sample name to the dataset name, ensuring dataset names remain unique in Galaxy even when multiple datasets have the same name on the sequencer." value="">
<option value="Do not rename" />
<option value="Preprend sample name" />
<option value="Prepend experiment name" />
<option value="Prepend experiment and sample name" />
</field>
</fields>
</form>
</sequencer_type>
-5
View File
@@ -15,7 +15,6 @@ from galaxy.managers.collections import DatasetCollectionManager
from galaxy.managers.tags import GalaxyTagManager
from galaxy.openid.providers import OpenIDProviders
from galaxy.queue_worker import GalaxyQueueWorker
from galaxy.sample_tracking import external_service_types
from galaxy.tools.cache import (
ToolCache,
ToolShedRepositoryCache
@@ -192,10 +191,6 @@ class UniverseApplication(object, config.ConfiguresGalaxyMixin):
self.job_manager = manager.JobManager(self)
self.application_stack.register_postfork_function(self.job_manager.start)
self.proxy_manager = ProxyManager(self.config)
# Initialize the external service types
self.external_service_types = external_service_types.ExternalServiceTypesCollection(
self.config.external_service_type_config_file,
self.config.external_service_type_path, self)
from galaxy.workflow import scheduling_manager
# Must be initialized after job_config.
-3
View File
@@ -41,7 +41,6 @@ PATH_DEFAULTS = dict(
data_manager_config_file=['config/data_manager_conf.xml', 'data_manager_conf.xml', 'config/data_manager_conf.xml.sample'],
datatypes_config_file=['config/datatypes_conf.xml', 'datatypes_conf.xml', 'config/datatypes_conf.xml.sample'],
build_sites_config_file=['config/build_sites.yml', 'config/build_sites.yml.sample'],
external_service_type_config_file=['config/external_service_types_conf.xml', 'external_service_types_conf.xml', 'config/external_service_types_conf.xml.sample'],
job_config_file=['config/job_conf.xml', 'job_conf.xml'],
tool_destinations_config_file=['config/tool_destinations.yml', 'config/tool_destinations.yml.sample'],
job_metrics_config_file=['config/job_metrics_conf.xml', 'job_metrics_conf.xml', 'config/job_metrics_conf.xml.sample'],
@@ -350,8 +349,6 @@ class Configuration(object):
self.persistent_communication_rooms = listify(kwargs.get("persistent_communication_rooms", []), do_strip=True)
self.enable_openid = string_as_bool(kwargs.get('enable_openid', 'False'))
self.enable_quotas = string_as_bool(kwargs.get('enable_quotas', 'False'))
# External Service types used in sample tracking
self.external_service_type_path = resolve_path(kwargs.get('external_service_type_path', 'external_service_types'), self.root)
# Tasked job runner.
self.use_tasked_jobs = string_as_bool(kwargs.get('use_tasked_jobs', False))
self.local_task_queue_workers = int(kwargs.get("local_task_queue_workers", 2))
-195
View File
@@ -1,195 +0,0 @@
# Contains actions that are used in External Services
import logging
import requests
from galaxy.util.template import fill_template
from galaxy.web import url_for
from .result_handlers.basic import ExternalServiceActionResultHandler
log = logging.getLogger(__name__)
class PopulatedExternalServiceAction(object):
def __init__(self, action, param_dict):
self.action = action
self.param_dict = param_dict
self.result = None
self.handled_results = None
def __getattr__(self, name):
return getattr(self.action, name)
def get_action_access_link(self, trans):
return self.action.get_action_access_link(trans, self.param_dict)
def perform_action(self):
if self.result is None:
self.result = self.action.perform_action(self.param_dict)
return self.result
def handle_results(self, trans):
if self.result is None:
self.perform_action()
if self.handled_results is None:
self.handled_results = self.action.handle_action(self.result, self.param_dict, trans)
return self.handled_results
class ExternalServiceAction(object):
""" Abstract Class for External Service Actions """
type = None
@classmethod
def from_elem(cls, elem, parent):
action_type = elem.get('type', None)
assert action_type, 'ExternalServiceAction requires a type'
return action_type_to_class[action_type](elem, parent)
def __init__(self, elem, parent):
self.name = elem.get('name', None)
assert self.name, 'ExternalServiceAction requires a name'
self.label = elem.get('label', self.name)
self.parent = parent
self.result_handlers = []
for handler in elem.findall('result_handler'):
self.result_handlers.append(ExternalServiceActionResultHandler.from_elem(handler, self))
def __action_url_id(self, param_dict):
rval = self.name
parent = self.parent
while hasattr(parent.parent, 'parent'):
rval = "%s|%s" % (parent.name, rval)
parent = parent.parent
rval = "%s|%s" % (param_dict['service_instance'].id, rval)
return rval
def get_action_access_link(self, trans, param_dict):
return url_for(controller='/external_services',
action="access_action",
external_service_action=self.__action_url_id(param_dict),
item=param_dict['item'].id,
item_type=param_dict['item'].__class__.__name__)
def populate_action(self, param_dict):
return PopulatedExternalServiceAction(self, param_dict)
def handle_action(self, completed_action, param_dict, trans):
handled_results = []
for handled_result in self.result_handlers:
handled_results.append(handled_result.handle_result(completed_action, param_dict, trans))
return handled_results
def perform_action(self, param_dict):
raise Exception('Abstract Method')
class ExternalServiceResult(object):
def __init__(self, name, param_dict):
self.name = name
self.param_dict = param_dict
@property
def content(self):
raise Exception('Abstract Method')
class ExternalServiceWebAPIActionResult(ExternalServiceResult):
def __init__(self, name, param_dict, url, method, target): # display_handler = None ):
ExternalServiceResult.__init__(self, name, param_dict)
self.url = url
self.method = method
self.target = target
self._content = None
@property
def content(self):
if self._content is None:
self._content = requests.get(self.url).text
return self._content
class ExternalServiceValueResult(ExternalServiceResult):
def __init__(self, name, param_dict, value):
self.name = name
self.param_dict = param_dict
self.value = value
@property
def content(self):
return self.value
class ExternalServiceWebAPIAction(ExternalServiceAction):
""" Action that accesses an external Web API and provides handlers for the requested content """
type = 'web_api'
class ExternalServiceWebAPIActionRequest(object):
def __init__(self, elem, parent):
self.target = elem.get('target', '_blank')
self.method = elem.get('method', 'post')
self.parent = parent
self.url = Template(elem.find('url'), parent)
def get_web_api_action(self, param_dict):
name = self.parent.name
target = self.target
method = self.method
url = self.url.build_template(param_dict).strip()
return ExternalServiceWebAPIActionResult(name, param_dict, url, method, target)
def __init__(self, elem, parent):
ExternalServiceAction.__init__(self, elem, parent)
self.web_api_request = self.ExternalServiceWebAPIActionRequest(elem.find('request'), parent)
def perform_action(self, param_dict):
return self.web_api_request.get_web_api_action(param_dict)
class ExternalServiceWebAction(ExternalServiceAction):
""" Action that accesses an external web application """
type = 'web'
def __init__(self, elem, parent):
ExternalServiceAction.__init__(self, elem, parent)
self.request_elem = elem.find('request')
self.url = Template(self.request_elem.find('url'), parent)
self.target = self.request_elem.get('target', '_blank')
self.method = self.request_elem.get('method', 'get')
def get_action_access_link(self, trans, param_dict):
url = self.url.build_template(param_dict).strip()
return url
class ExternalServiceTemplateAction(ExternalServiceAction):
""" Action that redirects to an external URL """
type = 'template'
def __init__(self, elem, parent):
ExternalServiceAction.__init__(self, elem, parent)
self.template = Template(elem.find('template'), parent)
def perform_action(self, param_dict):
return ExternalServiceValueResult(self.name, param_dict, self.template.build_template(param_dict))
action_type_to_class = {
ExternalServiceWebAction.type: ExternalServiceWebAction,
ExternalServiceWebAPIAction.type: ExternalServiceWebAPIAction,
ExternalServiceTemplateAction.type: ExternalServiceTemplateAction}
# utility classes
class Template(object):
def __init__(self, elem, parent):
self.text = elem.text
self.parent = parent
def build_template(self, param_dict):
template = fill_template(self.text, context=param_dict)
return template
@@ -1,44 +0,0 @@
# Contains parameters that are used in External Services
from galaxy.util import string_as_bool
from galaxy.util.template import fill_template
class ExternalServiceParameter(object):
""" Abstract Class for External Service Parameters """
type = None
requires_user_input = False
@classmethod
def from_elem(cls, elem, parent):
param_type = elem.get('type', None)
assert param_type, 'ExternalServiceParameter requires a type'
return parameter_type_to_class[param_type](elem, parent)
def __init__(self, elem, parent):
self.name = elem.get('name', None)
assert self.name, 'ExternalServiceParameter requires a name'
self.parent = parent
def get_value(self, param_dict):
raise Exception('Abstract Method')
class ExternalServiceTemplateParameter(ExternalServiceParameter):
""" Parameter that returns a string containing the requested content """
type = 'template'
def __init__(self, elem, parent):
ExternalServiceParameter.__init__(self, elem, parent)
self.strip = string_as_bool(elem.get('strip', 'False'))
self.text = elem.text
def get_value(self, param_dict):
value = fill_template(self.text, context=param_dict)
if self.strip:
value = value.strip()
return value
parameter_type_to_class = {ExternalServiceTemplateParameter.type: ExternalServiceTemplateParameter}
@@ -1,68 +0,0 @@
import logging
from json import loads
log = logging.getLogger(__name__)
class ExternalServiceActionResultHandler(object):
""" Basic Class for External Service Actions Result Handlers"""
type = 'display'
@classmethod
def from_elem(cls, elem, parent):
result_type = elem.get('type', None)
assert result_type, 'ExternalServiceActionResultHandler requires a type'
return result_type_to_class[result_type](elem, parent)
def __init__(self, elem, parent):
self.parent = parent
def handle_result(self, result, param_dict, trans):
return result.content
# need to think about how to restore or set mime type:
# both as specified in xml and also as set by an action,
# e.g. mimetype returned from web_api action should be reused here...
class ExternalServiceActionURLRedirectResultHandler(ExternalServiceActionResultHandler):
""" Basic Class for External Service Actions Result Handlers"""
type = 'web_redirect'
@classmethod
def from_elem(cls, elem, parent):
result_type = elem.get('type', None)
assert result_type, 'ExternalServiceActionResultHandler requires a type'
return result_type_to_class[result_type](elem, parent)
def __init__(self, elem, parent):
self.parent = parent
def handle_result(self, result, param_dict, trans):
return trans.response.send_redirect(result.content)
class ExternalServiceActionJSONResultHandler(ExternalServiceActionResultHandler):
"""Class for External Service Actions JQuery Result Handler"""
type = 'json_display'
def handle_result(self, result, param_dict, trans):
rval = loads(result.content)
return trans.fill_template('/external_services/generic_json.mako', result=rval, param_dict=param_dict, action=self.parent)
class ExternalServiceActionJQueryGridResultHandler(ExternalServiceActionResultHandler):
"""Class for External Service Actions JQuery Result Handler"""
type = 'jquery_grid'
def handle_result(self, result, param_dict, trans):
rval = loads(result.content)
return trans.fill_template('/external_services/generic_jquery_grid.mako', result=rval, param_dict=param_dict, action=self.parent)
result_type_to_class = {}
for handler_class in [ExternalServiceActionResultHandler, ExternalServiceActionURLRedirectResultHandler, ExternalServiceActionJQueryGridResultHandler, ExternalServiceActionJSONResultHandler]:
result_type_to_class[handler_class.type] = handler_class
-247
View File
@@ -1,247 +0,0 @@
# Contains objects for accessing external services applications
import logging
from galaxy.util.bunch import Bunch
from .actions import ExternalServiceAction
from .parameters import ExternalServiceParameter
log = logging.getLogger(__name__)
class ExternalServiceActionsGroup(object):
def __init__(self, parent, name, label=None):
self.name = name
self.label = label
self.parent = parent
self.items = []
@classmethod
def from_elem(self, elem, parent=None):
"""
Return ExternalServiceActionsGroup created from an xml element.
"""
if elem is not None:
name = elem.get('name')
label = elem.get('label')
rval = ExternalServiceActionsGroup(parent, name, label=label)
rval.load_sub_elems(elem)
else:
rval = ExternalServiceActionsGroup(None, None)
return rval
def load_sub_elems(self, elem):
for sub_elem in elem:
if sub_elem.tag == 'param':
self.add_item(ExternalServiceParameter.from_elem(sub_elem, self))
elif sub_elem.tag == 'action':
self.add_item(ExternalServiceAction.from_elem(sub_elem, self))
elif sub_elem.tag == 'section':
self.add_item(ExternalServiceActionsGroup.from_elem(sub_elem, self))
elif sub_elem.tag == 'conditional':
self.add_item(ExternalServiceActionsConditional(sub_elem, self))
else:
raise ValueError('Unknown tag: %s' % sub_elem.tag)
def add_item(self, item):
self.items.append(item)
def populate(self, service_instance, item=None, param_dict=None):
return PopulatedExternalService(self, service_instance, item, param_dict)
def prepare_actions(self, param_dict, parent_dict, parent_section):
group = Bunch()
group_section = ActionSection(self.name, self.label)
parent_section.append(group_section)
parent_dict[self.name] = group
for item in self.items:
if isinstance(item, ExternalServiceParameter):
group[item.name] = item.get_value(param_dict)
elif isinstance(item, ExternalServiceActionsGroup):
group[item.name] = item.prepare_actions(param_dict, group, group_section)
elif isinstance(item, ExternalServiceAction):
group_section.append(item.populate_action(param_dict))
elif isinstance(item, ExternalServiceActionsConditional):
conditional_group = Bunch()
conditional_group_section = ActionSection(item.name, item.label)
group_section.append(conditional_group_section)
group[item.name] = conditional_group
for case in item.get_current_cases(param_dict):
conditional_group[case.name] = case.prepare_actions(param_dict, conditional_group, conditional_group_section)
else:
raise TypeError('unknown item type found: %s' % item)
return group
class ExternalServiceActionsGroupWhen(ExternalServiceActionsGroup):
type = "when"
@classmethod
def from_elem(self, parent, elem):
"""Loads the proper when by attributes of elem"""
when_type = elem.get('type')
assert when_type in when_type_to_class, TypeError("When type not implemented: %s" % when_type)
return when_type_to_class[when_type].from_elem(parent, elem)
def is_case(self, param_dict):
raise TypeError("Abstract method")
def get_ref(self, param_dict):
ref = param_dict
for ref_name in self.parent.ref:
assert ref_name in ref, "Required dependency '%s' not found in incoming values" % ref_name
ref = ref.get(ref_name)
return ref
class ValueExternalServiceActionsGroupWhen(ExternalServiceActionsGroupWhen):
type = "value"
def __init__(self, parent, name, value, label=None):
super(ValueExternalServiceActionsGroupWhen, self).__init__(parent, name, label)
self.value = value
@classmethod
def from_elem(self, parent, elem):
"""Returns an instance of this when"""
rval = ValueExternalServiceActionsGroupWhen(parent, elem.get('name'), elem.get('value'), elem.get('label'))
rval.load_sub_elems(elem)
return rval
def is_case(self, param_dict):
ref = self.get_ref(param_dict)
return bool(str(ref) == self.value)
class BooleanExternalServiceActionsGroupWhen(ExternalServiceActionsGroupWhen):
type = "boolean"
def __init__(self, parent, name, value, label=None):
super(BooleanExternalServiceActionsGroupWhen, self).__init__(parent, name, label)
self.value = value
@classmethod
def from_elem(self, parent, elem):
"""Returns an instance of this when"""
rval = BooleanExternalServiceActionsGroupWhen(parent, elem.get('name'), elem.get('label'))
rval.load_sub_elems(elem)
return rval
def is_case(self, param_dict):
ref = self.get_ref(param_dict)
return bool(ref)
class ItemIsInstanceExternalServiceActionsGroupWhen(ExternalServiceActionsGroupWhen):
type = "item_type"
def __init__(self, parent, name, value, label=None):
super(ItemIsInstanceExternalServiceActionsGroupWhen, self).__init__(parent, name, label)
self.value = value
@classmethod
def from_elem(self, parent, elem):
"""Returns an instance of this when"""
rval = ItemIsInstanceExternalServiceActionsGroupWhen(parent, elem.get('name'), elem.get('value'), elem.get('label'))
rval.load_sub_elems(elem)
return rval
def is_case(self, param_dict):
ref = self.get_ref(param_dict)
return ref.__class__.__name__.lower() in map(lambda x: x.lower(), self.value.split('.')) # HACK!
when_type_to_class = {}
for class_type in [ValueExternalServiceActionsGroupWhen, BooleanExternalServiceActionsGroupWhen, ItemIsInstanceExternalServiceActionsGroupWhen]:
when_type_to_class[class_type.type] = class_type
class ExternalServiceActionsConditional(object):
type = "conditional"
def __init__(self, elem, parent):
self.parent = parent
self.name = elem.get('name', None)
assert self.name is not None, "Required 'name' attribute missing from ExternalServiceActionsConditional"
self.label = elem.get('label')
self.ref = elem.get('ref', None)
assert self.ref is not None, "Required 'ref' attribute missing from ExternalServiceActionsConditional"
self.ref = self.ref.split('.')
self.cases = []
for when_elem in elem.findall('when'):
self.cases.append(ExternalServiceActionsGroupWhen.from_elem(self, when_elem))
def get_current_cases(self, param_dict):
rval = []
for case in self.cases:
if case.is_case(param_dict):
rval.append(case)
return rval
class ActionSection(list):
def __init__(self, name, label):
list.__init__(self)
self.name = name
self.label = label
def has_action(self):
for item in self:
if not isinstance(item, ActionSection):
return True
else:
if item.has_action():
return True
return False
class PopulatedExternalService(object):
def __init__(self, service_group, service_instance, item, param_dict=None):
self.service_group = service_group
self.service_instance = service_instance
self.item = item
self.param_dict = param_dict
self.populate()
def __getattr__(self, name):
return getattr(self.service_instance, name) # should .service or.service_instance should be here...
def populate(self):
param_dict = {}
param_dict['fields'] = Bunch(**self.service_instance.form_values.content)
param_dict['item'] = self.item
param_dict['service'] = self.service_group.parent
param_dict['service_instance'] = self.service_instance
action_list = ActionSection(self.service_group.name, self.service_group.label)
for item in self.service_group.items:
if isinstance(item, ExternalServiceParameter):
param_dict[item.name] = item.get_value(param_dict)
elif isinstance(item, ExternalServiceAction):
action_list.append(item.populate_action(param_dict))
elif isinstance(item, ExternalServiceActionsGroup):
item.prepare_actions(param_dict, param_dict, action_list)
else:
raise Exception('unknown item type found')
self.param_dict = param_dict
self.actions = action_list
def perform_action_by_name(self, actions_list):
action = self.get_action_by_name(actions_list)
action.perform_action()
return action
def get_action_by_name(self, actions_list):
action = None
actions = self.actions # populated actions
for name in actions_list:
action_found = False
for action in actions:
if action.name == name:
action_found = True
actions = action
break
assert action_found, 'Action not found: %s in %s' % (name, actions_list)
assert action, 'Action not found: %s' % actions_list
return action
def __nonzero__(self):
return self.actions.has_action()
-192
View File
@@ -1,192 +0,0 @@
"""
Queue for running deferred code via plugins.
"""
import logging
import os
import threading
from Queue import Queue
from galaxy import model
from galaxy.util.bunch import Bunch
from galaxy.util.sleeper import Sleeper
log = logging.getLogger(__name__)
class DeferredJobQueue(object):
job_states = Bunch(READY='ready',
WAIT='wait',
INVALID='invalid')
def __init__(self, app):
self.app = app
self.sa_session = app.model.context.current
self.queue = Queue()
self.plugins = {}
self._load_plugins()
self.sleeper = Sleeper()
self.running = True
self.waiting_jobs = []
self.__check_jobs_at_startup()
self.monitor_thread = threading.Thread(target=self.__monitor)
self.monitor_thread.start()
log.info('Deferred job queue started')
def _load_plugins(self):
for fname in os.listdir(os.path.dirname(__file__)):
if not fname.startswith('_') and fname.endswith('.py'):
name = fname[:-3]
module_name = 'galaxy.jobs.deferred.' + name
try:
module = __import__(module_name)
except ImportError:
log.exception('Deferred job plugin appears to exist but is not loadable: %s', module_name)
continue
for comp in module_name.split(".")[1:]:
module = getattr(module, comp)
if '__all__' not in dir(module):
log.error('Plugin "%s" does not contain a list of exported classes in __all__' % module_name)
continue
for obj in module.__all__:
display_name = ':'.join((module_name, obj))
plugin = getattr(module, obj)
for name in ('check_job', 'run_job'):
if name not in dir(plugin):
log.error('Plugin "%s" does not contain required method "%s()"' % (display_name, name))
break
else:
self.plugins[obj] = plugin(self.app)
self.plugins[obj].job_states = self.job_states
log.debug('Loaded deferred job plugin: %s' % display_name)
def __check_jobs_at_startup(self):
waiting_jobs = self.sa_session.query(model.DeferredJob) \
.filter(model.DeferredJob.state == model.DeferredJob.states.WAITING).all()
for job in waiting_jobs:
if not self.__check_job_plugin(job):
continue
if 'check_interval' in dir(self.plugins[job.plugin]):
job.check_interval = self.plugins[job.plugin].check_interval
log.info('Recovered deferred job (id: %s) at startup' % job.id)
# Pass the job ID as opposed to the job, since the monitor thread
# needs to load it in its own threadlocal scoped session.
self.waiting_jobs.append(job.id)
def __monitor(self):
while self.running:
try:
self.__monitor_step()
except Exception:
log.exception('Exception in monitor_step')
self.sleeper.sleep(1)
log.info('job queue stopped')
def __monitor_step(self):
# TODO: Querying the database with this frequency is bad, we need message passing
new_jobs = self.sa_session.query(model.DeferredJob) \
.filter(model.DeferredJob.state == model.DeferredJob.states.NEW).all()
for job in new_jobs:
if not self.__check_job_plugin(job):
continue
job.state = model.DeferredJob.states.WAITING
self.sa_session.add(job)
self.sa_session.flush()
if 'check_interval' in dir(self.plugins[job.plugin]):
job.check_interval = self.plugins[job.plugin].check_interval
self.waiting_jobs.append(job)
new_waiting = []
for job in self.waiting_jobs:
try:
# Recovered jobs are passed in by ID
assert type(job) is int
job = self.sa_session.query(model.DeferredJob).get(job)
except Exception:
pass
if job.is_check_time:
try:
job_state = self.plugins[job.plugin].check_job(job)
except Exception:
self.__fail_job(job)
log.exception('Set deferred job %s to error because of an exception in check_job()' % job.id)
continue
if job_state == self.job_states.READY:
try:
self.plugins[job.plugin].run_job(job)
except Exception:
self.__fail_job(job)
log.exception('Set deferred job %s to error because of an exception in run_job()' % job.id)
continue
elif job_state == self.job_states.INVALID:
self.__fail_job(job)
log.error('Unable to run deferred job (id: %s): Plugin "%s" marked it as invalid' % (job.id, job.plugin))
continue
else:
new_waiting.append(job)
job.last_check = 'now'
else:
new_waiting.append(job)
self.waiting_jobs = new_waiting
def __check_job_plugin(self, job):
if job.plugin not in self.plugins:
log.error('Invalid deferred job plugin: %s') % job.plugin
job.state = model.DeferredJob.states.ERROR
self.sa_session.add(job)
self.sa_session.flush()
return False
return True
def __check_if_ready_to_run(self, job):
return self.plugins[job.plugin].check_job(job)
def __fail_job(self, job):
job.state = model.DeferredJob.states.ERROR
self.sa_session.add(job)
self.sa_session.flush()
def shutdown(self):
self.running = False
self.sleeper.wake()
class FakeTrans(object):
"""A fake trans for calling the external set metadata tool"""
def __init__(self, app, history=None, user=None):
class Dummy(object):
def __init__(self):
self.id = None
self.app = app
self.sa_session = app.model.context.current
self.dummy = Dummy()
if not history:
self.history = Dummy()
else:
self.history = history
if not user:
self.user = Dummy()
else:
self.user = user
self.model = app.model
def get_galaxy_session(self):
return self.dummy
def log_event(self, message, tool_id=None):
pass
def get_current_user_roles(self):
if self.user:
return self.user.all_roles()
else:
return []
def db_dataset_for(self, dbkey):
if self.history is None:
return None
datasets = self.sa_session.query(self.app.model.HistoryDatasetAssociation) \
.filter_by(deleted=False, history_id=self.history.id, extension="len")
for ds in datasets:
if dbkey == ds.dbkey:
return ds
return None
-384
View File
@@ -1,384 +0,0 @@
"""
Module for managing data transfer jobs.
"""
import logging
import re
import shutil
from sqlalchemy import and_
from galaxy.datatypes import sniff
from galaxy.jobs.actions.post import ActionBox
from galaxy.jobs.deferred import FakeTrans
from galaxy.tools.parameters import visit_input_values
from galaxy.tools.parameters.basic import DataToolParameter
from galaxy.util.odict import odict
from galaxy.workflow.modules import module_factory
log = logging.getLogger(__name__)
__all__ = ('DataTransfer', )
class DataTransfer(object):
check_interval = 15
dataset_name_re = re.compile('(dataset\d+)_(name)')
dataset_datatype_re = re.compile('(dataset\d+)_(datatype)')
def __init__(self, app):
self.app = app
self.sa_session = app.model.context.current
def create_job(self, trans, **kwd):
raise Exception("Unimplemented Method")
def check_job(self, job):
raise Exception("Unimplemented Method")
def run_job(self, job):
if job.params['type'] == 'init_transfer':
# TODO: don't create new downloads on restart.
if job.params['protocol'] in ['http', 'https']:
results = []
for result in job.params['results'].values():
result['transfer_job'] = self.app.transfer_manager.new(protocol=job.params['protocol'],
name=result['name'],
datatype=result['datatype'],
url=result['url'])
results.append(result)
elif job.params['protocol'] == 'scp':
results = []
result = {}
sample_datasets_dict = job.params['sample_datasets_dict']
# sample_datasets_dict looks something like the following. The outer dictionary keys are SampleDataset ids.
# {'7': {'status': 'Not started', 'name': '3.bed', 'file_path': '/tmp/library/3.bed', 'sample_id': 7,
# 'external_service_id': 2, 'error_msg': '', 'size': '8.0K'}}
for sample_dataset_id, sample_dataset_info_dict in sample_datasets_dict.items():
result = {}
result['transfer_job'] = self.app.transfer_manager.new(protocol=job.params['protocol'],
host=job.params['host'],
user_name=job.params['user_name'],
password=job.params['password'],
sample_dataset_id=sample_dataset_id,
status=sample_dataset_info_dict['status'],
name=sample_dataset_info_dict['name'],
file_path=sample_dataset_info_dict['file_path'],
sample_id=sample_dataset_info_dict['sample_id'],
external_service_id=sample_dataset_info_dict['external_service_id'],
error_msg=sample_dataset_info_dict['error_msg'],
size=sample_dataset_info_dict['size'])
results.append(result)
self.app.transfer_manager.run([r['transfer_job'] for r in results])
for result in results:
transfer_job = result.pop('transfer_job')
self.create_job(None,
transfer_job_id=transfer_job.id,
result=transfer_job.params,
sample_id=job.params['sample_id'])
# Update the state of the relevant SampleDataset
new_status = self.app.model.SampleDataset.transfer_status.IN_QUEUE
self._update_sample_dataset_status(protocol=job.params['protocol'],
sample_id=job.params['sample_id'],
result_dict=transfer_job.params,
new_status=new_status,
error_msg='')
job.state = self.app.model.DeferredJob.states.OK
self.sa_session.add(job)
self.sa_session.flush()
# TODO: Error handling: failure executing, or errors returned from the manager
if job.params['type'] == 'finish_transfer':
protocol = job.params['protocol']
# Update the state of the relevant SampleDataset
new_status = self.app.model.SampleDataset.transfer_status.ADD_TO_LIBRARY
if protocol in ['http', 'https']:
result_dict = job.params['result']
library_dataset_name = result_dict['name']
extension = result_dict['datatype']
elif protocol in ['scp']:
# In this case, job.params will be a dictionary that contains a key named 'result'. The value
# of the result key is a dictionary that looks something like:
# {'sample_dataset_id': '8', 'status': 'Not started', 'protocol': 'scp', 'name': '3.bed',
# 'file_path': '/data/library/3.bed', 'host': '127.0.0.1', 'sample_id': 8, 'external_service_id': 2,
# 'local_path': '/tmp/kjl2Ss4', 'password': 'galaxy', 'user_name': 'gvk', 'error_msg': '', 'size': '8.0K'}
try:
tj = self.sa_session.query(self.app.model.TransferJob).get(int(job.params['transfer_job_id']))
result_dict = tj.params
result_dict['local_path'] = tj.path
except Exception as e:
log.error("Updated transfer result unavailable, using old result. Error was: %s" % str(e))
result_dict = job.params['result']
library_dataset_name = result_dict['name']
# Determine the data format (see the relevant TODO item in the manual_data_transfer plugin)..
extension = sniff.guess_ext(result_dict['local_path'], sniff_order=self.app.datatypes_registry.sniff_order)
self._update_sample_dataset_status(protocol=job.params['protocol'],
sample_id=int(job.params['sample_id']),
result_dict=result_dict,
new_status=new_status,
error_msg='')
sample = self.sa_session.query(self.app.model.Sample).get(int(job.params['sample_id']))
ld = self.app.model.LibraryDataset(folder=sample.folder, name=library_dataset_name)
self.sa_session.add(ld)
self.sa_session.flush()
self.app.security_agent.copy_library_permissions(FakeTrans(self.app), sample.folder, ld)
ldda = self.app.model.LibraryDatasetDatasetAssociation(name=library_dataset_name,
extension=extension,
dbkey='?',
library_dataset=ld,
create_dataset=True,
sa_session=self.sa_session)
ldda.message = 'Transferred by the Data Transfer Plugin'
self.sa_session.add(ldda)
self.sa_session.flush()
ldda.state = ldda.states.QUEUED # flushed in the set property
ld.library_dataset_dataset_association_id = ldda.id
self.sa_session.add(ld)
self.sa_session.flush()
try:
# Move the dataset from its temporary location
shutil.move(job.transfer_job.path, ldda.file_name)
ldda.init_meta()
for name, spec in ldda.metadata.spec.items():
if name not in ['name', 'info', 'dbkey', 'base_name']:
if spec.get('default'):
setattr(ldda.metadata, name, spec.unwrap(spec.get('default')))
self.app.datatypes_registry.set_external_metadata_tool.tool_action.execute(self.app.datatypes_registry.set_external_metadata_tool,
FakeTrans(self.app,
history=sample.history,
user=sample.request.user),
incoming={'input1': ldda})
ldda.state = ldda.states.OK
# TODO: not sure if this flush is necessary
self.sa_session.add(ldda)
self.sa_session.flush()
except Exception as e:
log.exception('Failure preparing library dataset for finished transfer job (id: %s) via deferred job (id: %s):' %
(str(job.transfer_job.id), str(job.id)))
ldda.state = ldda.states.ERROR
if sample.workflow:
log.debug("\n\nLogging sample mappings as: %s" % sample.workflow['mappings'])
log.debug("job.params: %s" % job.params)
# We have a workflow. Update all mappings to ldda's, and when the final one is done
# execute_workflow with either the provided history, or a new one.
sub_done = True
rep_done = False
for k, v in sample.workflow['mappings'].items():
if 'hda' not in v and v['ds_tag'].startswith('hi|'):
sample.workflow['mappings'][k]['hda'] = self.app.security.decode_id(v['ds_tag'][3:])
for key, value in sample.workflow['mappings'].items():
if 'url' in value and value['url'] == job.params['result']['url']:
# DBTODO Make sure all ds| mappings get the URL of the dataset, for linking to later.
# If this dataset maps to what we just finished, update the ldda id in the sample.
sample.workflow['mappings'][key]['ldda'] = ldda.id
rep_done = True
# DBTODO replace the hi| mappings with the hda here. Just rip off the first three chars.
elif 'ldda' not in value and 'hda' not in value:
# We're not done if some mappings still don't have ldda or hda mappings.
sub_done = False
if sub_done and rep_done:
if not sample.history:
new_history = self.app.model.History(name="New History From %s" % sample.name, user=sample.request.user)
self.sa_session.add(new_history)
sample.history = new_history
self.sa_session.flush()
self._execute_workflow(sample)
# Check the workflow for substitution done-ness
self.sa_session.add(sample)
self.sa_session.flush()
elif sample.history:
# We don't have a workflow, but a history was provided.
# No processing, go ahead and chunk everything in the history.
if ldda.dataset.state in ['new', 'upload', 'queued', 'running', 'empty', 'discarded']:
log.error("Cannot import dataset '%s' to user history since its state is '%s'. " % (ldda.name, ldda.dataset.state))
elif ldda.dataset.state in ['ok', 'error']:
ldda.to_history_dataset_association(target_history=sample.history, add_to_history=True)
# Finished
job.state = self.app.model.DeferredJob.states.OK
self.sa_session.add(job)
self.sa_session.flush()
# Update the state of the relevant SampleDataset
new_status = self.app.model.SampleDataset.transfer_status.COMPLETE
self._update_sample_dataset_status(protocol=job.params['protocol'],
sample_id=int(job.params['sample_id']),
result_dict=job.params['result'],
new_status=new_status,
error_msg='')
if sample.datasets and not sample.untransferred_dataset_files:
# Update the state of the sample to the sample's request type's final state.
new_state = sample.request.type.final_sample_state
self._update_sample_state(sample.id, new_state)
# Update the state of the request, if possible
self._update_request_state(sample.request.id)
def _missing_params(self, params, required_params):
missing_params = [x for x in required_params if x not in params]
if missing_params:
log.error('Job parameters missing required keys: %s' % ', '.join(missing_params))
return True
return False
def _update_sample_dataset_status(self, protocol, sample_id, result_dict, new_status, error_msg=None):
# result_dict looks something like:
# {'url': '127.0.0.1/data/filtered_subreads.fa', 'name': 'Filtered reads'}
# TODO: error checking on valid new_status value
if protocol in ['http', 'https']:
sample_dataset = self.sa_session.query(self.app.model.SampleDataset) \
.filter(and_(self.app.model.SampleDataset.table.c.sample_id == sample_id,
self.app.model.SampleDataset.table.c.name == result_dict['name'],
self.app.model.SampleDataset.table.c.file_path == result_dict['url'])) \
.first()
elif protocol in ['scp']:
sample_dataset = self.sa_session.query(self.app.model.SampleDataset).get(int(result_dict['sample_dataset_id']))
sample_dataset.status = new_status
sample_dataset.error_msg = error_msg
self.sa_session.add(sample_dataset)
self.sa_session.flush()
def _update_sample_state(self, sample_id, new_state, comment=None):
sample = self.sa_session.query(self.app.model.Sample).get(sample_id)
if comment is None:
comment = 'Sample state set to %s' % str(new_state)
event = self.app.model.SampleEvent(sample, new_state, comment)
self.sa_session.add(event)
self.sa_session.flush()
def _update_request_state(self, request_id):
request = self.sa_session.query(self.app.model.Request).get(request_id)
# Make sure all the samples of the current request have the same state
common_state = request.samples_have_common_state
if not common_state:
# If the current request state is complete and one of its samples moved from
# the final sample state, then move the request state to In-progress
if request.is_complete:
message = "At least 1 sample state moved from the final sample state, so now the request's state is (%s)" % request.states.SUBMITTED
event = self.app.model.RequestEvent(request, request.states.SUBMITTED, message)
self.sa_session.add(event)
self.sa_session.flush()
else:
request_type_state = request.type.final_sample_state
if common_state.id == request_type_state.id:
# Since all the samples are in the final state, change the request state to 'Complete'
comment = "All samples of this sequencing request are in the final sample state (%s). " % request_type_state.name
state = request.states.COMPLETE
else:
comment = "All samples of this sequencing request are in the (%s) sample state. " % common_state.name
state = request.states.SUBMITTED
event = self.app.model.RequestEvent(request, state, comment)
self.sa_session.add(event)
self.sa_session.flush()
# TODO: handle email notification if it is configured to be sent when the samples are in this state.
def _execute_workflow(self, sample):
for key, value in sample.workflow['mappings'].items():
if 'hda' not in value and 'ldda' in value:
# If HDA is already here, it's an external input, we're not copying anything.
ldda = self.sa_session.query(self.app.model.LibraryDatasetDatasetAssociation).get(value['ldda'])
if ldda.dataset.state in ['new', 'upload', 'queued', 'running', 'empty', 'discarded']:
log.error("Cannot import dataset '%s' to user history since its state is '%s'. " % (ldda.name, ldda.dataset.state))
elif ldda.dataset.state in ['ok', 'error']:
hda = ldda.to_history_dataset_association(target_history=sample.history, add_to_history=True)
sample.workflow['mappings'][key]['hda'] = hda.id
self.sa_session.add(sample)
self.sa_session.flush()
workflow_dict = sample.workflow
import copy
new_wf_dict = copy.deepcopy(workflow_dict)
for key in workflow_dict['mappings']:
if not isinstance(key, int):
new_wf_dict['mappings'][int(key)] = workflow_dict['mappings'][key]
workflow_dict = new_wf_dict
fk_trans = FakeTrans(self.app, history=sample.history, user=sample.request.user)
workflow = self.sa_session.query(self.app.model.Workflow).get(workflow_dict['id'])
if not workflow:
log.error("Workflow mapping failure.")
return
if len(workflow.steps) == 0:
log.error("Workflow cannot be run because it does not have any steps")
return
if workflow.has_cycles:
log.error("Workflow cannot be run because it contains cycles")
return
if workflow.has_errors:
log.error("Workflow cannot be run because of validation errors in some steps")
return
# Build the state for each step
errors = {}
# Build a fake dictionary prior to execution.
# Prepare each step
for step in workflow.steps:
step.upgrade_messages = {}
# Contruct modules
if step.type == 'tool' or step.type is None:
# Restore the tool state for the step
step.module = module_factory.from_workflow_step(fk_trans, step)
# Fix any missing parameters
step.upgrade_messages = step.module.check_and_update_state()
# Any connected input needs to have value DummyDataset (these
# are not persisted so we need to do it every time)
step.module.add_dummy_datasets(connections=step.input_connections)
# Store state with the step
step.state = step.module.state
# Error dict
if step.tool_errors:
errors[step.id] = step.tool_errors
else:
# Non-tool specific stuff?
step.module = module_factory.from_workflow_step(fk_trans, step)
step.state = step.module.get_runtime_state()
# Connections by input name
step.input_connections_by_name = dict((conn.input_name, conn) for conn in step.input_connections)
for step in workflow.steps:
step.upgrade_messages = {}
# Connections by input name
step.input_connections_by_name = \
dict((conn.input_name, conn) for conn in step.input_connections)
# Extract just the arguments for this step by prefix
step_errors = None
if step.type == 'tool' or step.type is None:
module = module_factory.from_workflow_step(fk_trans, step)
# Fix any missing parameters
step.upgrade_messages = module.check_and_update_state()
# Any connected input needs to have value DummyDataset (these
# are not persisted so we need to do it every time)
module.add_dummy_datasets(connections=step.input_connections)
# Get the tool
tool = module.tool
# Get the state
step.state = state = module.state
if step_errors:
errors[step.id] = state.inputs["__errors__"] = step_errors
# Run each step, connecting outputs to inputs
workflow_invocation = self.app.model.WorkflowInvocation()
workflow_invocation.workflow = workflow
outputs = odict()
for i, step in enumerate(workflow.steps):
job = None
if step.type == 'tool' or step.type is None:
tool = self.app.toolbox.get_tool(step.tool_id)
def callback(input, prefixed_name, **kwargs):
if isinstance(input, DataToolParameter):
if prefixed_name in step.input_connections_by_name:
conn = step.input_connections_by_name[prefixed_name]
return outputs[conn.output_step.id][conn.output_name]
visit_input_values(tool.inputs, step.state.inputs, callback)
job, out_data = tool.execute(fk_trans, step.state.inputs, history=sample.history)
outputs[step.id] = out_data
for pja in step.post_job_actions:
if pja.action_type in ActionBox.immediate_actions:
ActionBox.execute(self.app, self.sa_session, pja, job, replacement_dict=None)
else:
job.add_post_job_action(pja)
else:
job, out_data = step.module.execute(fk_trans, step.state)
outputs[step.id] = out_data
if step.id in workflow_dict['mappings']:
data = self.sa_session.query(self.app.model.HistoryDatasetAssociation).get(workflow_dict['mappings'][str(step.id)]['hda'])
outputs[step.id]['output'] = data
workflow_invocation_step = self.app.model.WorkflowInvocationStep()
workflow_invocation_step.workflow_invocation = workflow_invocation
workflow_invocation_step.workflow_step = step
workflow_invocation_step.job = job
self.sa_session.add(workflow_invocation)
self.sa_session.flush()
@@ -1,107 +0,0 @@
"""
Generic module for managing manual data transfer jobs using Galaxy's built-in file browser.
This module can be used by various external services that are configured to transfer data manually.
"""
import logging
from .data_transfer import DataTransfer
log = logging.getLogger(__name__)
__all__ = ('ManualDataTransferPlugin', )
class ManualDataTransferPlugin(DataTransfer):
def __init__(self, app):
super(ManualDataTransferPlugin, self).__init__(app)
def create_job(self, trans, **kwd):
if 'sample' in kwd and 'sample_datasets' in kwd and 'external_service' in kwd and 'external_service_type' in kwd:
sample = kwd['sample']
sample_datasets = kwd['sample_datasets']
external_service = kwd['external_service']
external_service_type = kwd['external_service_type']
# TODO: is there a better way to store the protocol?
protocol = next(iter(external_service_type.data_transfer.keys()))
host = external_service.form_values.content['host']
user_name = external_service.form_values.content['user_name']
password = external_service.form_values.content['password']
# TODO: In the future, we may want to implement a way for the user to associate a selected file with one of
# the run outputs configured in the <run_details><results> section of the external service config file. The
# following was a first pass at implementing something (the datatype was included in the sample_dataset_dict),
# but without a way for the user to associate stuff it's useless. However, allowing the user this ability may
# open a can of worms, so maybe we shouldn't do it???
#
# for run_result_file_name, run_result_file_datatype in external_service_type.run_details[ 'results' ].items():
# # external_service_type.run_details[ 'results' ] looks something like: {'dataset1_name': 'dataset1_datatype'}
# if run_result_file_datatype in external_service.form_values.content:
# datatype = external_service.form_values.content[ run_result_file_datatype ]
#
# When the transfer is automatic (the process used in the SMRT Portal plugin), the datasets and datatypes
# can be matched up to those configured in the <run_details><results> settings in the external service type config
# (e.g., pacific_biosciences_smrt_portal.xml). However, that's a bit trickier here since the user is manually
# selecting files for transfer.
sample_datasets_dict = {}
for sample_dataset in sample_datasets:
sample_dataset_id = sample_dataset.id
sample_dataset_dict = dict(sample_id=sample_dataset.sample.id,
name=sample_dataset.name,
file_path=sample_dataset.file_path,
status=sample_dataset.status,
error_msg=sample_dataset.error_msg,
size=sample_dataset.size,
external_service_id=sample_dataset.external_service.id)
sample_datasets_dict[sample_dataset_id] = sample_dataset_dict
params = {'type' : 'init_transfer',
'sample_id' : sample.id,
'sample_datasets_dict' : sample_datasets_dict,
'protocol' : protocol,
'host' : host,
'user_name' : user_name,
'password' : password}
elif 'transfer_job_id' in kwd:
params = {'type' : 'finish_transfer',
'protocol' : kwd['result']['protocol'],
'sample_id' : kwd['sample_id'],
'result' : kwd['result'],
'transfer_job_id' : kwd['transfer_job_id']}
else:
log.error('No job was created because kwd does not include "samples" and "sample_datasets" or "transfer_job_id".')
return
deferred_job = self.app.model.DeferredJob(state=self.app.model.DeferredJob.states.NEW,
plugin='ManualDataTransferPlugin',
params=params)
self.sa_session.add(deferred_job)
self.sa_session.flush()
log.debug('Created a deferred job in the ManualDataTransferPlugin of type: %s' % params['type'])
# TODO: error reporting to caller (if possible?)
def check_job(self, job):
if self._missing_params(job.params, ['type']):
return self.job_states.INVALID
if job.params['type'] == 'init_transfer':
if job.params['protocol'] in ['http', 'https']:
raise Exception("Manual data transfer is not yet supported for http(s).")
elif job.params['protocol'] == 'scp':
if self._missing_params(job.params, ['protocol', 'host', 'user_name', 'password', 'sample_id', 'sample_datasets_dict']):
return self.job_states.INVALID
# TODO: what kind of checks do we need here?
return self.job_states.READY
return self.job_states.WAIT
if job.params['type'] == 'finish_transfer':
if self._missing_params(job.params, ['transfer_job_id']):
return self.job_states.INVALID
# Get the TransferJob object and add it to the DeferredJob so we only look it up once.
if not hasattr(job, 'transfer_job'):
job.transfer_job = self.sa_session.query(self.app.model.TransferJob).get(int(job.params['transfer_job_id']))
state = self.app.transfer_manager.get_state(job.transfer_job)
if not state:
log.error('No state for transfer job id: %s' % job.transfer_job.id)
return self.job_states.WAIT
if state['state'] in self.app.model.TransferJob.terminal_states:
return self.job_states.READY
log.debug("Checked on finish transfer job %s, not done yet." % job.id)
return self.job_states.WAIT
else:
log.error('Unknown job type for ManualDataTransferPlugin: %s' % str(job.params['type']))
return self.job_states.INVALID
@@ -1,131 +0,0 @@
"""
Module for managing jobs in Pacific Bioscience's SMRT Portal and automatically transferring files
produced by SMRT Portal.
"""
import logging
from string import Template
import requests
from .data_transfer import DataTransfer
log = logging.getLogger(__name__)
__all__ = ('SMRTPortalPlugin', )
class SMRTPortalPlugin(DataTransfer):
api_path = '/smrtportal/api'
def __init__(self, app):
super(SMRTPortalPlugin, self).__init__(app)
def create_job(self, trans, **kwd):
if 'secondary_analysis_job_id' in kwd:
sample = kwd['sample']
smrt_job_id = kwd['secondary_analysis_job_id']
external_service = sample.request.type.get_external_service('pacific_biosciences_smrt_portal')
external_service.load_data_transfer_settings(trans)
http_configs = external_service.data_transfer[trans.model.ExternalService.data_transfer_protocol.HTTP]
if not http_configs['automatic_transfer']:
raise Exception("Manual data transfer using http is not yet supported.")
smrt_host = external_service.form_values.content['host']
external_service_type = external_service.get_external_service_type(trans)
# TODO: is there a better way to store the protocol?
# external_service_type.data_transfer looks somethng like
# {'http': <galaxy.sample_tracking.data_transfer.HttpDataTransferFactory object at 0x1064239d0>}
protocol = next(iter(external_service_type.data_transfer.keys()))
results = {}
for k, v in external_service.form_values.content.items():
match = self.dataset_name_re.match(k) or self.dataset_datatype_re.match(k)
if match:
id, field = match.groups()
if id in results:
results[id][field] = v
else:
results[id] = {field : v}
for id, attrs in results.items():
url_template = external_service_type.run_details['results_urls'].get(id + '_name')
url = Template(url_template).substitute(host=smrt_host, secondary_analysis_job_id=kwd['secondary_analysis_job_id'])
results[id]['url'] = url
if sample.workflow:
# DBTODO Make sure all ds| mappings get the URL of the dataset, for linking to later.
for k, v in sample.workflow['mappings'].items():
if 'ds|%s' % id in v.values():
sample.workflow['mappings'][k]['url'] = url
self.sa_session.add(sample)
self.sa_session.flush()
params = {'type' : 'init_transfer',
'protocol' : protocol,
'sample_id' : sample.id,
'results' : results,
'smrt_host' : smrt_host,
'smrt_job_id' : smrt_job_id}
# Create a new SampleDataset for each run result dataset
self._associate_untransferred_datasets_with_sample(sample, external_service, results)
elif 'transfer_job_id' in kwd:
params = {'type' : 'finish_transfer',
'protocol' : kwd['result']['protocol'],
'sample_id' : kwd['sample_id'],
'result' : kwd['result'],
'transfer_job_id' : kwd['transfer_job_id']}
else:
log.error('No job was created because kwd does not include "secondary_analysis_job_id" or "transfer_job_id".')
return
deferred_job = self.app.model.DeferredJob(state=self.app.model.DeferredJob.states.NEW,
plugin='SMRTPortalPlugin',
params=params)
self.sa_session.add(deferred_job)
self.sa_session.flush()
log.debug('Created a deferred job in the SMRTPortalPlugin of type: %s' % params['type'])
# TODO: error reporting to caller (if possible?)
def check_job(self, job):
if self._missing_params(job.params, ['type']):
return self.job_states.INVALID
if job.params['type'] == 'init_transfer':
if self._missing_params(job.params, ['smrt_host', 'smrt_job_id']):
return self.job_states.INVALID
url = 'http://' + job.params['smrt_host'] + self.api_path + '/Jobs/' + job.params['smrt_job_id'] + '/Status'
r = requests.get(url)
status = r.json()
# TODO: error handling: unexpected json or bad response, bad url, etc.
if status['Code'] == 'Completed':
log.debug("SMRT Portal job '%s' is Completed. Initiating transfer." % job.params['smrt_job_id'])
return self.job_states.READY
return self.job_states.WAIT
if job.params['type'] == 'finish_transfer':
if self._missing_params(job.params, ['transfer_job_id']):
return self.job_states.INVALID
# Get the TransferJob object and add it to the DeferredJob so we only look it up once.
if not hasattr(job, 'transfer_job'):
job.transfer_job = self.sa_session.query(self.app.model.TransferJob).get(int(job.params['transfer_job_id']))
state = self.app.transfer_manager.get_state(job.transfer_job)
if not state:
log.error('No state for transfer job id: %s' % job.transfer_job.id)
return self.job_states.WAIT
if state['state'] in self.app.model.TransferJob.terminal_states:
return self.job_states.READY
log.debug("Checked on finish transfer job %s, not done yet." % job.id)
return self.job_states.WAIT
else:
log.error('Unknown job type for SMRTPortalPlugin: %s' % str(job.params['type']))
return self.job_states.INVALID
def _associate_untransferred_datasets_with_sample(self, sample, external_service, results_dict):
# results_dict looks something like:
# {'dataset2': {'datatype': 'fasta', 'url': '127.0.0.1:8080/data/filtered_subreads.fa', 'name': 'Filtered reads'} }
for key, val in results_dict.items():
file_path = val['url']
status = self.app.model.SampleDataset.transfer_status.NOT_STARTED
name = val['name']
size = 'unknown'
sample_dataset = self.app.model.SampleDataset(sample=sample,
file_path=file_path,
status=status,
name=name,
error_msg='',
size=size,
external_service=external_service)
self.sa_session.add(sample_dataset)
self.sa_session.flush()
-5
View File
@@ -98,11 +98,6 @@ class ProvidesAppContext(object):
def install_model(self):
return self.app.install_model
def request_types(self):
if self.sa_session.query(self.app.model.RequestType).filter_by(deleted=False).count() > 0:
return True
return False
class ProvidesUserContext(object):
""" For transaction-like objects to provide Galaxy convience layer for
+3 -502
View File
@@ -11,7 +11,6 @@ import numbers
import operator
import os
import pwd
import socket
import time
from datetime import datetime, timedelta
from string import Template
@@ -32,7 +31,7 @@ from galaxy.model.item_attrs import UsesAnnotations
from galaxy.model.util import pgcalc
from galaxy.security import get_permitted_actions
from galaxy.util import (directory_hash_id, Params, ready_name_for_url,
restore_text, send_mail, unicodify, unique_id)
restore_text, unicodify, unique_id)
from galaxy.util.bunch import Bunch
from galaxy.util.dictifiable import Dictifiable
from galaxy.util.hash_util import new_secure_hash
@@ -4415,12 +4414,7 @@ class MetadataFile(StorableObject):
class FormDefinition(object, Dictifiable):
# The following form_builder classes are supported by the FormDefinition class.
supported_field_types = [AddressField, CheckboxField, PasswordField, SelectField, TextArea, TextField, WorkflowField, WorkflowMappingField, HistoryField]
types = Bunch(REQUEST='Sequencing Request Form',
SAMPLE='Sequencing Sample Form',
EXTERNAL_SERVICE='External Service Information Form',
RUN_DETAILS_TEMPLATE='Sample run details template',
LIBRARY_INFO_TEMPLATE='Library information template',
USER_INFO='User Information')
types = Bunch(USER_INFO='User Information')
dict_collection_visible_keys = ['id', 'name']
dict_element_visible_keys = ['id', 'name', 'desc', 'form_definition_current_id', 'fields', 'layout']
@@ -4560,497 +4554,6 @@ class FormValues(object):
self.content = content
class Request(object, Dictifiable):
states = Bunch(NEW='New',
SUBMITTED='In Progress',
REJECTED='Rejected',
COMPLETE='Complete')
dict_collection_visible_keys = ['id', 'name', 'state']
def __init__(self, name=None, desc=None, request_type=None, user=None, form_values=None, notification=None):
self.name = name
self.desc = desc
self.type = request_type
self.values = form_values
self.user = user
self.notification = notification
self.samples_list = []
@property
def state(self):
latest_event = self.latest_event
if latest_event:
return latest_event.state
return None
@property
def latest_event(self):
if self.events:
return self.events[0]
return None
@property
def samples_have_common_state(self):
"""
Returns the state of this request's samples when they are all
in one common state. Otherwise returns False.
"""
state_for_comparison = self.samples[0].state
if state_for_comparison is None:
for s in self.samples:
if s.state is not None:
return False
for s in self.samples:
if s.state.id != state_for_comparison.id:
return False
return state_for_comparison
@property
def last_comment(self):
latest_event = self.latest_event
if latest_event:
if latest_event.comment:
return latest_event.comment
return ''
return 'No comment'
def get_sample(self, sample_name):
for sample in self.samples:
if sample.name == sample_name:
return sample
return None
@property
def is_unsubmitted(self):
return self.state in [self.states.REJECTED, self.states.NEW]
@property
def is_rejected(self):
return self.state == self.states.REJECTED
@property
def is_submitted(self):
return self.state == self.states.SUBMITTED
@property
def is_new(self):
return self.state == self.states.NEW
@property
def is_complete(self):
return self.state == self.states.COMPLETE
@property
def samples_without_library_destinations(self):
# Return all samples that are not associated with a library
samples = []
for sample in self.samples:
if not sample.library:
samples.append(sample)
return samples
@property
def samples_with_bar_code(self):
# Return all samples that have associated bar code
samples = []
for sample in self.samples:
if sample.bar_code:
samples.append(sample)
return samples
def send_email_notification(self, trans, common_state, final_state=False):
# Check if an email notification is configured to be sent when the samples
# are in this state
if self.notification and common_state.id not in self.notification['sample_states']:
return
comments = ''
# Send email
if trans.app.config.smtp_server is not None and self.notification and self.notification['email']:
body = """
Galaxy Sample Tracking Notification
===================================
User: %(user)s
Sequencing request: %(request_name)s
Sequencer configuration: %(request_type)s
Sequencing request state: %(request_state)s
Number of samples: %(num_samples)s
All samples in state: %(sample_state)s
"""
values = dict(user=self.user.email,
request_name=self.name,
request_type=self.type.name,
request_state=self.state,
num_samples=str(len(self.samples)),
sample_state=common_state.name,
create_time=self.create_time,
submit_time=self.create_time)
body = body % values
# check if this is the final state of the samples
if final_state:
txt = "Sample Name -> Data Library/Folder\r\n"
for s in self.samples:
if s.library:
library_name = s.library.name
folder_name = s.folder.name
else:
library_name = 'No target data library'
folder_name = 'No target data library folder'
txt = txt + "%s -> %s/%s\r\n" % (s.name, library_name, folder_name)
body = body + txt
to = self.notification['email']
frm = trans.app.config.email_from
if frm is None:
host = trans.request.host.split(':')[0]
if host in ['localhost', '127.0.0.1', '0.0.0.0']:
host = socket.getfqdn()
frm = 'galaxy-no-reply@' + host
subject = "Galaxy Sample Tracking notification: '%s' sequencing request" % self.name
try:
send_mail(frm, to, subject, body, trans.app.config)
comments = "Email notification sent to %s." % ", ".join(to).strip().strip(',')
except Exception as e:
comments = "Email notification failed. (%s)" % str(e)
# update the request history with the email notification event
elif not trans.app.config.smtp_server:
comments = "Email notification failed as SMTP server not set in config file"
if comments:
event = RequestEvent(self, self.state, comments)
trans.sa_session.add(event)
trans.sa_session.flush()
return comments
class RequestEvent(object):
def __init__(self, request=None, request_state=None, comment=''):
self.request = request
self.state = request_state
self.comment = comment
class ExternalService(object):
data_transfer_protocol = Bunch(HTTP='http',
HTTPS='https',
SCP='scp')
def __init__(self, name=None, description=None, external_service_type_id=None, version=None, form_definition_id=None, form_values_id=None, deleted=None):
self.name = name
self.description = description
self.external_service_type_id = external_service_type_id
self.version = version
self.form_definition_id = form_definition_id
self.form_values_id = form_values_id
self.deleted = deleted
self.label = None # Used in the request_type controller's __build_external_service_select_field() method
def get_external_service_type(self, trans):
return trans.app.external_service_types.all_external_service_types[self.external_service_type_id]
def load_data_transfer_settings(self, trans):
trans.app.external_service_types.reload(self.external_service_type_id)
self.data_transfer = {}
external_service_type = self.get_external_service_type(trans)
for data_transfer_protocol, data_transfer_obj in external_service_type.data_transfer.items():
if data_transfer_protocol == self.data_transfer_protocol.SCP:
scp_configs = {}
automatic_transfer = data_transfer_obj.config.get('automatic_transfer', 'false')
scp_configs['automatic_transfer'] = galaxy.util.string_as_bool(automatic_transfer)
scp_configs['host'] = self.form_values.content.get(data_transfer_obj.config.get('host', ''), '')
scp_configs['user_name'] = self.form_values.content.get(data_transfer_obj.config.get('user_name', ''), '')
scp_configs['password'] = self.form_values.content.get(data_transfer_obj.config.get('password', ''), '')
scp_configs['data_location'] = self.form_values.content.get(data_transfer_obj.config.get('data_location', ''), '')
scp_configs['rename_dataset'] = self.form_values.content.get(data_transfer_obj.config.get('rename_dataset', ''), '')
self.data_transfer[self.data_transfer_protocol.SCP] = scp_configs
if data_transfer_protocol == self.data_transfer_protocol.HTTP:
http_configs = {}
automatic_transfer = data_transfer_obj.config.get('automatic_transfer', 'false')
http_configs['automatic_transfer'] = galaxy.util.string_as_bool(automatic_transfer)
self.data_transfer[self.data_transfer_protocol.HTTP] = http_configs
def populate_actions(self, trans, item, param_dict=None):
return self.get_external_service_type(trans).actions.populate(self, item, param_dict=param_dict)
class RequestType(object, Dictifiable):
dict_collection_visible_keys = ['id', 'name', 'desc']
dict_element_visible_keys = ['id', 'name', 'desc', 'request_form_id', 'sample_form_id']
rename_dataset_options = Bunch(NO='Do not rename',
SAMPLE_NAME='Preprend sample name',
EXPERIMENT_NAME='Prepend experiment name',
EXPERIMENT_AND_SAMPLE_NAME='Prepend experiment and sample name')
permitted_actions = get_permitted_actions(filter='REQUEST_TYPE')
def __init__(self, name=None, desc=None, request_form=None, sample_form=None):
self.name = name
self.desc = desc
self.request_form = request_form
self.sample_form = sample_form
@property
def external_services(self):
external_services = []
for rtesa in self.external_service_associations:
external_services.append(rtesa.external_service)
return external_services
def get_external_service(self, external_service_type_id):
for rtesa in self.external_service_associations:
if rtesa.external_service.external_service_type_id == external_service_type_id:
return rtesa.external_service
return None
def get_external_services_for_manual_data_transfer(self, trans):
'''Returns all external services that use manual data transfer'''
external_services = []
for rtesa in self.external_service_associations:
external_service = rtesa.external_service
# load data transfer settings
external_service.load_data_transfer_settings(trans)
if external_service.data_transfer:
for transfer_type, transfer_type_settings in external_service.data_transfer.items():
if not transfer_type_settings['automatic_transfer']:
external_services.append(external_service)
return external_services
def delete_external_service_associations(self, trans):
'''Deletes all external service associations.'''
flush_needed = False
for rtesa in self.external_service_associations:
trans.sa_session.delete(rtesa)
flush_needed = True
if flush_needed:
trans.sa_session.flush()
def add_external_service_association(self, trans, external_service):
rtesa = trans.model.RequestTypeExternalServiceAssociation(self, external_service)
trans.sa_session.add(rtesa)
trans.sa_session.flush()
@property
def final_sample_state(self):
# The states mapper for this object orders ascending
return self.states[-1]
@property
def run_details(self):
if self.run:
# self.run[0] is [RequestTypeRunAssociation]
return self.run[0]
return None
def get_template_widgets(self, trans, get_contents=True):
# See if we have any associated templates. The get_contents param
# is passed by callers that are inheriting a template - these are
# usually new samples for which we want to include template fields,
# but not necessarily the contents of the inherited template.
rtra = self.run_details
if rtra:
run = rtra.run
template = run.template
if get_contents:
# See if we have any field contents
info = run.info
if info:
return template.get_widgets(trans.user, contents=info.content)
return template.get_widgets(trans.user)
return []
class RequestTypeExternalServiceAssociation(object):
def __init__(self, request_type, external_service):
self.request_type = request_type
self.external_service = external_service
class RequestTypePermissions(object):
def __init__(self, action, request_type, role):
self.action = action
self.request_type = request_type
self.role = role
class Sample(object, Dictifiable):
# The following form_builder classes are supported by the Sample class.
supported_field_types = [CheckboxField, SelectField, TextField, WorkflowField, WorkflowMappingField, HistoryField]
bulk_operations = Bunch(CHANGE_STATE='Change state',
SELECT_LIBRARY='Select data library and folder')
dict_collection_visible_keys = ['id', 'name']
def __init__(self, name=None, desc=None, request=None, form_values=None, bar_code=None, library=None, folder=None, workflow=None, history=None):
self.name = name
self.desc = desc
self.request = request
self.values = form_values
self.bar_code = bar_code
self.library = library
self.folder = folder
self.history = history
self.workflow = workflow
@property
def state(self):
latest_event = self.latest_event
if latest_event:
return latest_event.state
return None
@property
def latest_event(self):
if self.events:
return self.events[0]
return None
@property
def adding_to_library_dataset_files(self):
adding_to_library_datasets = []
for dataset in self.datasets:
if dataset.status == SampleDataset.transfer_status.ADD_TO_LIBRARY:
adding_to_library_datasets.append(dataset)
return adding_to_library_datasets
@property
def inprogress_dataset_files(self):
inprogress_datasets = []
for dataset in self.datasets:
if dataset.status not in [SampleDataset.transfer_status.NOT_STARTED, SampleDataset.transfer_status.COMPLETE]:
inprogress_datasets.append(dataset)
return inprogress_datasets
@property
def queued_dataset_files(self):
queued_datasets = []
for dataset in self.datasets:
if dataset.status == SampleDataset.transfer_status.IN_QUEUE:
queued_datasets.append(dataset)
return queued_datasets
@property
def transfer_error_dataset_files(self):
transfer_error_datasets = []
for dataset in self.datasets:
if dataset.status == SampleDataset.transfer_status.ERROR:
transfer_error_datasets.append(dataset)
return transfer_error_datasets
@property
def transferred_dataset_files(self):
transferred_datasets = []
for dataset in self.datasets:
if dataset.status == SampleDataset.transfer_status.COMPLETE:
transferred_datasets.append(dataset)
return transferred_datasets
@property
def transferring_dataset_files(self):
transferring_datasets = []
for dataset in self.datasets:
if dataset.status == SampleDataset.transfer_status.TRANSFERRING:
transferring_datasets.append(dataset)
return transferring_datasets
@property
def untransferred_dataset_files(self):
untransferred_datasets = []
for dataset in self.datasets:
if dataset.status != SampleDataset.transfer_status.COMPLETE:
untransferred_datasets.append(dataset)
return untransferred_datasets
@property
def run_details(self):
# self.runs is a list of SampleRunAssociations ordered descending on update_time.
if self.runs:
# Always use the latest run details template, self.runs[0] is a SampleRunAssociation
return self.runs[0]
# Inherit this sample's RequestType run details, if one exists.
return self.request.type.run_details
def get_template_widgets(self, trans, get_contents=True):
# Samples have a one-to-many relationship with run details, so we return the
# widgets for last associated template. The get_contents param will populate
# the widget fields with values from the template inherited from the sample's
# RequestType.
template = None
if self.runs:
# The self.runs mapper orders descending on update_time.
run = self.runs[0].run
template = run.template
if template is None:
# There are no run details associated with this sample, so inherit the
# run details template from the sample's RequestType.
rtra = self.request.type.run_details
if rtra:
run = rtra.run
template = run.template
if template:
if get_contents:
# See if we have any field contents
info = run.info
if info:
return template.get_widgets(trans.user, contents=info.content)
return template.get_widgets(trans.user)
return []
def populate_external_services(self, param_dict=None, trans=None):
if self.request and self.request.type:
return [service.populate_actions(item=self, param_dict=param_dict, trans=trans) for service in self.request.type.external_services]
class SampleState(object):
def __init__(self, name=None, desc=None, request_type=None):
self.name = name
self.desc = desc
self.request_type = request_type
class SampleEvent(object):
def __init__(self, sample=None, sample_state=None, comment=''):
self.sample = sample
self.state = sample_state
self.comment = comment
class SampleDataset(object):
transfer_status = Bunch(NOT_STARTED='Not started',
IN_QUEUE='In queue',
TRANSFERRING='Transferring dataset',
ADD_TO_LIBRARY='Adding to data library',
COMPLETE='Complete',
ERROR='Error')
def __init__(self, sample=None, name=None, file_path=None, status=None, error_msg=None, size=None, external_service=None):
self.sample = sample
self.name = name
self.file_path = file_path
self.status = status
self.error_msg = error_msg
self.size = size
self.external_service = external_service
class Run(object):
def __init__(self, form_definition, form_values, subindex=None):
self.template = form_definition
self.info = form_values
self.subindex = subindex
class RequestTypeRunAssociation(object):
def __init__(self, request_type, run):
self.request_type = request_type
self.run = run
class SampleRunAssociation(object):
def __init__(self, sample, run):
self.sample = sample
self.run = run
class UserAddress(object):
def __init__(self, user=None, desc=None, name=None, institution=None,
address=None, city=None, state=None, postal_code=None,
@@ -5370,7 +4873,6 @@ class LibraryDatasetCollectionAnnotationAssociation(object):
# Item rating classes.
class ItemRatingAssociation(object):
def __init__(self, id=None, user=None, item=None, rating=0):
self.id = id
@@ -5418,7 +4920,7 @@ class LibraryDatasetCollectionRatingAssociation(ItemRatingAssociation):
self.dataset_collection = dataset_collection
# Data Manager Classes
# Data manager classes.
class DataManagerHistoryAssociation(object):
def __init__(self, id=None, history=None, user=None):
self.id = id
@@ -5431,7 +4933,6 @@ class DataManagerJobAssociation(object):
self.id = id
self.job = job
self.data_manager_id = data_manager_id
# end of Data Manager Classes
class UserPreference (object):
-229
View File
@@ -1013,45 +1013,6 @@ model.FormDefinition.table = Table(
Column("type", TrimmedString(255), index=True),
Column("layout", JSONType()))
model.ExternalService.table = Table(
"external_service", metadata,
Column("id", Integer, primary_key=True),
Column("create_time", DateTime, default=now),
Column("update_time", DateTime, default=now, onupdate=now),
Column("name", TrimmedString(255), nullable=False),
Column("description", TEXT),
Column("external_service_type_id", TrimmedString(255), nullable=False),
Column("version", TrimmedString(255)),
Column("form_definition_id", Integer, ForeignKey("form_definition.id"), index=True),
Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True),
Column("deleted", Boolean, index=True, default=False))
model.RequestType.table = Table(
"request_type", metadata,
Column("id", Integer, primary_key=True),
Column("create_time", DateTime, default=now),
Column("update_time", DateTime, default=now, onupdate=now),
Column("name", TrimmedString(255), nullable=False),
Column("desc", TEXT),
Column("request_form_id", Integer, ForeignKey("form_definition.id"), index=True),
Column("sample_form_id", Integer, ForeignKey("form_definition.id"), index=True),
Column("deleted", Boolean, index=True, default=False))
model.RequestTypeExternalServiceAssociation.table = Table(
"request_type_external_service_association", metadata,
Column("id", Integer, primary_key=True),
Column("request_type_id", Integer, ForeignKey("request_type.id"), index=True),
Column("external_service_id", Integer, ForeignKey("external_service.id"), index=True))
model.RequestTypePermissions.table = Table(
"request_type_permissions", metadata,
Column("id", Integer, primary_key=True),
Column("create_time", DateTime, default=now),
Column("update_time", DateTime, default=now, onupdate=now),
Column("action", TEXT),
Column("request_type_id", Integer, ForeignKey("request_type.id"), nullable=True, index=True),
Column("role_id", Integer, ForeignKey("role.id"), index=True))
model.FormValues.table = Table(
"form_values", metadata,
Column("id", Integer, primary_key=True),
@@ -1060,97 +1021,6 @@ model.FormValues.table = Table(
Column("form_definition_id", Integer, ForeignKey("form_definition.id"), index=True),
Column("content", JSONType()))
model.Request.table = Table(
"request", metadata,
Column("id", Integer, primary_key=True),
Column("create_time", DateTime, default=now),
Column("update_time", DateTime, default=now, onupdate=now),
Column("name", TrimmedString(255), nullable=False),
Column("desc", TEXT),
Column("notification", JSONType()),
Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True),
Column("request_type_id", Integer, ForeignKey("request_type.id"), index=True),
Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True),
Column("deleted", Boolean, index=True, default=False))
model.RequestEvent.table = Table(
"request_event", metadata,
Column("id", Integer, primary_key=True),
Column("create_time", DateTime, default=now),
Column("update_time", DateTime, default=now, onupdate=now),
Column("request_id", Integer, ForeignKey("request.id"), index=True),
Column("state", TrimmedString(255), index=True),
Column("comment", TEXT))
model.Sample.table = Table(
"sample", metadata,
Column("id", Integer, primary_key=True),
Column("create_time", DateTime, default=now),
Column("update_time", DateTime, default=now, onupdate=now),
Column("name", TrimmedString(255), nullable=False),
Column("desc", TEXT),
Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True),
Column("request_id", Integer, ForeignKey("request.id"), index=True),
Column("bar_code", TrimmedString(255), index=True),
Column("library_id", Integer, ForeignKey("library.id"), index=True),
Column("folder_id", Integer, ForeignKey("library_folder.id"), index=True),
Column("deleted", Boolean, index=True, default=False),
Column("workflow", JSONType, nullable=True),
Column("history_id", Integer, ForeignKey("history.id"), nullable=True))
model.SampleState.table = Table(
"sample_state", metadata,
Column("id", Integer, primary_key=True),
Column("create_time", DateTime, default=now),
Column("update_time", DateTime, default=now, onupdate=now),
Column("name", TrimmedString(255), nullable=False),
Column("desc", TEXT),
Column("request_type_id", Integer, ForeignKey("request_type.id"), index=True))
model.SampleEvent.table = Table(
"sample_event", metadata,
Column("id", Integer, primary_key=True),
Column("create_time", DateTime, default=now),
Column("update_time", DateTime, default=now, onupdate=now),
Column("sample_id", Integer, ForeignKey("sample.id"), index=True),
Column("sample_state_id", Integer, ForeignKey("sample_state.id"), index=True),
Column("comment", TEXT))
model.SampleDataset.table = Table(
"sample_dataset", metadata,
Column("id", Integer, primary_key=True),
Column("create_time", DateTime, default=now),
Column("update_time", DateTime, default=now, onupdate=now),
Column("sample_id", Integer, ForeignKey("sample.id"), index=True),
Column("name", TrimmedString(255), nullable=False),
Column("file_path", TEXT),
Column("status", TrimmedString(255), nullable=False),
Column("error_msg", TEXT),
Column("size", TrimmedString(255)),
Column("external_service_id", Integer, ForeignKey("external_service.id"), index=True))
model.Run.table = Table(
"run", metadata,
Column("id", Integer, primary_key=True),
Column("create_time", DateTime, default=now),
Column("update_time", DateTime, default=now, onupdate=now),
Column("form_definition_id", Integer, ForeignKey("form_definition.id"), index=True),
Column("form_values_id", Integer, ForeignKey("form_values.id"), index=True),
Column("deleted", Boolean, index=True, default=False),
Column("subindex", TrimmedString(255), index=True))
model.RequestTypeRunAssociation.table = Table(
"request_type_run_association", metadata,
Column("id", Integer, primary_key=True),
Column("request_type_id", Integer, ForeignKey("request_type.id"), index=True, nullable=False),
Column("run_id", Integer, ForeignKey("run.id"), index=True, nullable=False))
model.SampleRunAssociation.table = Table(
"sample_run_association", metadata,
Column("id", Integer, primary_key=True),
Column("sample_id", Integer, ForeignKey("sample.id"), index=True, nullable=False),
Column("run_id", Integer, ForeignKey("run.id"), index=True, nullable=False))
model.Page.table = Table(
"page", metadata,
Column("id", Integer, primary_key=True),
@@ -1495,80 +1365,11 @@ def simple_mapping(model, **kwds):
mapper(model, model.table, properties=kwds)
mapper(model.Sample, model.Sample.table, properties=dict(
events=relation(model.SampleEvent,
backref="sample",
order_by=desc(model.SampleEvent.table.c.update_time)),
datasets=relation(model.SampleDataset,
backref="sample",
order_by=desc(model.SampleDataset.table.c.update_time)),
values=relation(model.FormValues,
primaryjoin=(model.Sample.table.c.form_values_id == model.FormValues.table.c.id)),
request=relation(model.Request,
primaryjoin=(model.Sample.table.c.request_id == model.Request.table.c.id)),
folder=relation(model.LibraryFolder,
primaryjoin=(model.Sample.table.c.folder_id == model.LibraryFolder.table.c.id)),
library=relation(model.Library,
primaryjoin=(model.Sample.table.c.library_id == model.Library.table.c.id)),
history=relation(model.History,
primaryjoin=(model.Sample.table.c.history_id == model.History.table.c.id)),
))
mapper(model.FormValues, model.FormValues.table, properties=dict(
form_definition=relation(model.FormDefinition,
primaryjoin=(model.FormValues.table.c.form_definition_id == model.FormDefinition.table.c.id))
))
mapper(model.Request, model.Request.table, properties=dict(
values=relation(model.FormValues,
primaryjoin=(model.Request.table.c.form_values_id == model.FormValues.table.c.id)),
type=relation(model.RequestType,
primaryjoin=(model.Request.table.c.request_type_id == model.RequestType.table.c.id)),
user=relation(model.User,
primaryjoin=(model.Request.table.c.user_id == model.User.table.c.id),
backref="requests"),
samples=relation(model.Sample,
primaryjoin=(model.Request.table.c.id == model.Sample.table.c.request_id),
order_by=asc(model.Sample.table.c.id)),
events=relation(model.RequestEvent,
backref="request",
order_by=desc(model.RequestEvent.table.c.update_time))
))
mapper(model.RequestEvent, model.RequestEvent.table, properties=None)
mapper(model.ExternalService, model.ExternalService.table, properties=dict(
form_definition=relation(model.FormDefinition,
primaryjoin=(model.ExternalService.table.c.form_definition_id == model.FormDefinition.table.c.id)),
form_values=relation(model.FormValues,
primaryjoin=(model.ExternalService.table.c.form_values_id == model.FormValues.table.c.id))
))
mapper(model.RequestType, model.RequestType.table, properties=dict(
states=relation(model.SampleState,
backref="request_type",
primaryjoin=(model.RequestType.table.c.id == model.SampleState.table.c.request_type_id),
order_by=asc(model.SampleState.table.c.update_time)),
request_form=relation(model.FormDefinition,
primaryjoin=(model.RequestType.table.c.request_form_id == model.FormDefinition.table.c.id)),
sample_form=relation(model.FormDefinition,
primaryjoin=(model.RequestType.table.c.sample_form_id == model.FormDefinition.table.c.id)),
))
mapper(model.RequestTypeExternalServiceAssociation, model.RequestTypeExternalServiceAssociation.table, properties=dict(
request_type=relation(model.RequestType,
primaryjoin=((model.RequestTypeExternalServiceAssociation.table.c.request_type_id == model.RequestType.table.c.id)),
backref="external_service_associations"),
external_service=relation(model.ExternalService,
primaryjoin=(model.RequestTypeExternalServiceAssociation.table.c.external_service_id == model.ExternalService.table.c.id))
))
mapper(model.RequestTypePermissions, model.RequestTypePermissions.table, properties=dict(
request_type=relation(model.RequestType, backref="actions"),
role=relation(model.Role, backref="request_type_actions")
))
mapper(model.FormDefinition, model.FormDefinition.table, properties=dict(
current=relation(model.FormDefinitionCurrent,
primaryjoin=(model.FormDefinition.table.c.form_definition_current_id == model.FormDefinitionCurrent.table.c.id))
@@ -1584,36 +1385,6 @@ mapper(model.FormDefinitionCurrent, model.FormDefinitionCurrent.table, propertie
primaryjoin=(model.FormDefinitionCurrent.table.c.latest_form_id == model.FormDefinition.table.c.id))
))
mapper(model.SampleEvent, model.SampleEvent.table, properties=dict(
state=relation(model.SampleState,
primaryjoin=(model.SampleEvent.table.c.sample_state_id == model.SampleState.table.c.id)),
))
mapper(model.SampleState, model.SampleState.table, properties=None)
mapper(model.SampleDataset, model.SampleDataset.table, properties=dict(
external_service=relation(model.ExternalService,
primaryjoin=(model.SampleDataset.table.c.external_service_id == model.ExternalService.table.c.id))
))
mapper(model.SampleRunAssociation, model.SampleRunAssociation.table, properties=dict(
sample=relation(model.Sample, backref="runs", order_by=desc(model.Run.table.c.update_time)),
run=relation(model.Run, backref="samples", order_by=asc(model.Sample.table.c.id))
))
mapper(model.RequestTypeRunAssociation, model.RequestTypeRunAssociation.table, properties=dict(
request_type=relation(model.RequestType, backref="run"),
run=relation(model.Run, backref="request_type")
))
mapper(model.Run, model.Run.table, properties=dict(
template=relation(model.FormDefinition,
primaryjoin=(model.Run.table.c.form_definition_id == model.FormDefinition.table.c.id)),
info=relation(model.FormValues,
primaryjoin=(model.Run.table.c.form_values_id == model.FormValues.table.c.id))
))
mapper(model.UserAddress, model.UserAddress.table, properties=dict(
user=relation(model.User,
primaryjoin=(model.UserAddress.table.c.user_id == model.User.table.c.id),
@@ -1,58 +0,0 @@
class DataTransferFactory(object):
type = None
def parse(self):
pass
class ScpDataTransferFactory(DataTransferFactory):
type = 'scp'
def __init__(self):
pass
def parse(self, config_file, elem):
self.config = {}
# TODO: The 'automatic_transfer' setting is for future use. If set to True, we will need to
# ensure the sample has an associated destination data library before it moves to a certain state
# ( e.g., Run started ).
self.config['automatic_transfer'] = elem.get('automatic_transfer')
self.config['host'] = elem.get('host')
self.config['user_name'] = elem.get('user_name')
self.config['password'] = elem.get('password')
self.config['data_location'] = elem.get('data_location')
# 'rename_dataset' is optional and it may not be defined in all external types
# It is only used is AB SOLiD external service type for now
rename_dataset = elem.get('rename_dataset', None)
if rename_dataset:
self.config['rename_dataset'] = rename_dataset
# Validate
for name, value in self.config.items():
assert value, "'%s' attribute missing in 'data_transfer' element of type 'scp' in external_service_type xml config file: '%s'." % (name, config_file)
class HttpDataTransferFactory(DataTransferFactory):
type = 'http'
def __init__(self):
pass
def parse(self, config_file, elem):
self.config = {}
self.config['automatic_transfer'] = elem.get('automatic_transfer')
# Validate
for name, value in self.config.items():
assert value, "'%s' attribute missing in 'data_transfer' element of type 'http' in external_service_type xml config file: '%s'." % (name, config_file)
class FtpDataTransferFactory(DataTransferFactory):
type = 'ftp'
def __init__(self):
pass
def parse(self, elem):
pass
data_transfer_factories = dict([(data_transfer.type, data_transfer()) for data_transfer in [ScpDataTransferFactory, HttpDataTransferFactory, FtpDataTransferFactory]])
@@ -1,127 +0,0 @@
import logging
import os
from galaxy import (
model,
util
)
from galaxy.external_services.service import ExternalServiceActionsGroup
from galaxy.forms.forms import form_factory
from galaxy.sample_tracking.data_transfer import data_transfer_factories
from galaxy.util.odict import odict
log = logging.getLogger(__name__)
class ExternalServiceTypeNotFoundException(Exception):
pass
class ExternalServiceTypesCollection(object):
def __init__(self, config_filename, root_dir, app):
self.all_external_service_types = odict()
self.root_dir = root_dir
self.app = app
try:
self.load_all(config_filename)
except Exception:
log.exception("ExternalServiceTypesCollection error reading %s", config_filename)
def load_all(self, config_filename):
self.visible_external_service_types = []
tree = util.parse_xml(config_filename)
root = tree.getroot()
for elem in root:
try:
if elem.tag == 'external_service_type':
file_path = elem.get("file")
visible = util.string_as_bool(elem.get("visible"))
external_service_type = self.load_external_service_type(os.path.join(self.root_dir, file_path), visible)
self.all_external_service_types[external_service_type.id] = external_service_type
log.debug("Loaded external_service_type: %s %s" % (external_service_type.name, external_service_type.config_version))
if visible:
self.visible_external_service_types.append(external_service_type.id)
except Exception:
log.exception("error reading external_service_type from path: %s", file_path)
def load_external_service_type(self, config_file, visible=True):
# Parse XML configuration file and get the root element
tree = util.parse_xml(config_file)
root = tree.getroot()
return ExternalServiceType(config_file, root, visible)
def reload(self, external_service_type_id):
"""
Attempt to reload the external_service_type identified by 'external_service_type_id', if successful
replace the old external_service_type.
"""
if external_service_type_id not in self.all_external_service_types.keys():
raise ExternalServiceTypeNotFoundException("No external_service_type with id %s" % external_service_type_id)
old_external_service_type = self.all_external_service_types[external_service_type_id]
new_external_service_type = self.load_external_service_type(old_external_service_type.config_file)
self.all_external_service_types[external_service_type_id] = new_external_service_type
log.debug("Reloaded external_service_type %s" % (external_service_type_id))
return new_external_service_type
class ExternalServiceType(object):
def __init__(self, external_service_type_xml_config, root, visible=True):
self.config_file = external_service_type_xml_config
self.parse(root)
self.visible = visible
root.clear()
def parse(self, root):
# Get the name
self.name = root.get("name")
if not self.name:
raise Exception("Missing external_service_type 'name'")
# Get the UNIQUE id for the tool
self.id = root.get("id")
if not self.id:
raise Exception("Missing external_service_type 'id'")
self.config_version = root.get("version")
if not self.config_version:
self.config_version = '1.0.0'
self.description = util.xml_text(root, "description")
self.version = util.xml_text(root.find("version"))
# parse the form
self.form_definition = form_factory.from_elem(root.find('form'))
self.parse_data_transfer_settings(root)
self.parse_run_details(root)
# external services actions
self.actions = ExternalServiceActionsGroup.from_elem(root.find('actions'), parent=self)
def parse_data_transfer_settings(self, root):
self.data_transfer = {}
data_transfer_settings_elem = root.find('data_transfer_settings')
# Currently only data transfer using scp or http is supported.
for data_transfer_elem in data_transfer_settings_elem.findall("data_transfer"):
if data_transfer_elem.get('protocol') == model.ExternalService.data_transfer_protocol.SCP:
scp_data_transfer = data_transfer_factories[model.ExternalService.data_transfer_protocol.SCP]
scp_data_transfer.parse(self.config_file, data_transfer_elem)
self.data_transfer[model.ExternalService.data_transfer_protocol.SCP] = scp_data_transfer
if data_transfer_elem.get('protocol') == model.ExternalService.data_transfer_protocol.HTTP:
http_data_transfer = data_transfer_factories[model.ExternalService.data_transfer_protocol.HTTP]
http_data_transfer.parse(self.config_file, data_transfer_elem)
self.data_transfer[model.ExternalService.data_transfer_protocol.HTTP] = http_data_transfer
def parse_run_details(self, root):
self.run_details = {}
run_details_elem = root.find('run_details')
if run_details_elem is not None:
results_elem = run_details_elem.find('results')
if results_elem is not None:
# Get the list of resulting datatypes
# TODO: the 'results_urls' attribute is only useful if the transfer protocol is http(s), so check if that is the case.
self.run_details['results'], self.run_details['results_urls'] = self.parse_run_details_results(results_elem)
def parse_run_details_results(self, root):
datatypes_dict = {}
urls_dict = {}
for datatype_elem in root.findall("dataset"):
name = datatype_elem.get('name')
datatypes_dict[name] = datatype_elem.get('datatype')
urls_dict[name] = datatype_elem.get('url', None)
return datatypes_dict, urls_dict
@@ -1,33 +0,0 @@
"""
RequestType
"""
from galaxy.model import RequestType
from .sample import sample_state_factory
RENAME_DATASET_OPTIONS = dict([(f_type.lower(), f_descript) for f_type, f_descript in RequestType.rename_dataset_options.items()])
class RequestTypeFactory(object):
def __init__(self, sample_state_factory, rename_dataset_options):
self.sample_state_factory = sample_state_factory
self.rename_dataset_options = rename_dataset_options
def new(self, name, request_form, sample_form, external_service, description=None, sample_states=None):
"""Return new RequestType."""
assert name, 'RequestType requires a name'
return RequestType(name=name, desc=description, request_form=request_form, sample_form=sample_form, external_service=external_service)
def from_elem(self, elem, request_form, sample_form, external_service):
"""Return RequestType created from an xml string."""
name = elem.get('name', '')
description = elem.get('description', '')
rval = self.new(name, request_form, sample_form, external_service=external_service, description=description)
# load states
sample_states_elem = elem.find('sample_states')
if sample_states_elem:
for sample_state_elem in sample_states_elem.findall('state'):
self.sample_state_factory.from_elem(rval, sample_state_elem) # should this be the return value??
return rval
request_type_factory = RequestTypeFactory(sample_state_factory, RENAME_DATASET_OPTIONS)
-26
View File
@@ -1,26 +0,0 @@
"""
Sample
"""
from galaxy.model import SampleState
class SampleStateFactory(object):
def new(self, request_type, name, description=None):
"""
Return new SampleState.
"""
assert name, 'SampleState requires a name'
if description is None:
description = ''
return SampleState(name=name, desc=description, request_type=request_type)
def from_elem(self, request_type, elem):
"""
Return SampleState created from an xml string.
"""
name = elem.get('name', None)
description = elem.get('description', None)
return self.new(request_type, name, description=description)
sample_state_factory = SampleStateFactory()
+1 -27
View File
@@ -30,9 +30,7 @@ class RBACAgent:
LIBRARY_ACCESS=Action("access library", "Restrict access to this library to only users having associated role", "restrict"),
LIBRARY_ADD=Action("add library item", "Users having associated role can add library items to this library item", "grant"),
LIBRARY_MODIFY=Action("modify library item", "Users having associated role can modify this library item", "grant"),
LIBRARY_MANAGE=Action("manage library permissions", "Users having associated role can manage roles associated with permissions on this library item", "grant"),
# Request type permissions
REQUEST_TYPE_ACCESS=Action("access request_type", "Restrict access to this request type to only users having associated role", "restrict")
LIBRARY_MANAGE=Action("manage library permissions", "Users having associated role can manage roles associated with permissions on this library item", "grant")
)
def get_action(self, name, default=None):
@@ -79,9 +77,6 @@ class RBACAgent:
def get_private_user_role(self, user):
raise Exception("Unimplemented Method")
def get_accessible_request_types(self, trans, user):
raise Exception("Unimplemented Method")
def user_set_default_permissions(self, user, permissions={}, history=False, dataset=False):
raise Exception("Unimplemented Method")
@@ -940,27 +935,6 @@ class GalaxyRBACAgent(RBACAgent):
permissions[action] = [item_permission.role]
return permissions
def get_accessible_request_types(self, trans, user):
"""Return all RequestTypes that the received user has permission to access."""
accessible_request_types = []
current_user_role_ids = [role.id for role in user.all_roles()]
request_type_access_action = self.permitted_actions.REQUEST_TYPE_ACCESS.action
restricted_request_type_ids = [rtp.request_type_id for rtp in trans.sa_session.query(trans.model.RequestTypePermissions)
.filter(trans.model.RequestTypePermissions.table.c.action == request_type_access_action).distinct()]
accessible_restricted_request_type_ids = [rtp.request_type_id for rtp in trans.sa_session.query(trans.model.RequestTypePermissions)
.filter(and_(
trans.model.RequestTypePermissions.table.c.action == request_type_access_action,
trans.model.RequestTypePermissions.table.c.role_id.in_(current_user_role_ids)))]
# Filter to get libraries accessible by the current user. Get both
# public libraries and restricted libraries accessible by the current user.
for request_type in trans.sa_session.query(trans.model.RequestType) \
.filter(and_(trans.model.RequestType.table.c.deleted == false(),
(or_(not_(trans.model.RequestType.table.c.id.in_(restricted_request_type_ids)),
trans.model.RequestType.table.c.id.in_(accessible_restricted_request_type_ids))))) \
.order_by(trans.app.model.RequestType.name):
accessible_request_types.append(request_type)
return accessible_request_types
def copy_dataset_permissions(self, src, dst):
if not isinstance(src, self.model.Dataset):
src = src.dataset
+3 -695
View File
@@ -10,10 +10,7 @@ from paste.httpexceptions import (
HTTPNotImplemented,
HTTPRequestRangeNotSatisfiable
)
from six import (
string_types,
text_type
)
from six import string_types
from sqlalchemy import true
from galaxy import (
@@ -48,15 +45,8 @@ from galaxy.web import (
)
from galaxy.web.form_builder import (
AddressField,
build_select_field,
CheckboxField,
HistoryField,
PasswordField,
SelectField,
TextArea,
TextField,
WorkflowField,
WorkflowMappingField
PasswordField
)
from galaxy.workflow.modules import WorkflowModuleInjector
@@ -261,7 +251,7 @@ class JSAppLauncher(BaseUIController):
#: window-scoped js function to call to start the app (will be passed options, bootstrapped)
DEFAULT_ENTRY_FN = "app"
#: keys used when serializing current user for bootstrapped data
USER_BOOTSTRAP_KEYS = ('id', 'email', 'username', 'is_admin', 'tags_used', 'requests',
USER_BOOTSTRAP_KEYS = ('id', 'email', 'username', 'is_admin', 'tags_used',
'total_disk_usage', 'nice_total_disk_usage', 'quota_percent', 'preferences')
def __init__(self, app):
@@ -1299,580 +1289,6 @@ class UsesFormDefinitionsMixin:
else:
return [fdc.latest_form for fdc in fdc_list if fdc.latest_form.type == form_type]
def get_all_forms_by_type(self, trans, cntrller, form_type):
forms = self.get_all_forms(trans,
filter=dict(deleted=False),
form_type=form_type)
if not forms:
message = "There are no forms on which to base the template, so create a form and then add the template."
return trans.response.send_redirect(web.url_for(controller='forms',
action='create_form_definition',
cntrller=cntrller,
message=message,
status='done',
form_type=form_type))
return forms
@web.expose
def add_template(self, trans, cntrller, item_type, form_type, **kwd):
params = util.Params(kwd)
form_id = params.get('form_id', 'none')
message = util.restore_text(params.get('message', ''))
action = ''
status = params.get('status', 'done')
forms = self.get_all_forms_by_type(trans, cntrller, form_type)
# form_type must be one of: RUN_DETAILS_TEMPLATE, LIBRARY_INFO_TEMPLATE
in_library = form_type == trans.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE
in_sample_tracking = form_type == trans.model.FormDefinition.types.RUN_DETAILS_TEMPLATE
if in_library:
show_deleted = util.string_as_bool(params.get('show_deleted', False))
use_panels = util.string_as_bool(params.get('use_panels', False))
library_id = params.get('library_id', None)
folder_id = params.get('folder_id', None)
ldda_id = params.get('ldda_id', None)
is_admin = trans.user_is_admin() and cntrller in ['library_admin', 'requests_admin']
current_user_roles = trans.get_current_user_roles()
elif in_sample_tracking:
request_type_id = params.get('request_type_id', None)
sample_id = params.get('sample_id', None)
try:
if in_sample_tracking:
item, item_desc, action, id = self.get_item_and_stuff(trans,
item_type=item_type,
request_type_id=request_type_id,
sample_id=sample_id)
elif in_library:
item, item_desc, action, id = self.get_item_and_stuff(trans,
item_type=item_type,
library_id=library_id,
folder_id=folder_id,
ldda_id=ldda_id,
is_admin=is_admin)
if not item:
message = "Invalid %s id ( %s ) specified." % (item_desc, str(id))
if in_sample_tracking:
return trans.response.send_redirect(web.url_for(controller='request_type',
action='browse_request_types',
id=request_type_id,
message=util.sanitize_text(message),
status='error'))
if in_library:
return trans.response.send_redirect(web.url_for(controller='library_common',
action='browse_library',
cntrller=cntrller,
id=library_id,
show_deleted=show_deleted,
message=util.sanitize_text(message),
status='error'))
except ValueError:
# At this point, the client has already redirected, so this is just here to prevent the unnecessary traceback
return None
if in_library:
# Make sure the user is authorized to do what they are trying to do.
authorized = True
if not (is_admin or trans.app.security_agent.can_modify_library_item(current_user_roles, item)):
authorized = False
unauthorized = 'modify'
if not (is_admin or trans.app.security_agent.can_access_library_item(current_user_roles, item, trans.user)):
authorized = False
unauthorized = 'access'
if not authorized:
message = "You are not authorized to %s %s '%s'." % (unauthorized, item_desc, item.name)
return trans.response.send_redirect(web.url_for(controller='library_common',
action='browse_library',
cntrller=cntrller,
id=library_id,
show_deleted=show_deleted,
message=util.sanitize_text(message),
status='error'))
# If the inheritable checkbox is checked, the param will be in the request
inheritable = CheckboxField.is_checked(params.get('inheritable', ''))
if params.get('add_template_button', False):
if form_id not in [None, 'None', 'none']:
form = trans.sa_session.query(trans.app.model.FormDefinition).get(trans.security.decode_id(form_id))
form_values = trans.app.model.FormValues(form, {})
trans.sa_session.add(form_values)
trans.sa_session.flush()
if item_type == 'library':
assoc = trans.model.LibraryInfoAssociation(item, form, form_values, inheritable=inheritable)
elif item_type == 'folder':
assoc = trans.model.LibraryFolderInfoAssociation(item, form, form_values, inheritable=inheritable)
elif item_type == 'ldda':
assoc = trans.model.LibraryDatasetDatasetInfoAssociation(item, form, form_values)
elif item_type in ['request_type', 'sample']:
run = trans.model.Run(form, form_values)
trans.sa_session.add(run)
trans.sa_session.flush()
if item_type == 'request_type':
# Delete current RequestTypeRunAssociation, if one exists.
rtra = item.run_details
if rtra:
trans.sa_session.delete(rtra)
trans.sa_session.flush()
# Add the new RequestTypeRunAssociation. Templates associated with a RequestType
# are automatically inherited to the samples.
assoc = trans.model.RequestTypeRunAssociation(item, run)
elif item_type == 'sample':
assoc = trans.model.SampleRunAssociation(item, run)
trans.sa_session.add(assoc)
trans.sa_session.flush()
message = 'A template based on the form "%s" has been added to this %s.' % (form.name, item_desc)
new_kwd = dict(action=action,
cntrller=cntrller,
message=util.sanitize_text(message),
status='done')
if in_sample_tracking:
new_kwd.update(dict(controller='request_type',
request_type_id=request_type_id,
sample_id=sample_id,
id=id))
return trans.response.send_redirect(web.url_for(**new_kwd))
elif in_library:
new_kwd.update(dict(controller='library_common',
use_panels=use_panels,
library_id=library_id,
folder_id=folder_id,
id=id,
show_deleted=show_deleted))
return trans.response.send_redirect(web.url_for(**new_kwd))
else:
message = "Select a form on which to base the template."
status = "error"
form_id_select_field = self.build_form_id_select_field(trans, forms, selected_value=kwd.get('form_id', 'none'))
try:
decoded_form_id = trans.security.decode_id(form_id)
except Exception:
decoded_form_id = None
if decoded_form_id:
for form in forms:
if decoded_form_id == form.id:
widgets = form.get_widgets(trans.user)
break
else:
widgets = []
new_kwd = dict(cntrller=cntrller,
item_name=item.name,
item_desc=item_desc,
item_type=item_type,
form_type=form_type,
widgets=widgets,
form_id_select_field=form_id_select_field,
message=message,
status=status)
if in_sample_tracking:
new_kwd.update(dict(request_type_id=request_type_id,
sample_id=sample_id))
elif in_library:
new_kwd.update(dict(use_panels=use_panels,
library_id=library_id,
folder_id=folder_id,
ldda_id=ldda_id,
inheritable_checked=inheritable,
show_deleted=show_deleted))
return trans.fill_template('/common/select_template.mako',
**new_kwd)
@web.expose
def edit_template(self, trans, cntrller, item_type, form_type, **kwd):
# Edit the template itself, keeping existing field contents, if any.
params = util.Params(kwd)
message = util.restore_text(params.get('message', ''))
edited = util.string_as_bool(params.get('edited', False))
action = ''
# form_type must be one of: RUN_DETAILS_TEMPLATE, LIBRARY_INFO_TEMPLATE
in_library = form_type == trans.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE
in_sample_tracking = form_type == trans.model.FormDefinition.types.RUN_DETAILS_TEMPLATE
if in_library:
show_deleted = util.string_as_bool(params.get('show_deleted', False))
use_panels = util.string_as_bool(params.get('use_panels', False))
library_id = params.get('library_id', None)
folder_id = params.get('folder_id', None)
ldda_id = params.get('ldda_id', None)
is_admin = trans.user_is_admin() and cntrller in ['library_admin', 'requests_admin']
current_user_roles = trans.get_current_user_roles()
elif in_sample_tracking:
request_type_id = params.get('request_type_id', None)
sample_id = params.get('sample_id', None)
try:
if in_library:
item, item_desc, action, id = self.get_item_and_stuff(trans,
item_type=item_type,
library_id=library_id,
folder_id=folder_id,
ldda_id=ldda_id,
is_admin=is_admin)
elif in_sample_tracking:
item, item_desc, action, id = self.get_item_and_stuff(trans,
item_type=item_type,
request_type_id=request_type_id,
sample_id=sample_id)
except ValueError:
return None
if in_library:
if not (is_admin or trans.app.security_agent.can_modify_library_item(current_user_roles, item)):
message = "You are not authorized to modify %s '%s'." % (item_desc, item.name)
return trans.response.send_redirect(web.url_for(controller='library_common',
action='browse_library',
cntrller=cntrller,
id=library_id,
show_deleted=show_deleted,
message=util.sanitize_text(message),
status='error'))
# An info_association must exist at this point
if in_library:
info_association, inherited = item.get_info_association(restrict=True)
elif in_sample_tracking:
# Here run_details is a RequestTypeRunAssociation
rtra = item.run_details
info_association = rtra.run
template = info_association.template
if edited:
# The form on which the template is based has been edited, so we need to update the
# info_association with the current form
fdc = trans.sa_session.query(trans.app.model.FormDefinitionCurrent).get(template.form_definition_current_id)
info_association.template = fdc.latest_form
trans.sa_session.add(info_association)
trans.sa_session.flush()
message = "The template for this %s has been updated with your changes." % item_desc
new_kwd = dict(action=action,
cntrller=cntrller,
id=id,
message=util.sanitize_text(message),
status='done')
if in_library:
new_kwd.update(dict(controller='library_common',
use_panels=use_panels,
library_id=library_id,
folder_id=folder_id,
show_deleted=show_deleted))
return trans.response.send_redirect(web.url_for(**new_kwd))
elif in_sample_tracking:
new_kwd.update(dict(controller='request_type',
request_type_id=request_type_id,
sample_id=sample_id))
return trans.response.send_redirect(web.url_for(**new_kwd))
# "template" is a FormDefinition, so since we're changing it, we need to use the latest version of it.
vars = dict(id=trans.security.encode_id(template.form_definition_current_id),
response_redirect=web.url_for(controller='request_type',
action='edit_template',
cntrller=cntrller,
item_type=item_type,
form_type=form_type,
edited=True,
**kwd))
return trans.response.send_redirect(web.url_for(controller='forms', action='edit_form_definition', **vars))
@web.expose
def edit_template_info(self, trans, cntrller, item_type, form_type, **kwd):
# Edit the contents of the template fields without altering the template itself.
params = util.Params(kwd)
# form_type must be one of: RUN_DETAILS_TEMPLATE, LIBRARY_INFO_TEMPLATE
in_library = form_type == trans.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE
in_sample_tracking = form_type == trans.model.FormDefinition.types.RUN_DETAILS_TEMPLATE
if in_library:
library_id = params.get('library_id', None)
folder_id = params.get('folder_id', None)
ldda_id = params.get('ldda_id', None)
show_deleted = util.string_as_bool(params.get('show_deleted', False))
use_panels = util.string_as_bool(params.get('use_panels', False))
is_admin = (trans.user_is_admin() and cntrller == 'library_admin')
current_user_roles = trans.get_current_user_roles()
elif in_sample_tracking:
request_type_id = params.get('request_type_id', None)
sample_id = params.get('sample_id', None)
sample = trans.sa_session.query(trans.model.Sample).get(trans.security.decode_id(sample_id))
message = util.restore_text(params.get('message', ''))
try:
if in_library:
item, item_desc, action, id = self.get_item_and_stuff(trans,
item_type=item_type,
library_id=library_id,
folder_id=folder_id,
ldda_id=ldda_id,
is_admin=is_admin)
elif in_sample_tracking:
item, item_desc, action, id = self.get_item_and_stuff(trans,
item_type=item_type,
request_type_id=request_type_id,
sample_id=sample_id)
except ValueError:
if cntrller == 'api':
trans.response.status = 400
return None
return None
if in_library:
if not (is_admin or trans.app.security_agent.can_modify_library_item(current_user_roles, item)):
message = "You are not authorized to modify %s '%s'." % (item_desc, item.name)
if cntrller == 'api':
trans.response.status = 400
return message
return trans.response.send_redirect(web.url_for(controller='library_common',
action='browse_library',
cntrller=cntrller,
id=library_id,
show_deleted=show_deleted,
message=util.sanitize_text(message),
status='error'))
# We need the type of each template field widget
widgets = item.get_template_widgets(trans)
# The list of widgets may include an AddressField which we need to save if it is new
for widget_dict in widgets:
widget = widget_dict['widget']
if isinstance(widget, AddressField):
value = util.restore_text(params.get(widget.name, ''))
if value == 'new':
if params.get('edit_info_button', False):
if self.field_param_values_ok(widget.name, 'AddressField', **kwd):
# Save the new address
address = trans.app.model.UserAddress(user=trans.user)
self.save_widget_field(trans, address, widget.name, **kwd)
widget.value = str(address.id)
else:
message = 'Required fields are missing contents.'
if cntrller == 'api':
trans.response.status = 400
return message
new_kwd = dict(action=action,
id=id,
message=util.sanitize_text(message),
status='error')
if in_library:
new_kwd.update(dict(controller='library_common',
cntrller=cntrller,
use_panels=use_panels,
library_id=library_id,
folder_id=folder_id,
show_deleted=show_deleted))
return trans.response.send_redirect(web.url_for(**new_kwd))
if in_sample_tracking:
new_kwd.update(dict(controller='request_type',
request_type_id=request_type_id,
sample_id=sample_id))
return trans.response.send_redirect(web.url_for(**new_kwd))
else:
# Form was submitted via refresh_on_change
widget.value = 'new'
elif value == text_type('none'):
widget.value = ''
else:
widget.value = value
elif isinstance(widget, CheckboxField):
# We need to check the value from kwd since util.Params would have munged the list if
# the checkbox is checked.
value = kwd.get(widget.name, '')
if CheckboxField.is_checked(value):
widget.value = 'true'
else:
widget.value = util.restore_text(params.get(widget.name, ''))
# Save updated template field contents
field_contents = self.clean_field_contents(widgets, **kwd)
if field_contents:
if in_library:
# In in a library, since information templates are inherited, the template fields can be displayed
# on the information page for a folder or ldda when it has no info_association object. If the user
# has added field contents on an inherited template via a parent's info_association, we'll need to
# create a new form_values and info_association for the current object. The value for the returned
# inherited variable is not applicable at this level.
info_association, inherited = item.get_info_association(restrict=True)
elif in_sample_tracking:
assoc = item.run_details
if item_type == 'request_type' and assoc:
# If we're dealing with a RequestType, assoc will be a ReuqestTypeRunAssociation.
info_association = assoc.run
elif item_type == 'sample' and assoc:
# If we're dealing with a Sample, assoc will be a SampleRunAssociation if the
# Sample has one. If the Sample does not have a SampleRunAssociation, assoc will
# be the Sample's RequestType RequestTypeRunAssociation, in which case we need to
# create a SampleRunAssociation using the inherited template from the RequestType.
if isinstance(assoc, trans.model.RequestTypeRunAssociation):
form_definition = assoc.run.template
new_form_values = trans.model.FormValues(form_definition, {})
trans.sa_session.add(new_form_values)
trans.sa_session.flush()
new_run = trans.model.Run(form_definition, new_form_values)
trans.sa_session.add(new_run)
trans.sa_session.flush()
sra = trans.model.SampleRunAssociation(item, new_run)
trans.sa_session.add(sra)
trans.sa_session.flush()
info_association = sra.run
else:
info_association = assoc.run
else:
info_association = None
if info_association:
template = info_association.template
info = info_association.info
form_values = trans.sa_session.query(trans.app.model.FormValues).get(info.id)
# Update existing content only if it has changed
flush_required = False
for field_contents_key, field_contents_value in field_contents.items():
if field_contents_key in form_values.content:
if form_values.content[field_contents_key] != field_contents_value:
flush_required = True
form_values.content[field_contents_key] = field_contents_value
else:
flush_required = True
form_values.content[field_contents_key] = field_contents_value
if flush_required:
trans.sa_session.add(form_values)
trans.sa_session.flush()
else:
if in_library:
# Inherit the next available info_association so we can get the template
info_association, inherited = item.get_info_association()
template = info_association.template
# Create a new FormValues object
form_values = trans.app.model.FormValues(template, field_contents)
trans.sa_session.add(form_values)
trans.sa_session.flush()
# Create a new info_association between the current library item and form_values
if item_type == 'folder':
# A LibraryFolder is a special case because if it inherited the template from its parent,
# we want to set inheritable to True for its info_association. This allows for the default
# inheritance to be False for each level in the Library hierarchy unless we're creating a new
# level in the hierarchy, in which case we'll inherit the "inheritable" setting from the parent
# level.
info_association = trans.app.model.LibraryFolderInfoAssociation(item, template, form_values, inheritable=inherited)
trans.sa_session.add(info_association)
trans.sa_session.flush()
elif item_type == 'ldda':
info_association = trans.app.model.LibraryDatasetDatasetInfoAssociation(item, template, form_values)
trans.sa_session.add(info_association)
trans.sa_session.flush()
message = 'The information has been updated.'
if cntrller == 'api':
return 200, message
new_kwd = dict(action=action,
cntrller=cntrller,
id=id,
message=util.sanitize_text(message),
status='done')
if in_library:
new_kwd.update(dict(controller='library_common',
use_panels=use_panels,
library_id=library_id,
folder_id=folder_id,
show_deleted=show_deleted))
if in_sample_tracking:
new_kwd.update(dict(controller='requests_common',
cntrller='requests_admin',
id=trans.security.encode_id(sample.id),
sample_id=sample_id))
return trans.response.send_redirect(web.url_for(**new_kwd))
@web.expose
def delete_template(self, trans, cntrller, item_type, form_type, **kwd):
params = util.Params(kwd)
# form_type must be one of: RUN_DETAILS_TEMPLATE, LIBRARY_INFO_TEMPLATE
in_library = form_type == trans.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE
in_sample_tracking = form_type == trans.model.FormDefinition.types.RUN_DETAILS_TEMPLATE
if in_library:
is_admin = (trans.user_is_admin() and cntrller == 'library_admin')
current_user_roles = trans.get_current_user_roles()
show_deleted = util.string_as_bool(params.get('show_deleted', False))
use_panels = util.string_as_bool(params.get('use_panels', False))
library_id = params.get('library_id', None)
folder_id = params.get('folder_id', None)
ldda_id = params.get('ldda_id', None)
elif in_sample_tracking:
request_type_id = params.get('request_type_id', None)
sample_id = params.get('sample_id', None)
message = util.restore_text(params.get('message', ''))
try:
if in_library:
item, item_desc, action, id = self.get_item_and_stuff(trans,
item_type=item_type,
library_id=library_id,
folder_id=folder_id,
ldda_id=ldda_id,
is_admin=is_admin)
elif in_sample_tracking:
item, item_desc, action, id = self.get_item_and_stuff(trans,
item_type=item_type,
request_type_id=request_type_id,
sample_id=sample_id)
except ValueError:
return None
if in_library:
if not (is_admin or trans.app.security_agent.can_modify_library_item(current_user_roles, item)):
message = "You are not authorized to modify %s '%s'." % (item_desc, item.name)
return trans.response.send_redirect(web.url_for(controller='library_common',
action='browse_library',
cntrller=cntrller,
id=library_id,
show_deleted=show_deleted,
message=util.sanitize_text(message),
status='error'))
if in_library:
info_association, inherited = item.get_info_association()
elif in_sample_tracking:
info_association = item.run_details
if not info_association:
message = "There is no template for this %s" % item_type
else:
if in_library:
info_association.deleted = True
trans.sa_session.add(info_association)
trans.sa_session.flush()
elif in_sample_tracking:
trans.sa_session.delete(info_association)
trans.sa_session.flush()
message = 'The template for this %s has been deleted.' % item_type
new_kwd = dict(action=action,
cntrller=cntrller,
id=id,
message=util.sanitize_text(message),
status='done')
if in_library:
new_kwd.update(dict(controller='library_common',
use_panels=use_panels,
library_id=library_id,
folder_id=folder_id,
show_deleted=show_deleted))
return trans.response.send_redirect(web.url_for(**new_kwd))
if in_sample_tracking:
new_kwd.update(dict(controller='request_type',
request_type_id=request_type_id,
sample_id=sample_id))
return trans.response.send_redirect(web.url_for(**new_kwd))
def widget_fields_have_contents(self, widgets):
# Return True if any of the fields in widgets contain contents, widgets is a list of dictionaries that looks something like:
# [{'widget': <galaxy.web.form_builder.TextField object at 0x10867aa10>, 'helptext': 'Field 0 help (Optional)', 'label': 'Field 0'}]
for field in widgets:
if (isinstance(field['widget'], TextArea) or isinstance(field['widget'], TextField)) and field['widget'].value:
return True
if isinstance(field['widget'], SelectField) and field['widget'].options:
for option_label, option_value, selected in field['widget'].options:
if selected:
return True
if isinstance(field['widget'], CheckboxField) and field['widget'].checked:
return True
if isinstance(field['widget'], WorkflowField) and str(field['widget'].value).lower() not in ['none']:
return True
if isinstance(field['widget'], WorkflowMappingField) and str(field['widget'].value).lower() not in ['none']:
return True
if isinstance(field['widget'], HistoryField) and str(field['widget'].value).lower() not in ['none']:
return True
if isinstance(field['widget'], AddressField) and str(field['widget'].value).lower() not in ['none']:
return True
return False
def clean_field_contents(self, widgets, **kwd):
field_contents = {}
for widget_dict in widgets:
widget = widget_dict['widget']
value = kwd.get(widget.name, '')
if isinstance(widget, CheckboxField):
# CheckboxField values are lists if the checkbox is checked
value = str(widget.is_checked(value)).lower()
elif isinstance(widget, AddressField):
# If the address was new, is has already been saved and widget.value is the new address.id
value = widget.value
field_contents[widget.name] = util.restore_text(value)
return field_contents
def field_param_values_ok(self, widget_name, widget_type, **kwd):
# Make sure required fields have contents, etc
params = util.Params(kwd)
@@ -1935,114 +1351,6 @@ class UsesFormDefinitionsMixin:
values[field_name] = field_value
return values
def populate_widgets_from_kwd(self, trans, widgets, **kwd):
# A form submitted via refresh_on_change requires us to populate the widgets with the contents of
# the form fields the user may have entered so that when the form refreshes the contents are retained.
params = util.Params(kwd)
populated_widgets = []
for widget_dict in widgets:
widget = widget_dict['widget']
if params.get(widget.name, False):
# The form included a field whose contents should be used to set the
# value of the current widget (widget.name is the name set by the
# user when they defined the FormDefinition).
if isinstance(widget, AddressField):
value = util.restore_text(params.get(widget.name, ''))
if value == 'none':
value = ''
widget.value = value
widget_dict['widget'] = widget
# Populate the AddressField params with the form field contents
widget_params_dict = {}
for field_name, label, help_text in widget.fields():
form_param_name = '%s_%s' % (widget.name, field_name)
widget_params_dict[form_param_name] = util.restore_text(params.get(form_param_name, ''))
widget.params = widget_params_dict
elif isinstance(widget, CheckboxField):
# Check the value from kwd since util.Params would have
# stringify'd the list if the checkbox is checked.
value = kwd.get(widget.name, '')
if CheckboxField.is_checked(value):
widget.value = 'true'
widget_dict['widget'] = widget
elif isinstance(widget, SelectField):
# Ensure the selected option remains selected.
value = util.restore_text(params.get(widget.name, ''))
processed_options = []
for option_label, option_value, option_selected in widget.options:
selected = value == option_value
processed_options.append((option_label, option_value, selected))
widget.options = processed_options
else:
widget.value = util.restore_text(params.get(widget.name, ''))
widget_dict['widget'] = widget
populated_widgets.append(widget_dict)
return populated_widgets
def get_item_and_stuff(self, trans, item_type, **kwd):
# Return an item, description, action and an id based on the item_type. Valid item_types are
# library, folder, ldda, request_type, sample.
if item_type == 'library':
library_id = kwd.get('library_id', None)
id = library_id
try:
item = trans.sa_session.query(trans.app.model.Library).get(trans.security.decode_id(library_id))
except Exception:
item = None
item_desc = 'data library'
action = 'library_info'
elif item_type == 'folder':
folder_id = kwd.get('folder_id', None)
id = folder_id
try:
item = trans.sa_session.query(trans.app.model.LibraryFolder).get(trans.security.decode_id(folder_id))
except Exception:
item = None
item_desc = 'folder'
action = 'folder_info'
elif item_type == 'ldda':
ldda_id = kwd.get('ldda_id', None)
id = ldda_id
try:
item = trans.sa_session.query(trans.app.model.LibraryDatasetDatasetAssociation).get(trans.security.decode_id(ldda_id))
except Exception:
item = None
item_desc = 'dataset'
action = 'ldda_edit_info'
elif item_type == 'request_type':
request_type_id = kwd.get('request_type_id', None)
id = request_type_id
try:
item = trans.sa_session.query(trans.app.model.RequestType).get(trans.security.decode_id(request_type_id))
except Exception:
item = None
item_desc = 'request type'
action = 'view_editable_request_type'
elif item_type == 'sample':
sample_id = kwd.get('sample_id', None)
id = sample_id
try:
item = trans.sa_session.query(trans.app.model.Sample).get(trans.security.decode_id(sample_id))
except Exception:
item = None
item_desc = 'sample'
action = 'view_sample'
else:
item = None
# message = "Invalid item type ( %s )" % str( item_type )
item_desc = None
action = None
id = None
return item, item_desc, action, id
def build_form_id_select_field(self, trans, forms, selected_value='none'):
return build_select_field(trans,
objs=forms,
label_attr='name',
select_field_name='form_id',
selected_value=selected_value,
refresh_on_change=True)
class SharableMixin:
""" Mixin for a controller that manages an item that can be shared. """
+1 -10
View File
@@ -5,7 +5,7 @@ from datetime import datetime, timedelta
from string import punctuation as PUNCTUATION
import six
from sqlalchemy import and_, false, func, or_
from sqlalchemy import and_, false, or_
import galaxy.queue_worker
from galaxy import (
@@ -1477,15 +1477,6 @@ class AdminGalaxy(controller.JSAppLauncher, AdminActions, UsesQuotaMixin, QuotaP
message = 'Usage has changed by %s to %s.' % (nice_size(new - current), nice_size(new))
return (message, 'done')
@web.expose
@web.require_admin
def name_autocomplete_data(self, trans, q=None, limit=None, timestamp=None):
"""Return autocomplete data for user emails"""
ac_data = ""
for user in trans.sa_session.query(trans.app.model.User).filter_by(deleted=False).filter(func.lower(trans.app.model.User.email).like(q.lower() + "%")):
ac_data = ac_data + user.email + "\n"
return ac_data
@web.expose_api
@web.require_admin
def manage_roles_and_groups_for_user(self, trans, payload=None, **kwd):
@@ -49,7 +49,6 @@ class RootController(controller.JSAppLauncher, UsesAnnotations):
def _get_extended_config(self, trans):
app = trans.app
user_requests = bool(trans.user and (trans.user.requests or app.security_agent.get_accessible_request_types(trans, trans.user)))
config = {
'active_view' : 'analysis',
'enable_cloud_launch' : app.config.get_bool('enable_cloud_launch', False),
@@ -58,8 +57,6 @@ class RootController(controller.JSAppLauncher, UsesAnnotations):
'toolbox_in_panel' : app.toolbox.to_dict(trans),
'message_box_visible' : app.config.message_box_visible,
'show_inactivity_warning' : app.config.user_activation_on and trans.user and not trans.user.active,
# TODO: move to user
'user_requests' : user_requests
}
# TODO: move to user
@@ -1,59 +0,0 @@
<%inherit file="/base.mako"/>
<%namespace file="json_common.mako" import="display_item" />
<%def name="title()">${param_dict['service_instance'].name}: ${action.label}</%def>
<%def name="display_json_grid_result( headers, rows )">
%for row in rows:
%for name in headers:
<div class="form-row">
<label>${name}</label>
${display_item( row.get( name ) )}
<div style="clear: both"></div>
</div>
%endfor
%endfor
</%def>
<%
#HACK!!!! need to use better method of displaying jqGrid here, needs to allow paging as optionally available.
if 'Rows' in result: #paged
records = result['Records']
total = result['Total']
rows = result['Rows']
page = result['Page']
else:
rows = result
records = None
total = None
page = None
headers = rows[0].keys()
%>
<div class="toolForm">
<div class="toolFormTitle">${action.label} of ${param_dict['service_instance'].name} (${param_dict['service'].name}) on ${param_dict['item'].name}</div>
<div class="toolFormBody">
%if records:
<div class="form-row">
<label>Records</label>
${records}
<div style="clear: both"></div>
</div>
%endif
%if total:
<div class="form-row">
<label>Total</label>
${total}
<div style="clear: both"></div>
</div>
%endif
%if page:
<div class="form-row">
<label>Page</label>
${page}
<div style="clear: both"></div>
</div>
%endif
${display_json_grid_result( headers, rows )}
</div>
</div>
@@ -1,11 +0,0 @@
<%inherit file="/base.mako"/>
<%namespace file="json_common.mako" import="display_item" />
<%def name="title()">${action.label} of ${param_dict['service_instance'].name} (${param_dict['service'].name}) on ${param_dict['item'].name}</%def>
<div class="toolForm">
<div class="toolFormTitle">${action.label} of ${param_dict['service_instance'].name} (${param_dict['service'].name}) on ${param_dict['item'].name}</i></div>
<div class="toolFormBody">
${display_item( result )}
</div>
</div>
@@ -1,28 +0,0 @@
<%def name="display_dict( result_dict )">
%for key, value in result_dict.items():
<div class="form-row">
<label>${key}</label>
${display_item( value )}
<div style="clear: both"></div>
</div>
%endfor
</%def>
<%def name="display_list( items )">
<ul>
%for item in items:
<li>${display_item( item ) }</li>
%endfor
</ul>
</%def>
<%def name="display_item( item )">
%if isinstance( item, ( list, tuple ) ):
${display_list( item )}
%elif isinstance( item, dict ):
${display_dict( item )}
%else:
${item}
%endif
</%def>
@@ -30,7 +30,6 @@
'ftp_upload_dir' : app.config.get("ftp_upload_dir", None),
'ftp_upload_site' : app.config.get("ftp_upload_site", None),
'datatypes_disable_auto' : app.config.get_bool("datatypes_disable_auto", False),
'user_requests' : bool( trans.user and ( trans.user.requests or app.security_agent.get_accessible_request_types( trans, trans.user ) ) ),
'user_json' : get_user_dict()
}
%>