mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge pull request #5103 from guerler/remove_sample_tracking
Remove sample tracking backend support, preserve data tables
This commit is contained in:
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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.
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
@@ -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. """
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
%>
|
||||
|
||||
Reference in New Issue
Block a user