17 new commits in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/79fd1058b25d/ Changeset: 79fd1058b25d User: dannon Date: 2013-10-25 00:40:09 Summary: Clean up imports in util package base Affected #: 1 file diff -r 97cb7306dc345ce5e81861e421c5277701161802 -r 79fd1058b25dc43a5292843a01106e38dfd83994 lib/galaxy/util/__init__.py --- a/lib/galaxy/util/__init__.py +++ b/lib/galaxy/util/__init__.py @@ -2,25 +2,30 @@ Utility functions used systemwide. """ -import logging, threading, random, string, re, binascii, pickle, time, datetime, math, re, os, sys, tempfile, stat, grp, smtplib, errno, shutil +import binascii, errno, grp, logging, os, pickle, random, re, shutil, smtplib, stat, string, sys, tempfile, threading from email.MIMEText import MIMEText from os.path import relpath from hashlib import md5 from galaxy import eggs -import pkg_resources -pkg_resources.require( 'docutils' ) +eggs.require( 'docutils' ) import docutils.core import docutils.writers.html4css1 -pkg_resources.require( 'elementtree' ) +eggs.require( 'elementtree' ) from elementtree import ElementTree, ElementInclude -pkg_resources.require( "wchartype" ) +eggs.require( "wchartype" ) import wchartype +from inflection import Inflector, English +inflector = Inflector(English) + +eggs.require( "simplejson" ) +import simplejson + log = logging.getLogger(__name__) _lock = threading.RLock() @@ -35,12 +40,6 @@ NULL_CHAR = '\000' BINARY_CHARS = [ NULL_CHAR ] -from inflection import Inflector, English -inflector = Inflector(English) - -pkg_resources.require( "simplejson" ) -import simplejson - def is_multi_byte( chars ): for char in chars: try: @@ -946,5 +945,5 @@ return os.path.abspath(galaxy_root_path) if __name__ == '__main__': - import doctest, sys + import doctest doctest.testmod(sys.modules[__name__], verbose=False) https://bitbucket.org/galaxy/galaxy-central/commits/a81f9cdc2035/ Changeset: a81f9cdc2035 User: dannon Date: 2013-10-25 00:42:18 Summary: Correct errors in util's xml_to_dict Affected #: 1 file diff -r 79fd1058b25dc43a5292843a01106e38dfd83994 -r a81f9cdc2035e6c7ea2febeb0a1af7e10d2ef2a3 lib/galaxy/util/__init__.py --- a/lib/galaxy/util/__init__.py +++ b/lib/galaxy/util/__init__.py @@ -176,9 +176,9 @@ sub_elem_dict[ key ].append( value ) for key, value in sub_elem_dict.iteritems(): if len( value ) == 1: - rval[ elem.tag ][ k ] = value[0] + rval[ elem.tag ][ key ] = value[0] else: - rval[ elem.tag ][ k ] = value + rval[ elem.tag ][ key ] = value if elem.attrib: for key, value in elem.attrib.iteritems(): rval[ elem.tag ][ "@%s" % key ] = value https://bitbucket.org/galaxy/galaxy-central/commits/8305d16cf154/ Changeset: 8305d16cf154 User: dannon Date: 2013-10-25 00:43:05 Summary: Strip unused variables from util package Affected #: 1 file diff -r a81f9cdc2035e6c7ea2febeb0a1af7e10d2ef2a3 -r 8305d16cf1542166a8d1c2bd04abd8a0562ca438 lib/galaxy/util/__init__.py --- a/lib/galaxy/util/__init__.py +++ b/lib/galaxy/util/__init__.py @@ -44,7 +44,7 @@ for char in chars: try: char = unicode( char ) - except UnicodeDecodeError, e: + except UnicodeDecodeError: # Probably binary return False if wchartype.is_asian( char ) or \ @@ -736,7 +736,7 @@ name = names.next() file = os.path.join(dir, prefix + name) try: - linked_path = os.link( src, file ) + os.link( src, file ) return (os.path.abspath(file)) except OSError, e: if e.errno == errno.EEXIST: https://bitbucket.org/galaxy/galaxy-central/commits/02bbb394aa98/ Changeset: 02bbb394aa98 User: dannon Date: 2013-10-25 00:59:49 Summary: Fix missing HTTPNotImplemented import in api/annotations; cleanup existing. Affected #: 1 file diff -r 8305d16cf1542166a8d1c2bd04abd8a0562ca438 -r 02bbb394aa9856c5a607a8e61e7dfea9a7241dfd lib/galaxy/webapps/galaxy/api/annotations.py --- a/lib/galaxy/webapps/galaxy/api/annotations.py +++ b/lib/galaxy/webapps/galaxy/api/annotations.py @@ -1,21 +1,15 @@ """ API operations on annotations. """ -import logging, os, string, shutil, urllib, re, socket -from cgi import escape, FieldStorage -from galaxy import util, datatypes, jobs, web, util -from galaxy.web.base.controller import BaseAPIController, UsesHistoryMixin, UsesHistoryDatasetAssociationMixin, UsesStoredWorkflowMixin +import logging +from galaxy import web from galaxy.model.item_attrs import UsesAnnotations from galaxy.util.sanitize_html import sanitize_html -import galaxy.datatypes -from galaxy.util.bunch import Bunch - -import pkg_resources -pkg_resources.require( "Routes" ) -import routes +from galaxy.web.base.controller import BaseAPIController, HTTPNotImplemented, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin, UsesStoredWorkflowMixin log = logging.getLogger( __name__ ) + class BaseAnnotationsController( BaseAPIController, UsesAnnotations, UsesHistoryMixin, UsesHistoryDatasetAssociationMixin, UsesStoredWorkflowMixin ): @web.expose_api @@ -25,7 +19,6 @@ if item is not None: return self.get_item_annotation_str( trans.sa_session, trans.get_user(), item ) - @web.expose_api def create( self, trans, payload, **kwd ): if "text" not in payload: @@ -53,23 +46,29 @@ def undelete( self, trans, **kwd ): raise HTTPNotImplemented() + class HistoryAnnotationsController(BaseAnnotationsController): controller_name = "history_annotations" tagged_item_id = "history_id" + def _get_item_from_id(self, trans, idstr): hist = self.get_history( trans, idstr ) return hist + class HistoryContentAnnotationsController(BaseAnnotationsController): controller_name = "history_content_annotations" tagged_item_id = "history_content_id" + def _get_item_from_id(self, trans, idstr): hda = self.get_dataset(trans, idstr) return hda + class WorkflowAnnotationsController(BaseAnnotationsController): controller_name = "workflow_annotations" tagged_item_id = "workflow_id" + def _get_item_from_id(self, trans, idstr): hda = self.get_stored_workflow(trans, idstr) return hda https://bitbucket.org/galaxy/galaxy-central/commits/a19245efea88/ Changeset: a19245efea88 User: dannon Date: 2013-10-25 01:08:04 Summary: Fix broken string_as_bool in history_contents API Affected #: 1 file diff -r 02bbb394aa9856c5a607a8e61e7dfea9a7241dfd -r a19245efea8847b994e90602c33813554a50ce03 lib/galaxy/webapps/galaxy/api/history_contents.py --- a/lib/galaxy/webapps/galaxy/api/history_contents.py +++ b/lib/galaxy/webapps/galaxy/api/history_contents.py @@ -2,15 +2,12 @@ API operations on the contents of a history. """ -from galaxy import web, util -from galaxy import exceptions -from galaxy.web.base.controller import BaseAPIController, url_for -from galaxy.web.base.controller import UsesHistoryDatasetAssociationMixin, UsesHistoryMixin -from galaxy.web.base.controller import UsesLibraryMixin, UsesLibraryMixinItems -from galaxy.datatypes import sniff +import logging +from galaxy import exceptions, util, web +from galaxy.web.base.controller import (BaseAPIController, url_for, + UsesHistoryDatasetAssociationMixin, UsesHistoryMixin, UsesLibraryMixin, + UsesLibraryMixinItems) -import os -import logging log = logging.getLogger( __name__ ) class HistoryContentsController( BaseAPIController, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin, @@ -354,7 +351,7 @@ # a request body is optional here purge = False if kwd.get( 'payload', None ): - purge = string_as_bool( kwd['payload'].get( 'purge', False ) ) + purge = util.string_as_bool( kwd['payload'].get( 'purge', False ) ) rval = { 'id' : id } try: @@ -389,7 +386,7 @@ log.exception( 'HDA API, delete: uncaught HTTPInternalServerError: %s, %s\n%s', id, str( kwd ), str( http_server_err ) ) raise - except exceptions.httpexceptions.HTTPException, http_exc: + except exceptions.httpexceptions.HTTPException: raise except Exception, exc: log.exception( 'HDA API, delete: uncaught exception: %s, %s\n%s', https://bitbucket.org/galaxy/galaxy-central/commits/28119d3941aa/ Changeset: 28119d3941aa User: dannon Date: 2013-10-25 01:10:39 Summary: Use standard spacing in history contents API, strip whitespace. Affected #: 1 file diff -r a19245efea8847b994e90602c33813554a50ce03 -r 28119d3941aae98156cdb4e07243ce7e3c4848a3 lib/galaxy/webapps/galaxy/api/history_contents.py --- a/lib/galaxy/webapps/galaxy/api/history_contents.py +++ b/lib/galaxy/webapps/galaxy/api/history_contents.py @@ -10,8 +10,10 @@ log = logging.getLogger( __name__ ) + class HistoryContentsController( BaseAPIController, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin, UsesLibraryMixin, UsesLibraryMixinItems ): + @web.expose_api_anonymous def index( self, trans, history_id, ids=None, **kwd ): """ @@ -44,11 +46,9 @@ and ( history_id == trans.security.encode_id( trans.history.id ) ) ): #TODO:?? is secure? history = trans.history - # otherwise, check permissions for the history first else: history = self.get_history( trans, history_id, check_ownership=True, check_accessible=True ) - # if ids, return _FULL_ data (as show) for each id passed if ids: ids = ids.split( ',' ) @@ -57,7 +57,6 @@ if encoded_hda_id in ids: #TODO: share code with show rval.append( self._detailed_hda_dict( trans, hda ) ) - # if no ids passed, return a _SUMMARY_ of _all_ datasets in the history else: details = kwd.get( 'details', None ) or [] @@ -71,13 +70,11 @@ rval.append( self._detailed_hda_dict( trans, hda ) ) else: rval.append( self._summary_hda_dict( trans, history_id, hda ) ) - except Exception, e: # for errors that are not specific to one hda (history lookup or summary list) rval = "Error in history API at listing contents: " + str( e ) log.error( rval + ": %s, %s" % ( type( e ), str( e ) ), exc_info=True ) trans.response.status = 500 - return rval #TODO: move to model or Mixin @@ -114,7 +111,6 @@ hda_dict[ 'display_types' ] = self.get_old_display_applications( trans, hda ) hda_dict[ 'display_apps' ] = self.get_display_apps( trans, hda ) return hda_dict - except Exception, exc: # catch error here - returning a briefer hda_dict with an error attribute log.exception( "Error in history API at listing contents with history %s, hda %s: (%s) %s", @@ -149,24 +145,20 @@ #TODO: dataset/hda by id (from history) OR check_ownership for anon user hda = self.get_history_dataset_association( trans, history, id, check_ownership=False, check_accessible=True ) - else: #TODO: do we really need the history? history = self.get_history( trans, history_id, check_ownership=True, check_accessible=True, deleted=False ) hda = self.get_history_dataset_association( trans, history, id, check_ownership=True, check_accessible=True ) - hda_dict = self.get_hda_dict( trans, hda ) hda_dict[ 'display_types' ] = self.get_old_display_applications( trans, hda ) hda_dict[ 'display_apps' ] = self.get_display_apps( trans, hda ) - except Exception, e: msg = "Error in history API at listing dataset: %s" % ( str(e) ) log.error( msg, exc_info=True ) trans.response.status = 500 return msg - return hda_dict @web.expose_api @@ -183,28 +175,25 @@ copy from library: 'source' = 'library' 'content' = [the encoded id from the library dataset] - + copy from HDA: 'source' = 'hda' 'content' = [the encoded id from the HDA] ..note: Currently, a user can only copy an HDA from a history that the user owns. - + :rtype: dict :returns: dictionary containing detailed information for the new HDA """ - #TODO: copy existing, accessible hda - dataset controller, copy_datasets #TODO: convert existing, accessible hda - model.DatasetInstance(or hda.datatype).get_converter_types - # check parameters source = payload.get('source', None) content = payload.get('content', None) if source not in ['library', 'hda'] or content is None: trans.response.status = 400 return "Please define the source ('library' or 'hda') and the content." - # retrieve history try: history = self.get_history( trans, history_id, check_ownership=True, check_accessible=False ) @@ -212,10 +201,8 @@ # no way to tell if it failed bc of perms or other (all MessageExceptions) trans.response.status = 500 return str( e ) - # copy from library dataset if source == 'library': - # get library data set try: ld = self.get_library_dataset( trans, content, check_ownership=False, check_accessible=False ) @@ -226,17 +213,14 @@ return str( e ) except Exception, e: return str( e ) - # insert into history hda = ld.library_dataset_dataset_association.to_history_dataset_association( history, add_to_history=True ) trans.sa_session.flush() return hda.to_dict() - elif source == 'hda': try: #NOTE: user currently only capable of copying one of their own datasets hda = self.get_dataset( trans, content ) - except ( exceptions.httpexceptions.HTTPRequestRangeNotSatisfiable, exceptions.httpexceptions.HTTPBadRequest ), id_exc: # wot... @@ -250,12 +234,10 @@ trans.response.status = 500 log.exception( "history: %s, source: %s, content: %s", history_id, source, content ) return str( exc ) - data_copy=hda.copy( copy_children=True ) result=history.add_dataset( data_copy ) trans.sa_session.flush() return result.to_dict() - else: # other options trans.response.status = 501 @@ -291,27 +273,22 @@ if history_id != trans.security.encode_id( trans.history.id ): trans.response.status = 401 return { 'error': 'Anonymous users cannot edit histories other than their current history' } - anon_allowed_payload = {} if 'deleted' in payload: anon_allowed_payload[ 'deleted' ] = payload[ 'deleted' ] if 'visible' in payload: anon_allowed_payload[ 'visible' ] = payload[ 'visible' ] - payload = self._validate_and_parse_update_payload( anon_allowed_payload ) hda = self.get_dataset( trans, id, check_ownership=False, check_accessible=False, check_state=True ) if hda.history != trans.history: trans.response.status = 401 return { 'error': 'Anonymous users cannot edit datasets outside their current history' } - else: payload = self._validate_and_parse_update_payload( payload ) hda = self.get_dataset( trans, id, check_ownership=True, check_accessible=True, check_state=True ) - # get_dataset can return a string during an error if hda and isinstance( hda, trans.model.HistoryDatasetAssociation ): changed = self.set_hda_from_dict( trans, hda, payload ) - except Exception, exception: log.error( 'Update of history (%s), HDA (%s) failed: %s', history_id, id, str( exception ), exc_info=True ) @@ -323,7 +300,6 @@ else: trans.response.status = 500 return { 'error': str( exception ) } - return changed @web.expose_api @@ -352,22 +328,18 @@ purge = False if kwd.get( 'payload', None ): purge = util.string_as_bool( kwd['payload'].get( 'purge', False ) ) - rval = { 'id' : id } try: hda = self.get_dataset( trans, id, check_ownership=True, check_accessible=True, check_state=True ) hda.deleted = True - if purge: if not trans.app.config.allow_user_dataset_purge: raise exceptions.httpexceptions.HTTPForbidden( detail='This instance does not allow user dataset purging' ) - hda.purged = True trans.sa_session.add( hda ) trans.sa_session.flush() - if hda.dataset.user_can_purge: try: hda.dataset.full_delete() @@ -376,12 +348,9 @@ pass # flush now to preserve deleted state in case of later interruption trans.sa_session.flush() - rval[ 'purged' ] = True - trans.sa_session.flush() rval[ 'deleted' ] = True - except exceptions.httpexceptions.HTTPInternalServerError, http_server_err: log.exception( 'HDA API, delete: uncaught HTTPInternalServerError: %s, %s\n%s', id, str( kwd ), str( http_server_err ) ) @@ -393,7 +362,6 @@ id, str( kwd ), str( exc ) ) trans.response.status = 500 rval.update({ 'error': str( exc ) }) - return rval def _validate_and_parse_update_payload( self, payload ): @@ -416,7 +384,6 @@ 'metadata_dbkey', 'metadata_column_names', 'metadata_column_types', 'metadata_columns', 'metadata_comment_lines', 'metadata_data_lines' ) - validated_payload = {} for key, val in payload.items(): # TODO: lots of boilerplate here, but overhead on abstraction is equally onerous https://bitbucket.org/galaxy/galaxy-central/commits/9fcd7759a210/ Changeset: 9fcd7759a210 User: dannon Date: 2013-10-25 01:11:56 Summary: Irods objectstore: fix invalid rcCollCreate Affected #: 1 file diff -r 28119d3941aae98156cdb4e07243ce7e3c4848a3 -r 9fcd7759a210684ad2762340da7dbeab753c24c0 lib/galaxy/objectstore/rods.py --- a/lib/galaxy/objectstore/rods.py +++ b/lib/galaxy/objectstore/rods.py @@ -6,16 +6,14 @@ import os import time -import errno import logging -#import traceback from posixpath import join as path_join from posixpath import basename as path_basename from posixpath import dirname as path_dirname from galaxy.objectstore import DiskObjectStore, ObjectStore, local_extra_dirs -from galaxy.exceptions import ObjectNotFound, ObjectInvalid +from galaxy.exceptions import ObjectNotFound import galaxy.eggs galaxy.eggs.require( 'PyRods' ) @@ -121,7 +119,7 @@ log.debug( 'Creating collection %s' % collname ) ci = irods.collInp_t() ci.collName = collname - status = rcCollCreate( self.rods_conn, ci ) + status = irods.rcCollCreate( self.rods_conn, ci ) assert status == 0, '__mkcolls(): Failed to create collection: %s' % collname @local_extra_dirs https://bitbucket.org/galaxy/galaxy-central/commits/b9e78741f439/ Changeset: b9e78741f439 User: dannon Date: 2013-10-25 01:25:28 Summary: Import cleanup in toolshed repositories API Affected #: 1 file diff -r 9fcd7759a210684ad2762340da7dbeab753c24c0 -r b9e78741f439f06c30b07d7d591c7c83183be6a6 lib/galaxy/webapps/tool_shed/api/repositories.py --- a/lib/galaxy/webapps/tool_shed/api/repositories.py +++ b/lib/galaxy/webapps/tool_shed/api/repositories.py @@ -1,11 +1,12 @@ -import logging +import logging, os from time import strftime -from galaxy.web.framework.helpers import time_ago + from galaxy import eggs +from galaxy import util from galaxy import web -from galaxy import util from galaxy.util import json from galaxy.web.base.controller import BaseAPIController +from galaxy.web.framework.helpers import time_ago import tool_shed.repository_types.util as rt_util import tool_shed.util.shed_util_common as suc from tool_shed.galaxy_install import repository_util @@ -14,9 +15,7 @@ eggs.require( 'mercurial' ) -from mercurial import commands from mercurial import hg -from mercurial import ui log = logging.getLogger( __name__ ) https://bitbucket.org/galaxy/galaxy-central/commits/b154d93fb369/ Changeset: b154d93fb369 User: dannon Date: 2013-10-25 01:26:06 Summary: Fix broken debug statements in toolshed repositories API -- would have caused Exception in reset_metadata Affected #: 1 file diff -r b9e78741f439f06c30b07d7d591c7c83183be6a6 -r b154d93fb369443a5979aa49e59c42fe27e7df53 lib/galaxy/webapps/tool_shed/api/repositories.py --- a/lib/galaxy/webapps/tool_shed/api/repositories.py +++ b/lib/galaxy/webapps/tool_shed/api/repositories.py @@ -278,7 +278,7 @@ encoded_id = trans.security.encode_id( repository.id ) if encoded_id in encoded_ids_to_skip: log.debug( "Skipping repository with id %s because it is in encoded_ids_to_skip %s" % \ - ( str( repository_id ), str( encoded_ids_to_skip ) ) ) + ( str( repository.id ), str( encoded_ids_to_skip ) ) ) elif repository.type == rt_util.TOOL_DEPENDENCY_DEFINITION and repository.id not in handled_repository_ids: results = handle_repository( trans, repository, results ) # Now reset metadata on all remaining repositories. @@ -286,7 +286,7 @@ encoded_id = trans.security.encode_id( repository.id ) if encoded_id in encoded_ids_to_skip: log.debug( "Skipping repository with id %s because it is in encoded_ids_to_skip %s" % \ - ( str( repository_id ), str( encoded_ids_to_skip ) ) ) + ( str( repository.id ), str( encoded_ids_to_skip ) ) ) elif repository.type != rt_util.TOOL_DEPENDENCY_DEFINITION and repository.id not in handled_repository_ids: results = handle_repository( trans, repository, results ) stop_time = strftime( "%Y-%m-%d %H:%M:%S" ) https://bitbucket.org/galaxy/galaxy-central/commits/9de91cccfcf0/ Changeset: 9de91cccfcf0 User: dannon Date: 2013-10-25 01:27:15 Summary: Remove unused encoded_repository_id from get_ordered_installable_revisions in toolshed repository API Affected #: 1 file diff -r b154d93fb369443a5979aa49e59c42fe27e7df53 -r 9de91cccfcf0b7fc5f1beac6209e0111fd5c4af2 lib/galaxy/webapps/tool_shed/api/repositories.py --- a/lib/galaxy/webapps/tool_shed/api/repositories.py +++ b/lib/galaxy/webapps/tool_shed/api/repositories.py @@ -38,7 +38,6 @@ try: # Get the repository information. repository = suc.get_repository_by_name_and_owner( trans.app, name, owner ) - encoded_repository_id = trans.security.encode_id( repository.id ) repo_dir = repository.repo_path( trans.app ) repo = hg.repository( suc.get_configured_ui(), repo_dir ) ordered_installable_revisions = suc.get_ordered_metadata_changeset_revisions( repository, repo, downloadable=True ) https://bitbucket.org/galaxy/galaxy-central/commits/d0a805216378/ Changeset: d0a805216378 User: dannon Date: 2013-10-25 01:28:02 Summary: Add missing import HTTPBadRequest to toolshed repository revisions API Affected #: 1 file diff -r 9de91cccfcf0b7fc5f1beac6209e0111fd5c4af2 -r d0a80521637842c250665010848e7eb749946cce lib/galaxy/webapps/tool_shed/api/repository_revisions.py --- a/lib/galaxy/webapps/tool_shed/api/repository_revisions.py +++ b/lib/galaxy/webapps/tool_shed/api/repository_revisions.py @@ -5,7 +5,7 @@ from galaxy import web from galaxy import util from galaxy.model.orm import and_, not_, select -from galaxy.web.base.controller import BaseAPIController +from galaxy.web.base.controller import BaseAPIController, HTTPBadRequest from tool_shed.util import export_util import tool_shed.util.shed_util_common as suc https://bitbucket.org/galaxy/galaxy-central/commits/880f16259947/ Changeset: 880f16259947 User: dannon Date: 2013-10-25 01:29:09 Summary: Remove unused code in toolshed repository revisions API; strip whitespace. Affected #: 1 file diff -r d0a80521637842c250665010848e7eb749946cce -r 880f162599472ce9316ccef0e0c56b4d16c11424 lib/galaxy/webapps/tool_shed/api/repository_revisions.py --- a/lib/galaxy/webapps/tool_shed/api/repository_revisions.py +++ b/lib/galaxy/webapps/tool_shed/api/repository_revisions.py @@ -43,7 +43,6 @@ if not changeset_revision: raise HTTPBadRequest( detail="Missing required parameter 'changeset_revision'." ) export_repository_dependencies = payload.get( 'export_repository_dependencies', False ) - download_dir = payload.get( 'download_dir', '/tmp' ) try: # We'll currently support only gzip-compressed tar archives. file_type = 'gz' @@ -167,7 +166,6 @@ flush_needed = False for key, new_value in payload.items(): if hasattr( repository_metadata, key ): - old_value = getattr( repository_metadata, key ) setattr( repository_metadata, key, new_value ) if key in [ 'tools_functionally_correct', 'time_last_tested' ]: # Automatically update repository_metadata.time_last_tested. @@ -186,10 +184,10 @@ action='show', id=trans.security.encode_id( repository_metadata.id ) ) return repository_metadata_dict - + def __get_value_mapper( self, trans, repository_metadata ): value_mapper = { 'id' : trans.security.encode_id, 'repository_id' : trans.security.encode_id } if repository_metadata.time_last_tested is not None: - value_mapper[ 'time_last_tested' ] = time_ago + value_mapper[ 'time_last_tested' ] = time_ago return value_mapper https://bitbucket.org/galaxy/galaxy-central/commits/3e5cbc50db38/ Changeset: 3e5cbc50db38 User: dannon Date: 2013-10-25 01:31:10 Summary: Fix bug in galaxy's tool_shed_repositories API -- would cause exception if there was ever a shed_tool_conf. Affected #: 1 file diff -r 880f162599472ce9316ccef0e0c56b4d16c11424 -r 3e5cbc50db38f44d54ee0682d4320658ba6d4763 lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py --- a/lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py +++ b/lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py @@ -267,8 +267,7 @@ if shed_tool_conf: # Get the tool_path setting. index, shed_conf_dict = suc.get_shed_tool_conf_dict( trans.app, shed_tool_conf ) - # BUG, FIXME: Shed config dict does not exist in this context - tool_path = shed_config_dict[ 'tool_path' ] + tool_path = shed_conf_dict[ 'tool_path' ] else: # Pick a semi-random shed-related tool panel configuration file and get the tool_path setting. for shed_config_dict in trans.app.toolbox.shed_tool_confs: https://bitbucket.org/galaxy/galaxy-central/commits/31d16cab8447/ Changeset: 31d16cab8447 User: dannon Date: 2013-10-25 01:32:30 Summary: Fix yet another missing import in tool_shed_repositories API Affected #: 1 file diff -r 3e5cbc50db38f44d54ee0682d4320658ba6d4763 -r 31d16cab8447c111e1de64575208db8d16063e17 lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py --- a/lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py +++ b/lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py @@ -13,6 +13,8 @@ from tool_shed.util import encoding_util from tool_shed.util import metadata_util from tool_shed.util import workflow_util +from tool_shed.util import tool_util + import tool_shed.util.shed_util_common as suc log = logging.getLogger( __name__ ) https://bitbucket.org/galaxy/galaxy-central/commits/404f0c60e9bd/ Changeset: 404f0c60e9bd User: dannon Date: 2013-10-25 01:34:38 Summary: Complete cleanup of tool_shed_repositories API -- remove unused code and strip whitespace Affected #: 1 file diff -r 31d16cab8447c111e1de64575208db8d16063e17 -r 404f0c60e9bd45654bef1ea68bc64a26c0811bca lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py --- a/lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py +++ b/lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py @@ -34,6 +34,7 @@ message += 'Installing_Galaxy_tool_shed_repository_tools_into_a_local_Galaxy_instance.' return message + class ToolShedRepositoriesController( BaseAPIController ): """RESTful controller for interactions with tool shed repositories.""" @@ -43,7 +44,7 @@ GET /api/tool_shed_repositories/{encoded_tool_shed_repository_id}/exported_workflows Display a list of dictionaries containing information about this tool shed repository's exported workflows. - + :param id: the encoded id of the ToolShedRepository object """ # Example URL: http://localhost:8763/api/tool_shed_repositories/f2db41e1fa331b3e/exported_w... @@ -74,10 +75,10 @@ POST /api/tool_shed_repositories/import_workflow Import the specified exported workflow contained in the specified installed tool shed repository into Galaxy. - + :param key: the API key of the Galaxy user with which the imported workflow will be associated. :param id: the encoded id of the ToolShedRepository object - + The following parameters are included in the payload. :param index: the index location of the workflow tuple in the list of exported workflows stored in the metadata for the specified repository """ @@ -109,7 +110,7 @@ POST /api/tool_shed_repositories/import_workflow Import all of the exported workflows contained in the specified installed tool shed repository into Galaxy. - + :param key: the API key of the Galaxy user with which the imported workflows will be associated. :param id: the encoded id of the ToolShedRepository object """ @@ -229,7 +230,6 @@ return dict( status='error', error=message ) if raw_text: items = json.from_json_string( raw_text ) - repository_dict = items[ 0 ] repository_revision_dict = items[ 1 ] repo_info_dict = items[ 2 ] else: @@ -415,7 +415,6 @@ install_tool_dependencies = payload.get( 'install_tool_dependencies', False ) new_tool_panel_section_label = payload.get( 'new_tool_panel_section_label', '' ) shed_tool_conf = payload.get( 'shed_tool_conf', None ) - tool_path = payload.get( 'tool_path', None ) tool_panel_section_id = payload.get( 'tool_panel_section_id', '' ) all_installed_tool_shed_repositories = [] for index, tool_shed_url in enumerate( tool_shed_urls ): @@ -451,7 +450,6 @@ :param owner (required): the owner of the Repository :param changset_revision (required): the changset_revision of the RepositoryMetadata object associated with the Repository """ - api_key = kwd.get( 'key', None ) # Get the information about the repository to be installed from the payload. tool_shed_url = payload.get( 'tool_shed_url', '' ) if not tool_shed_url: @@ -471,7 +469,6 @@ ordered_tsr_ids = repair_dict.get( 'ordered_tsr_ids', [] ) ordered_repo_info_dicts = repair_dict.get( 'ordered_repo_info_dicts', [] ) if ordered_tsr_ids and ordered_repo_info_dicts: - repositories_for_repair = [] for index, tsr_id in enumerate( ordered_tsr_ids ): repository = trans.sa_session.query( trans.model.ToolShedRepository ).get( trans.security.decode_id( tsr_id ) ) repo_info_dict = ordered_repo_info_dicts[ index ] @@ -496,7 +493,7 @@ PUT /api/tool_shed_repositories/reset_metadata_on_installed_repositories Resets all metadata on all repositories installed into Galaxy in an "orderly fashion". - + :param key: the API key of the Galaxy admin user. """ try: https://bitbucket.org/galaxy/galaxy-central/commits/23c9b4736570/ Changeset: 23c9b4736570 User: dannon Date: 2013-10-25 06:39:47 Summary: Simplify undelete_quota, there are no params. Affected #: 2 files diff -r 404f0c60e9bd45654bef1ea68bc64a26c0811bca -r 23c9b4736570957fd2d54bde163d99943efb2e1c lib/galaxy/actions/admin.py --- a/lib/galaxy/actions/admin.py +++ b/lib/galaxy/actions/admin.py @@ -150,7 +150,7 @@ message += ', '.join( names ) return message - def _undelete_quota( self, quota, params ): + def _undelete_quota( self, quota ): quotas = util.listify( quota ) names = [] for q in quotas: diff -r 404f0c60e9bd45654bef1ea68bc64a26c0811bca -r 23c9b4736570957fd2d54bde163d99943efb2e1c lib/galaxy/webapps/galaxy/api/quotas.py --- a/lib/galaxy/webapps/galaxy/api/quotas.py +++ b/lib/galaxy/webapps/galaxy/api/quotas.py @@ -5,7 +5,6 @@ from galaxy.web.base.controller import BaseAPIController, UsesQuotaMixin, url_for from galaxy.web.base.controllers.admin import Admin from galaxy import web, util -from elementtree.ElementTree import XML from galaxy.web.params import QuotaParamParser from galaxy.actions.admin import AdminActions @@ -140,8 +139,7 @@ Undeletes a quota """ quota = self.get_quota( trans, id, deleted=True ) - params = self.get_quota_params( payload ) try: - return self._undelete_quota( quota, params ) + return self._undelete_quota( quota ) except ActionInputError, e: raise HTTPBadRequest( detail=str( e ) ) https://bitbucket.org/galaxy/galaxy-central/commits/5a259fa8aabb/ Changeset: 5a259fa8aabb User: dannon Date: 2013-10-25 06:40:01 Summary: Merge with central Affected #: 6 files diff -r 23c9b4736570957fd2d54bde163d99943efb2e1c -r 5a259fa8aabb4d977c5ed091d1658b9674d0e67c lib/galaxy/webapps/galaxy/api/tools.py --- a/lib/galaxy/webapps/galaxy/api/tools.py +++ b/lib/galaxy/webapps/galaxy/api/tools.py @@ -119,14 +119,18 @@ return { "message": { "type": "error", "data" : vars[ 'errors' ] } } # TODO: check for errors and ensure that output dataset(s) are available. - output_datasets = vars.get( 'out_data', {} ).values() + output_datasets = vars.get( 'out_data', {} ).iteritems() rval = { "outputs": [] } outputs = rval[ "outputs" ] #TODO:?? poss. only return ids? - for output in output_datasets: + for output_name, output in output_datasets: output_dict = output.to_dict() + #add the output name back into the output data structure + #so it's possible to figure out which newly created elements + #correspond with which tool file outputs + output_dict['output_name'] = output_name outputs.append( trans.security.encode_dict_ids( output_dict ) ) return rval diff -r 23c9b4736570957fd2d54bde163d99943efb2e1c -r 5a259fa8aabb4d977c5ed091d1658b9674d0e67c lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py --- a/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py +++ b/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py @@ -341,10 +341,13 @@ elif action_type == 'setup_r_environment': # setup an R environment # <action type="setup_r_environment"> - # <r_base name="package_r_3_0_1" owner="bgruening" /> + # <repository name="package_r_3_0_1" owner="bgruening"> + # <package name="R" version="3.0.1" /> + # </repository> + # <!-- allow installing an R packages --> + # <package>https://github.com/bgruening/download_store/raw/master/DESeq2-1_0_18/BiocGenerics_0.6.0.tar.gz</package> # </action> - # allow downloading and installing an R package - # <package>https://github.com/bgruening/download_store/raw/master/DESeq2-1_0_18/BiocGenerics_0.6.0.tar.gz</package> + if action_dict.get( 'env_shell_file_paths', False ): install_environment.add_env_shell_file_paths( action_dict[ 'env_shell_file_paths' ] ) else: @@ -371,10 +374,73 @@ # R libraries are installed to $INSTALL_DIR (install_dir), we now set the R_LIBS path to that directory # TODO: That code is used a lot for the different environments and should be refactored, once the environments are integrated modify_env_command_dict = dict( name="R_LIBS", action="prepend_to", value=install_dir ) - modify_env_command = td_common_util.create_or_update_env_shell_file( install_dir, modify_env_command_dict ) - return_code = handle_command( app, tool_dependency, install_dir, modify_env_command ) + env_entry, env_file = td_common_util.create_or_update_env_shell_file( install_dir, modify_env_command_dict ) + return_code = file_append( env_entry, env_file, skip_if_contained=True, make_executable=True ) + if return_code: return + elif action_type == 'setup_ruby_environment': + # setup an Ruby environment + # <action type="setup_ruby_environment"> + # <repository name="package_ruby_2_0" owner="bgruening"> + # <package name="ruby" version="2.0" /> + # </repository> + # <!-- allow downloading and installing an Ruby package from http://rubygems.org/ --> + # <package>protk</package> + # <package>protk=1.2.4</package> + # <package>http://url-to-some-gem-file.de/protk.gem</package> + # </action> + if action_dict.get( 'env_shell_file_paths', False ): + install_environment.add_env_shell_file_paths( action_dict[ 'env_shell_file_paths' ] ) + else: + log.warning( 'Missing Ruby environment. Please check if your specified Ruby installation exists.' ) + return + + dir = os.path.curdir + current_dir = os.path.abspath( os.path.join( work_dir, dir ) ) + with lcd( current_dir ): + with settings( warn_only=True ): + for (gem, gem_version) in action_dict[ 'ruby_packages' ]: + if os.path.isfile( gem ): + # we assume a local shipped gem file + cmd = '''export PATH=$PATH:$RUBY_HOME/bin && export GEM_HOME=$INSTALL_DIR && + gem install --local %s''' % ( gem ) + elif gem.find('://') != -1: + # we assume a URL to a gem file + url = gem + gem_name = url.split( '/' )[ -1 ] + td_common_util.url_download( work_dir, gem_name, url, extract=False ) + cmd = '''export PATH=$PATH:$RUBY_HOME/bin && export GEM_HOME=$INSTALL_DIR && + gem install --local %s ''' % ( gem_name ) + else: + # gem file from rubygems.org with or without version number + if gem_version: + # version number was specified + cmd = '''export PATH=$PATH:$RUBY_HOME/bin && export GEM_HOME=$INSTALL_DIR && + gem install %s --version "=%s"''' % ( gem, gem_version) + else: + # no version number given + cmd = '''export PATH=$PATH:$RUBY_HOME/bin && export GEM_HOME=$INSTALL_DIR && + gem install %s''' % ( gem ) + cmd = install_environment.build_command( td_common_util.evaluate_template( cmd, install_dir ) ) + return_code = handle_command( app, tool_dependency, install_dir, cmd ) + if return_code: + return + + # Ruby libraries are installed to $INSTALL_DIR (install_dir), we now set the GEM_PATH path to that directory + # TODO: That code is used a lot for the different environments and should be refactored, once the environments are integrated + modify_env_command_dict = dict( name="GEM_PATH", action="prepend_to", value=install_dir ) + env_entry, env_file = td_common_util.create_or_update_env_shell_file( install_dir, modify_env_command_dict ) + return_code = file_append( env_entry, env_file, skip_if_contained=True, make_executable=True ) + if return_code: + return + + modify_env_command_dict = dict( name="PATH", action="prepend_to", value=os.path.join(install_dir, 'bin') ) + env_entry, env_file = td_common_util.create_or_update_env_shell_file( install_dir, modify_env_command_dict ) + return_code = file_append( env_entry, env_file, skip_if_contained=True, make_executable=True ) + if return_code: + return + else: # We're handling a complex repository dependency where we only have a set_environment tag set. # <action type="set_environment"> diff -r 23c9b4736570957fd2d54bde163d99943efb2e1c -r 5a259fa8aabb4d977c5ed091d1658b9674d0e67c lib/tool_shed/galaxy_install/tool_dependencies/install_util.py --- a/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py +++ b/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py @@ -613,10 +613,13 @@ action_dict[ 'configure_opts' ] = configure_opts elif action_type == 'setup_r_environment': # setup an R environment - # <action type="setup_r_environment" name="package_r_3_0_1" owner="bgruening"> - # <package>https://github.com/bgruening/download_store/raw/master/DESeq2-1_0_18/BiocGenerics_0.6.0.tar.gz</package> + # <action type="setup_r_environment"> + # <repository name="package_r_3_0_1" owner="bgruening"> + # <package name="R" version="3.0.1" /> + # </repository> + # <!-- allow installing an R packages --> + # <package>https://github.com/bgruening/download_store/raw/master/DESeq2-1_0_18/BiocGenerics_0.6.0.tar.gz</package> # </action> - env_shell_file_paths = td_common_util.get_env_shell_file_paths( app, action_elem.find('repository') ) all_env_shell_file_paths.extend( env_shell_file_paths ) @@ -631,6 +634,46 @@ action_dict[ 'r_packages' ] = r_packages else: continue + elif action_type == 'setup_ruby_environment': + # setup an Ruby environment + # <action type="setup_ruby_environment"> + # <repository name="package_ruby_2_0" owner="bgruening"> + # <package name="ruby" version="2.0" /> + # </repository> + # <!-- allow downloading and installing an Ruby package from http://rubygems.org/ --> + # <package>protk</package> + # <package>protk=1.2.4</package> + # <package>http://url-to-some-gem-file.de/protk.gem</package> + # </action> + + env_shell_file_paths = td_common_util.get_env_shell_file_paths( app, action_elem.find('repository') ) + all_env_shell_file_paths.extend( env_shell_file_paths ) + if all_env_shell_file_paths: + action_dict[ 'env_shell_file_paths' ] = all_env_shell_file_paths + ruby_packages = list() + for env_elem in action_elem: + if env_elem.tag == 'package': + """ + A valid gem definition can be: + protk=1.2.4 + protk + ftp://ftp.gruening.de/protk.gem + """ + gem_token = env_elem.text.strip().split('=') + if len(gem_token) == 2: + # version string + gem_name = gem_token[0] + gem_version = gem_token[1] + ruby_packages.append( [gem_name, gem_version] ) + else: + # gem name for rubygems.org without version number + gem = env_elem.text.strip() + ruby_packages.append( [gem, None] ) + + if ruby_packages: + action_dict[ 'ruby_packages' ] = ruby_packages + else: + continue elif action_type == 'make_install': # make; make install; allow providing make options if action_elem.text: diff -r 23c9b4736570957fd2d54bde163d99943efb2e1c -r 5a259fa8aabb4d977c5ed091d1658b9674d0e67c test-data/extract_genomic_dna_out2.fasta --- a/test-data/extract_genomic_dna_out2.fasta +++ b/test-data/extract_genomic_dna_out2.fasta @@ -1,6 +1,6 @@ ->droPer1_super_1_139823_139913_- +>droPer1_super_1_139823_139913_- AK028861 CGTCGGCTTCTGCTTCTGCTGATGATGGTCGTTCTTCTTCCTTTACTTCT TCCTATTTTTCTTCCTTCCCTTACACTATATCTTCCTTTA ->droPer1_super_1_156750_156844_- +>droPer1_super_1_156750_156844_- BC126698 CCGGGCTGCGGCAAGGGATTCACCTGCTCCAAACAGCTCAAGGTGCACTC CCGCACGCACACGGGCGAGAAGCCCTATCACTGCGACATCTGCT diff -r 23c9b4736570957fd2d54bde163d99943efb2e1c -r 5a259fa8aabb4d977c5ed091d1658b9674d0e67c tools/extract/extract_genomic_dna.py --- a/tools/extract/extract_genomic_dna.py +++ b/tools/extract/extract_genomic_dna.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ usage: %prog $input $out_file1 - -1, --cols=N,N,N,N: Columns for start, end, strand in input file + -1, --cols=N,N,N,N,N: Columns for start, end, strand in input file -d, --dbkey=N: Genome build of input file -o, --output_format=N: the data type of the output file -g, --GALAXY_DATA_INDEX_DIR=N: the directory containing alignseq.loc @@ -54,7 +54,13 @@ # options, args = doc_optparse.parse( __doc__ ) try: - chrom_col, start_col, end_col, strand_col = parse_cols_arg( options.cols ) + if len(options.cols.split(',')) == 5: + # BED file + chrom_col, start_col, end_col, strand_col, name_col = parse_cols_arg( options.cols ) + else: + # gff file + chrom_col, start_col, end_col, strand_col = parse_cols_arg( options.cols ) + name_col = False dbkey = options.dbkey output_format = options.output_format gff_format = options.gff @@ -136,7 +142,8 @@ if isinstance( feature, ( Header, Comment ) ): line_count += 1 continue - + + name = "" if gff_format and interpret_features: # Processing features. gff_util.convert_gff_coords_to_bed( feature ) @@ -153,6 +160,8 @@ chrom = fields[chrom_col] start = int( fields[start_col] ) end = int( fields[end_col] ) + if name_col: + name = fields[name_col] if gff_format: start, end = gff_util.convert_gff_coords_to_bed( [start, end] ) if includes_strand_col: @@ -237,13 +246,16 @@ sequence = reverse_complement( sequence ) if output_format == "fasta" : - l = len( sequence ) + l = len( sequence ) c = 0 if gff_format: start, end = gff_util.convert_bed_coords_to_gff( [ start, end ] ) fields = [dbkey, str( chrom ), str( start ), str( end ), strand] meta_data = "_".join( fields ) - fout.write( ">%s\n" % meta_data ) + if name.strip(): + fout.write( ">%s %s\n" % (meta_data, name) ) + else: + fout.write( ">%s\n" % meta_data ) while c < l: b = min( c + 50, l ) fout.write( "%s\n" % str( sequence[c:b] ) ) diff -r 23c9b4736570957fd2d54bde163d99943efb2e1c -r 5a259fa8aabb4d977c5ed091d1658b9674d0e67c tools/extract/extract_genomic_dna.xml --- a/tools/extract/extract_genomic_dna.xml +++ b/tools/extract/extract_genomic_dna.xml @@ -1,4 +1,4 @@ -<tool id="Extract genomic DNA 1" name="Extract Genomic DNA" version="2.2.2"> +<tool id="Extract genomic DNA 1" name="Extract Genomic DNA" version="2.2.3"><description>using coordinates from assembled/unassembled genomes</description><command interpreter="python"> extract_genomic_dna.py $input $out_file1 -o $out_format -d $dbkey @@ -11,9 +11,9 @@ #if isinstance( $input.datatype, $__app__.datatypes_registry.get_datatype_by_extension('gff').__class__): -1 1,4,5,7 --gff #else: - -1 ${input.metadata.chromCol},${input.metadata.startCol},${input.metadata.endCol},${input.metadata.strandCol} + -1 ${input.metadata.chromCol},${input.metadata.startCol},${input.metadata.endCol},${input.metadata.strandCol},${input.metadata.nameCol} #end if - + #if $seq_source.index_source == "cached": ## Genomic data from cache. -g ${GALAXY_DATA_INDEX_DIR} @@ -52,16 +52,30 @@ </data></outputs><requirements> + <requirement type="package">ucsc_tools</requirement><requirement type="binary">faToTwoBit</requirement> - <requirement type="package">ucsc_tools</requirement></requirements><tests><test><param name="input" value="1.bed" dbkey="hg17" ftype="bed" /><param name="interpret_features" value="yes"/><param name="index_source" value="cached"/> - <param name="out_format" value="fasta"/> - <output name="out_file1" file="extract_genomic_dna_out1.fasta" /> + <param name="out_format" value="fasta"/> + <output name="out_file1"> + <assert_contents> + <!-- First few lines... --> + <has_text text=">hg17_chr1_147962192_147962580_- CCDS989.1_cds_0_0_chr1_147962193_r" /> + <has_text text="ACTTGATCCTGCTCCCTCGGTGTCTGCATTGACTCCTCATGCTGGGACTG" /> + <has_text text="GACCCGTCAACCCCCCTGCTCGCTGCTCACGTACCTTCATCACTTTTAGT" /> + <has_text text="GATGATGCAACTTTCGAGGAATGGTTCCCCCAAGGGCGGCCCCCAAAAGT" /> + <!-- Last few lines... --> + <has_text text="GCTGTGGCACAGAACATGGACTCTGTGTTTAAGGAGCTCTTGGGAAAGAC" /> + <has_text text="CTCTGTCCGCCAGGGCCTTGGGCCAGCATCTACCACCTCTCCCAGTCCTG" /> + <has_text text="GGCCCCGAAGCCCAAAGGCCCCGCCCAGCAGCCGCCTGGGCAGGAACAAA" /> + <has_text text="GGCTTCTCCCGGGGCCCTGGGGCCCCAGCCTCACCCTCAGCTTCCCACCC" /> + <has_text text="CCAGGGCCTAGACACGACCCCCAAGCCACACTGA" /> + </assert_contents> + </output></test><test><param name="input" value="droPer1.bed" dbkey="droPer1" ftype="bed" /> @@ -152,14 +166,14 @@ Extracting sequences with **FASTA** output data type returns:: - >hg17_chr7_127475281_127475310_+ + >hg17_chr7_127475281_127475310_+ NM_000230 GTAGGAATCGCAGCGCCAGCGGTTGCAAG - >hg17_chr7_127485994_127486166_+ + >hg17_chr7_127485994_127486166_+ NM_000230 GCCCAAGAAGCCCATCCTGGGAAGGAAAATGCATTGGGGAACCCTGTGCG GATTCTTGTGGCTTTGGCCCTATCTTTTCTATGTCCAAGCTGTGCCCATC CAAAAAGTCCAAGATGACACCAAAACCCTCATCAAGACAATTGTCACCAG GATCAATGACATTTCACACACG - >hg17_chr7_127486011_127486166_+ + >hg17_chr7_127486011_127486166_+ D49487 TGGGAAGGAAAATGCATTGGGGAACCCTGTGCGGATTCTTGTGGCTTTGG CCCTATCTTTTCTATGTCCAAGCTGTGCCCATCCAAAAAGTCCAAGATGA CACCAAAACCCTCATCAAGACAATTGTCACCAGGATCAATGACATTTCAC Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.