3 new commits in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/changeset/87a113b8473f/ changeset: 87a113b8473f user: jgoecks date: 2012-10-19 20:02:23 summary: Set up track config in BackboneTrack objects and use config in Circster. affected #: 3 files diff -r 083d480bf5e35009f01e9439f11aa7e68716e429 -r 87a113b8473f2d69d3c0c3393a46a9f968ac234e static/scripts/utils/config.js --- a/static/scripts/utils/config.js +++ b/static/scripts/utils/config.js @@ -4,28 +4,19 @@ * A configuration setting. Currently key is used as id. */ var ConfigSetting = Backbone.Model.extend({ - defaults: { - key: null, - value: null, - type: 'text', - label: null, - options: null, - hidden: false - }, - initialize: function(options) { // Use key as id for now. var key = this.get('key'); this.set('id', key); // Set defaults based on key. - var defaults = _.find(ConfigSetting.known_settings, function(s) { return s.key === key; }); + var defaults = _.find(ConfigSetting.known_settings_defaults, function(s) { return s.key === key; }); if (defaults) { this.set(_.extend({}, defaults, options)); } - // If type color, get random color. - if (this.get('type') === 'color') { + // If type color and no value, get random color. + if (this.get('type') === 'color' && !this.get('value')) { this.set('value', util_mod.get_random_color()); } @@ -50,22 +41,22 @@ this.set('value'); } }, { - // Keep a master list of settings. This list is useful to fetching attributes based on key. - known_settings: [ + // This is a master list of default settings for known settings. + known_settings_defaults: [ { key: 'name', label: 'Name', type: 'text', default_value: '' }, - { key: 'color', label: 'Color', type: 'color', default_value: undefined }, - { key: 'min_value', label: 'Min Value', type: 'float', default_value: undefined }, - { key: 'max_value', label: 'Max Value', type: 'float', default_value: undefined }, + { key: 'color', label: 'Color', type: 'color', default_value: null }, + { key: 'min_value', label: 'Min Value', type: 'float', default_value: null }, + { key: 'max_value', label: 'Max Value', type: 'float', default_value: null }, { key: 'mode', type: 'string', default_value: this.mode, hidden: true }, { key: 'height', type: 'int', default_value: 32, hidden: true }, { key: 'pos_color', label: 'Positive Color', type: 'color', default_value: "4169E1" }, { key: 'negative_color', label: 'Negative Color', type: 'color', default_value: "FF8C00" }, - { key: 'block_color', label: 'Block color', type: 'color', default_value: undefined }, + { key: 'block_color', label: 'Block color', type: 'color', default_value: null }, { key: 'label_color', label: 'Label color', type: 'color', default_value: 'black' }, { key: 'show_insertions', label: 'Show insertions', type: 'bool', default_value: false }, { key: 'show_counts', label: 'Show summary counts', type: 'bool', default_value: true }, { key: 'mode', type: 'string', default_value: this.mode, hidden: true }, - { key: 'reverse_strand_color', label: 'Antisense strand color', type: 'color', default_value: undefined }, + { key: 'reverse_strand_color', label: 'Antisense strand color', type: 'color', default_value: null }, { key: 'show_differences', label: 'Show differences only', type: 'bool', default_value: true }, { key: 'histogram_max', label: 'Histogram maximum', type: 'float', default_value: null, help: 'Clear value to set automatically' }, { key: 'mode', type: 'string', default_value: this.mode, hidden: true } @@ -92,24 +83,15 @@ }, /** - * Restore settings' values from a dictionary of key-value pairs. - * This function is needed for backwards compatibility. - */ - restore_values: function(values) { - var self = this; - _.keys(values, function(key) { - var setting = self.find(function(s) { return s.get('key') === key; }); - if (setting) { - setting.set('value', values.key); - } - }); - }, - - /** - * Returns value for a given key. + * Returns value for a given key. Returns undefined if there is no setting with the specified key. */ get_value: function(key) { - return this.get(key).get('value'); + var s = this.get(key); + if (s) { + return s.get('value'); + } + + return undefined; } }, { /** diff -r 083d480bf5e35009f01e9439f11aa7e68716e429 -r 87a113b8473f2d69d3c0c3393a46a9f968ac234e static/scripts/viz/circster.js --- a/static/scripts/viz/circster.js +++ b/static/scripts/viz/circster.js @@ -251,13 +251,6 @@ // When data is ready, render track. $.when(data_ready_deferred).then(function() { $.when(self._render_data(track_parent_elt)).then(function() { - // Apply prefs to all track data. - // TODO: move to _render_data? - var prefs = self.options.track.get('prefs'), - block_color = prefs.block_color; - if (!block_color) { block_color = prefs.color; } - track_parent_elt.selectAll('path.chrom-data').style('stroke', block_color).style('fill', block_color); - chroms_paths.style("fill", self.options.bg_fill); }); }); @@ -393,6 +386,12 @@ return self._render_chrom_data(svg, chrom_arc, data); }); + // Apply prefs to all track data. + var config = track.get('config'), + block_color = config.get_value('block_color');; + if (!block_color) { block_color = config.get_value('color'); } + self.options.parent_elt.selectAll('path.chrom-data').style('stroke', block_color).style('fill', block_color); + rendered_deferred.resolve(svg); }); diff -r 083d480bf5e35009f01e9439f11aa7e68716e429 -r 87a113b8473f2d69d3c0c3393a46a9f968ac234e static/scripts/viz/visualization.js --- a/static/scripts/viz/visualization.js +++ b/static/scripts/viz/visualization.js @@ -731,9 +731,16 @@ this.set('id', options.dataset_id); // -- Set up config settings. -- - this.set('settings', config_mod.ConfigSettingCollection.from_config_dict(options.prefs)); - // Set up data manager. + this.set('config', config_mod.ConfigSettingCollection.from_config_dict(options.prefs)); + + // Set up some minimal config. + this.get('config').add( [ + { key: 'name', value: this.get('name') }, + { key: 'color' } + ] ); + + // -- Set up data manager. -- var preloaded_data = this.get('preloaded_data'); if (preloaded_data) { preloaded_data = preloaded_data.data; https://bitbucket.org/galaxy/galaxy-central/changeset/d0e40dd01870/ changeset: d0e40dd01870 user: jgoecks date: 2012-10-20 03:07:56 summary: Circster: add programmatic support for removing tracks. affected #: 1 file diff -r 87a113b8473f2d69d3c0c3393a46a9f968ac234e -r d0e40dd01870b6b408d64d25c57edbd1f7b39288 static/scripts/viz/circster.js --- a/static/scripts/viz/circster.js +++ b/static/scripts/viz/circster.js @@ -50,8 +50,9 @@ this.scale = 1; this.track_views = null; - // When track added to the model, add to view as well. + // When tracks added to/removed from model, update view. this.model.get('tracks').on('add', this.add_track, this); + this.model.get('tracks').on('remove', this.remove_track, this); }, /** @@ -158,8 +159,6 @@ * Render a single track on the outside of the current visualization. */ add_track: function(new_track) { - // TODO: Reset scale and zoom. - // Recompute and update track bounds. var new_track_bounds = this.get_tracks_bounds(); _.each(this.track_views, function(track_view, i) { @@ -186,6 +185,23 @@ var track_bounds = new_track_bounds[ new_track_bounds.length-1 ]; track_bounds[1] = track_bounds[0]; this.label_track_view.update_radius_bounds(track_bounds); + }, + + /** + * Remove a track from the view. + */ + remove_track: function(track, tracks, options) { + // -- Remove track from view. -- + var track_view = this.track_views[options.index]; + this.track_views.splice(options.index, 1); + track_view.$el.remove(); + + // Recompute and update track bounds. + var new_track_bounds = this.get_tracks_bounds(); + _.each(this.track_views, function(track_view, i) { + //console.log(self.get_tracks_bounds(), i); + track_view.update_radius_bounds(new_track_bounds[i]); + }); } }); https://bitbucket.org/galaxy/galaxy-central/changeset/e26fec7ee17f/ changeset: e26fec7ee17f user: jgoecks date: 2012-10-20 03:08:16 summary: merge affected #: 16 files diff -r d0e40dd01870b6b408d64d25c57edbd1f7b39288 -r e26fec7ee17feb082efe7c9f815c5821f07e0f32 lib/galaxy/datatypes/data.py --- a/lib/galaxy/datatypes/data.py +++ b/lib/galaxy/datatypes/data.py @@ -536,7 +536,9 @@ TODO: Do we need to merge gzip files using gzjoin? cat seems to work, but might be brittle. Need to revisit this. """ - if len(split_files) == 1: + if not split_files: + raise ValueError('Asked to merge zero files as %s' % output_file) + elif len(split_files) == 1: cmd = 'mv -f %s %s' % ( split_files[0], output_file ) else: cmd = 'cat %s > %s' % ( ' '.join(split_files), output_file ) diff -r d0e40dd01870b6b408d64d25c57edbd1f7b39288 -r e26fec7ee17feb082efe7c9f815c5821f07e0f32 lib/galaxy/jobs/splitters/multi.py --- a/lib/galaxy/jobs/splitters/multi.py +++ b/lib/galaxy/jobs/splitters/multi.py @@ -115,6 +115,7 @@ try: working_directory = job_wrapper.working_directory task_dirs = [os.path.join(working_directory, x) for x in os.listdir(working_directory) if x.startswith('task_')] + assert task_dirs, "Should be at least one sub-task!" # TODO: Output datasets can be very complex. This doesn't handle metadata files outputs = job_wrapper.get_output_hdas_and_fnames() pickone_done = [] @@ -129,10 +130,18 @@ # Just include those files f in the output list for which the # file f exists; some files may not exist if a task fails. output_files = [ f for f in output_files if os.path.exists(f) ] - log.debug('files %s ' % output_files) - output_type.merge(output_files, output_file_name) - log.debug('merge finished: %s' % output_file_name) - pass # TODO: merge all the files + if output_files: + log.debug('files %s ' % output_files) + if len(output_files) < len(task_dirs): + log.debug('merging only %i out of expected %i files for %s' + % (len(output_files), len(task_dirs), output_file_name)) + output_type.merge(output_files, output_file_name) + log.debug('merge finished: %s' % output_file_name) + else: + msg = 'nothing to merge for %s (expected %i files)' \ + % (output_file_name, len(task_dirs)) + log.debug(msg) + stderr += msg + "\n" elif output in pickone_outputs: # just pick one of them if output not in pickone_done: diff -r d0e40dd01870b6b408d64d25c57edbd1f7b39288 -r e26fec7ee17feb082efe7c9f815c5821f07e0f32 lib/galaxy/webapps/community/controllers/admin.py --- a/lib/galaxy/webapps/community/controllers/admin.py +++ b/lib/galaxy/webapps/community/controllers/admin.py @@ -7,7 +7,7 @@ from galaxy.util import inflector from galaxy.util.shed_util import get_changectx_for_changeset, get_configured_ui from common import * -from repository import RepositoryListGrid, CategoryListGrid +from repository import RepositoryGrid, CategoryGrid from galaxy import eggs eggs.require( 'mercurial' ) @@ -17,7 +17,7 @@ log = logging.getLogger( __name__ ) -class UserListGrid( grids.Grid ): +class UserGrid( grids.Grid ): # TODO: move this to an admin_common controller since it is virtually the same in the galaxy webapp. class UserLoginColumn( grids.TextColumn ): def get_value( self, trans, grid, user ): @@ -66,10 +66,10 @@ default_sort_key = "email" columns = [ UserLoginColumn( "Email", - key="email", - link=( lambda item: dict( operation="information", id=item.id ) ), - attach_popup=True, - filterable="advanced" ), + key="email", + link=( lambda item: dict( operation="information", id=item.id ) ), + attach_popup=True, + filterable="advanced" ), UserNameColumn( "User Name", key="username", attach_popup=False, @@ -116,7 +116,7 @@ def get_current_item( self, trans, **kwargs ): return trans.user -class RoleListGrid( grids.Grid ): +class RoleGrid( grids.Grid ): # TODO: move this to an admin_common controller since it is virtually the same in the galaxy webapp. class NameColumn( grids.TextColumn ): def get_value( self, trans, grid, role ): @@ -207,7 +207,7 @@ def apply_query_filter( self, trans, query, **kwd ): return query.filter( model.Role.type != model.Role.types.PRIVATE ) -class GroupListGrid( grids.Grid ): +class GroupGrid( grids.Grid ): # TODO: move this to an admin_common controller since it is virtually the same in the galaxy webapp. class NameColumn( grids.TextColumn ): def get_value( self, trans, grid, group ): @@ -278,34 +278,35 @@ preserve_state = False use_paging = True -class ManageCategoryListGrid( CategoryListGrid ): - columns = [ col for col in CategoryListGrid.columns ] +class ManageCategoryGrid( CategoryGrid ): + columns = [ col for col in CategoryGrid.columns ] # Override the NameColumn to include an Edit link - columns[ 0 ] = CategoryListGrid.NameColumn( "Name", - key="Category.name", - link=( lambda item: dict( operation="Edit", id=item.id ) ), - model_class=model.Category, - attach_popup=False ) + columns[ 0 ] = CategoryGrid.NameColumn( "Name", + key="Category.name", + link=( lambda item: dict( operation="Edit", id=item.id ) ), + model_class=model.Category, + attach_popup=False ) global_actions = [ grids.GridAction( "Add new category", dict( controller='admin', action='manage_categories', operation='create' ) ) ] -class AdminRepositoryListGrid( RepositoryListGrid ): - columns = [ RepositoryListGrid.NameColumn( "Name", - key="name", - link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ), - attach_popup=True ), - RepositoryListGrid.DescriptionColumn( "Synopsis", - key="description", - attach_popup=False ), - RepositoryListGrid.MetadataRevisionColumn( "Metadata Revisions" ), - RepositoryListGrid.UserColumn( "Owner", - model_class=model.User, - link=( lambda item: dict( operation="repositories_by_user", id=item.id ) ), - attach_popup=False, - key="User.username" ), - RepositoryListGrid.EmailAlertsColumn( "Alert", attach_popup=False ), +class AdminRepositoryGrid( RepositoryGrid ): + columns = [ RepositoryGrid.NameColumn( "Name", + key="name", + link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ), + attach_popup=True ), + RepositoryGrid.DescriptionColumn( "Synopsis", + key="description", + attach_popup=False ), + RepositoryGrid.MetadataRevisionColumn( "Metadata Revisions" ), + RepositoryGrid.UserColumn( "Owner", + model_class=model.User, + link=( lambda item: dict( operation="repositories_by_user", id=item.id ) ), + attach_popup=False, + key="User.username" ), + RepositoryGrid.EmailAlertsColumn( "Alert", attach_popup=False ), + RepositoryGrid.DeprecatedColumn( "Deprecated", attach_popup=False ), # Columns that are valid for filtering but are not visible. grids.DeletedColumn( "Deleted", key="deleted", @@ -316,7 +317,7 @@ key="free-text-search", visible=False, filterable="standard" ) ) - operations = [ operation for operation in RepositoryListGrid.operations ] + operations = [ operation for operation in RepositoryGrid.operations ] operations.append( grids.GridOperation( "Delete", allow_multiple=False, condition=( lambda item: not item.deleted ), @@ -327,7 +328,7 @@ async_compatible=False ) ) standard_filters = [] -class RepositoryMetadataListGrid( grids.Grid ): +class RepositoryMetadataGrid( grids.Grid ): class IdColumn( grids.IntegerColumn ): def get_value( self, trans, grid, repository_metadata ): return repository_metadata.id @@ -414,17 +415,18 @@ preserve_state = False use_paging = True def build_initial_query( self, trans, **kwd ): - return trans.sa_session.query( self.model_class ) \ - .join( model.Repository.table ) + return trans.sa_session.query( model.RepositoryMetadata ) \ + .join( model.Repository.table ) \ + .filter( model.Repository.table.c.deprecated == False ) class AdminController( BaseUIController, Admin ): - user_list_grid = UserListGrid() - role_list_grid = RoleListGrid() - group_list_grid = GroupListGrid() - manage_category_list_grid = ManageCategoryListGrid() - repository_list_grid = AdminRepositoryListGrid() - repository_metadata_list_grid = RepositoryMetadataListGrid() + user_list_grid = UserGrid() + role_list_grid = RoleGrid() + group_list_grid = GroupGrid() + manage_category_grid = ManageCategoryGrid() + repository_grid = AdminRepositoryGrid() + repository_metadata_grid = RepositoryMetadataGrid() @web.expose @web.require_admin @@ -477,7 +479,7 @@ return self.delete_repository( trans, **kwd ) elif operation == "undelete": return self.undelete_repository( trans, **kwd ) - # The changeset_revision_select_field in the RepositoryListGrid performs a refresh_on_change + # The changeset_revision_select_field in the RepositoryGrid performs a refresh_on_change # which sends in request parameters like changeset_revison_1, changeset_revision_2, etc. One # of the many select fields on the grid performed the refresh_on_change, so we loop through # all of the received values to see which value is not the repository tip. If we find it, we @@ -495,7 +497,7 @@ id=trans.security.encode_id( repository.id ), changeset_revision=v ) ) # Render the list view - return self.repository_list_grid( trans, **kwd ) + return self.repository_grid( trans, **kwd ) @web.expose @web.require_admin def browse_repository_metadata( self, trans, **kwd ): @@ -515,7 +517,7 @@ return trans.response.send_redirect( web.url_for( controller='repository', action='browse_repositories', **kwd ) ) - return self.repository_metadata_list_grid( trans, **kwd ) + return self.repository_metadata_grid( trans, **kwd ) @web.expose @web.require_admin def create_category( self, trans, **kwd ): @@ -645,9 +647,9 @@ @web.require_admin def manage_categories( self, trans, **kwd ): if 'f-free-text-search' in kwd: - # Trick to enable searching repository name, description from the CategoryListGrid. - # What we've done is rendered the search box for the RepositoryListGrid on the grid.mako - # template for the CategoryListGrid. See ~/templates/webapps/community/category/grid.mako. + # Trick to enable searching repository name, description from the CategoryGrid. + # What we've done is rendered the search box for the RepositoryGrid on the grid.mako + # template for the CategoryGrid. See ~/templates/webapps/community/category/grid.mako. # Since we are searching repositories and not categories, redirect to browse_repositories(). return trans.response.send_redirect( web.url_for( controller='admin', action='browse_repositories', @@ -674,7 +676,7 @@ return trans.response.send_redirect( web.url_for( controller='admin', action='edit_category', **kwd ) ) - return self.manage_category_list_grid( trans, **kwd ) + return self.manage_category_grid( trans, **kwd ) @web.expose @web.require_admin def regenerate_statistics( self, trans, **kwd ): @@ -732,7 +734,8 @@ multiple=True, display='checkboxes' ) for repository in trans.sa_session.query( trans.model.Repository ) \ - .filter( trans.model.Repository.table.c.deleted == False ) \ + .filter( and_( trans.model.Repository.table.c.deleted == False, + trans.model.Repository.table.c.deprecated == False ) ) \ .order_by( trans.model.Repository.table.c.name, trans.model.Repository.table.c.user_id ): owner = repository.user.username diff -r d0e40dd01870b6b408d64d25c57edbd1f7b39288 -r e26fec7ee17feb082efe7c9f815c5821f07e0f32 lib/galaxy/webapps/community/controllers/repository.py --- a/lib/galaxy/webapps/community/controllers/repository.py +++ b/lib/galaxy/webapps/community/controllers/repository.py @@ -25,7 +25,7 @@ VALID_REPOSITORYNAME_RE = re.compile( "^[a-z0-9\_]+$" ) -class CategoryListGrid( grids.Grid ): +class CategoryGrid( grids.Grid ): class NameColumn( grids.TextColumn ): def get_value( self, trans, grid, category ): return category.name @@ -65,14 +65,14 @@ preserve_state = False use_paging = True -class ValidCategoryListGrid( CategoryListGrid ): +class ValidCategoryGrid( CategoryGrid ): class RepositoriesColumn( grids.TextColumn ): def get_value( self, trans, grid, category ): if category.repositories: viewable_repositories = 0 for rca in category.repositories: repository = rca.repository - if repository.downloadable_revisions: + if not repository.deprecated and repository.downloadable_revisions: viewable_repositories += 1 return viewable_repositories return 0 @@ -81,13 +81,13 @@ template='/webapps/community/category/valid_grid.mako' default_sort_key = "name" columns = [ - CategoryListGrid.NameColumn( "Name", - key="Category.name", - link=( lambda item: dict( operation="valid_repositories_by_category", id=item.id ) ), - attach_popup=False ), - CategoryListGrid.DescriptionColumn( "Description", - key="Category.description", - attach_popup=False ), + CategoryGrid.NameColumn( "Name", + key="Category.name", + link=( lambda item: dict( operation="valid_repositories_by_category", id=item.id ) ), + attach_popup=False ), + CategoryGrid.DescriptionColumn( "Description", + key="Category.description", + attach_popup=False ), # Columns that are valid for filtering but are not visible. RepositoriesColumn( "Valid repositories", model_class=model.Repository, @@ -102,7 +102,7 @@ preserve_state = False use_paging = True -class RepositoryListGrid( grids.Grid ): +class RepositoryGrid( grids.Grid ): class NameColumn( grids.TextColumn ): def get_value( self, trans, grid, repository ): return repository.name @@ -188,6 +188,11 @@ if trans.user and repository.email_alerts and trans.user.email in from_json_string( repository.email_alerts ): return 'yes' return '' + class DeprecatedColumn( grids.TextColumn ): + def get_value( self, trans, grid, repository ): + if repository.deprecated: + return 'yes' + return '' # Grid definition title = "Repositories" model_class = model.Repository @@ -248,21 +253,83 @@ .outerjoin( model.RepositoryCategoryAssociation.table ) \ .outerjoin( model.Category.table ) -class EmailAlertsRepositoryListGrid( RepositoryListGrid ): +class RepositoriesIOwnGrid( RepositoryGrid ): + title = "Repositories I own" columns = [ - RepositoryListGrid.NameColumn( "Name", - key="name", - link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ), + RepositoryGrid.NameColumn( "Name", + key="name", + link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ), + attach_popup=True ), + RepositoryGrid.MetadataRevisionColumn( "Metadata Revisions" ), + RepositoryGrid.TipRevisionColumn( "Tip Revision" ), + RepositoryGrid.CategoryColumn( "Category", + model_class=model.Category, + key="Category.name", attach_popup=False ), - RepositoryListGrid.DescriptionColumn( "Synopsis", - key="description", - attach_popup=False ), - RepositoryListGrid.UserColumn( "Owner", - model_class=model.User, - link=( lambda item: dict( operation="repositories_by_user", id=item.id ) ), - attach_popup=False, - key="User.username" ), - RepositoryListGrid.EmailAlertsColumn( "Alert", attach_popup=False ), + RepositoryGrid.DeprecatedColumn( "Deprecated" ) + ] + columns.append( grids.MulticolFilterColumn( "Search repository name", + cols_to_filter=[ columns[0] ], + key="free-text-search", + visible=False, + filterable="standard" ) ) + operations = [ grids.GridOperation( "Mark as deprecated", + allow_multiple=False, + condition=( lambda item: not item.deleted and not item.deprecated ), + async_compatible=False ), + grids.GridOperation( "Mark as not deprecated", + allow_multiple=False, + condition=( lambda item: not item.deleted and item.deprecated ), + async_compatible=False ) ] + def build_initial_query( self, trans, **kwd ): + return trans.sa_session.query( model.Repository ) \ + .filter( model.Repository.table.c.user_id == trans.user.id ) \ + .join( model.User.table ) \ + .outerjoin( model.RepositoryCategoryAssociation.table ) \ + .outerjoin( model.Category.table ) + +class DeprecatedRepositoriesIOwnGrid( RepositoriesIOwnGrid ): + title = "Deprecated repositories I own" + columns = [ + RepositoriesIOwnGrid.NameColumn( "Name", + key="name", + link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ), + attach_popup=True ), + RepositoriesIOwnGrid.MetadataRevisionColumn( "Metadata Revisions" ), + RepositoriesIOwnGrid.TipRevisionColumn( "Tip Revision" ), + RepositoriesIOwnGrid.CategoryColumn( "Category", + model_class=model.Category, + key="Category.name", + attach_popup=False ), + ] + columns.append( grids.MulticolFilterColumn( "Search repository name", + cols_to_filter=[ columns[0] ], + key="free-text-search", + visible=False, + filterable="standard" ) ) + def build_initial_query( self, trans, **kwd ): + return trans.sa_session.query( model.Repository ) \ + .filter( and_( model.Repository.table.c.user_id == trans.user.id, + model.Repository.table.c.deprecated == True ) ) \ + .join( model.User.table ) \ + .outerjoin( model.RepositoryCategoryAssociation.table ) \ + .outerjoin( model.Category.table ) + +class EmailAlertsRepositoryGrid( RepositoryGrid ): + columns = [ + RepositoryGrid.NameColumn( "Name", + key="name", + link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ), + attach_popup=False ), + RepositoryGrid.DescriptionColumn( "Synopsis", + key="description", + attach_popup=False ), + RepositoryGrid.UserColumn( "Owner", + model_class=model.User, + link=( lambda item: dict( operation="repositories_by_user", id=item.id ) ), + attach_popup=False, + key="User.username" ), + RepositoryGrid.EmailAlertsColumn( "Alert", attach_popup=False ), # Columns that are valid for filtering but are not visible. grids.DeletedColumn( "Deleted", key="deleted", @@ -277,29 +344,67 @@ grids.GridAction( "User preferences", dict( controller='user', action='index', cntrller='repository' ) ) ] -class WritableRepositoryListGrid( RepositoryListGrid ): +class MyWritableRepositoriesGrid( RepositoryGrid ): + # This grid filters out repositories that have been marked as deprecated. + columns = [ + RepositoryGrid.NameColumn( "Name", + key="name", + link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ), + attach_popup=True ), + RepositoryGrid.MetadataRevisionColumn( "Metadata Revisions" ), + RepositoryGrid.TipRevisionColumn( "Tip Revision" ), + RepositoryGrid.UserColumn( "Owner", + model_class=model.User, + link=( lambda item: dict( operation="repositories_by_user", id=item.id ) ), + attach_popup=False, + key="User.username" ), + RepositoryGrid.EmailAlertsColumn( "Alert", attach_popup=False ), + # Columns that are valid for filtering but are not visible. + RepositoryGrid.EmailColumn( "Email", + model_class=model.User, + key="email", + visible=False ), + RepositoryGrid.RepositoryCategoryColumn( "Category", + model_class=model.Category, + key="Category.name", + visible=False ), + grids.DeletedColumn( "Deleted", + key="deleted", + visible=False, + filterable="advanced" ) + ] + columns.append( grids.MulticolFilterColumn( "Search repository name", + cols_to_filter=[ columns[0] ], + key="free-text-search", + visible=False, + filterable="standard" ) ) + operations = [ grids.GridOperation( "Receive email alerts", + allow_multiple=False, + condition=( lambda item: not item.deleted ), + async_compatible=False ) ] def build_initial_query( self, trans, **kwd ): # TODO: improve performance by adding a db table associating users with repositories for which they have write access. - username = kwd[ 'username' ] + username = trans.user.username clause_list = [] - for repository in trans.sa_session.query( self.model_class ) \ - .filter( self.model_class.table.c.deleted == False ): + for repository in trans.sa_session.query( model.Repository ) \ + .filter( and_( model.Repository.table.c.deprecated == False, + model.Repository.table.c.deleted == False ) ): allow_push = repository.allow_push if allow_push: allow_push_usernames = allow_push.split( ',' ) if username in allow_push_usernames: - clause_list.append( self.model_class.table.c.id == repository.id ) + clause_list.append( model.Repository.table.c.id == repository.id ) if clause_list: - return trans.sa_session.query( self.model_class ) \ + return trans.sa_session.query( model.Repository ) \ .filter( or_( *clause_list ) ) \ .join( model.User.table ) \ .outerjoin( model.RepositoryCategoryAssociation.table ) \ .outerjoin( model.Category.table ) # Return an empty query. - return trans.sa_session.query( self.model_class ) \ - .filter( self.model_class.table.c.id < 0 ) + return [] -class ValidRepositoryListGrid( RepositoryListGrid ): +class ValidRepositoryGrid( RepositoryGrid ): + # This grid filters out repositories that have been marked as deprecated. class CategoryColumn( grids.TextColumn ): def get_value( self, trans, grid, repository ): rval = '<ul>' @@ -330,16 +435,16 @@ return '' title = "Valid repositories" columns = [ - RepositoryListGrid.NameColumn( "Name", - key="name", - attach_popup=True ), - RepositoryListGrid.DescriptionColumn( "Synopsis", - key="description", - attach_popup=False ), + RepositoryGrid.NameColumn( "Name", + key="name", + attach_popup=True ), + RepositoryGrid.DescriptionColumn( "Synopsis", + key="description", + attach_popup=False ), RevisionColumn( "Installable Revisions" ), - RepositoryListGrid.UserColumn( "Owner", - model_class=model.User, - attach_popup=False ), + RepositoryGrid.UserColumn( "Owner", + model_class=model.User, + attach_popup=False ), # Columns that are valid for filtering but are not visible. RepositoryCategoryColumn( "Category", model_class=model.Category, @@ -355,22 +460,25 @@ def build_initial_query( self, trans, **kwd ): if 'id' in kwd: # The user is browsing categories of valid repositories, so filter the request by the received id, which is a category id. - return trans.sa_session.query( self.model_class ) \ + return trans.sa_session.query( model.Repository ) \ + .filter( model.Repository.table.c.deprecated == False ) \ .join( model.RepositoryMetadata.table ) \ .join( model.User.table ) \ .join( model.RepositoryCategoryAssociation.table ) \ .join( model.Category.table ) \ .filter( and_( model.Category.table.c.id == trans.security.decode_id( kwd[ 'id' ] ), model.RepositoryMetadata.table.c.downloadable == True ) ) - # The user performed a free text search on the ValidCategoryListGrid. - return trans.sa_session.query( self.model_class ) \ + # The user performed a free text search on the ValidCategoryGrid. + return trans.sa_session.query( model.Repository ) \ + .filter( model.Repository.table.c.deprecated == False ) \ .join( model.RepositoryMetadata.table ) \ .join( model.User.table ) \ .outerjoin( model.RepositoryCategoryAssociation.table ) \ .outerjoin( model.Category.table ) \ .filter( model.RepositoryMetadata.table.c.downloadable == True ) -class MatchedRepositoryListGrid( grids.Grid ): +class MatchedRepositoryGrid( grids.Grid ): + # This grid filters out repositories that have been marked as deprecated. class NameColumn( grids.TextColumn ): def get_value( self, trans, grid, repository_metadata ): return repository_metadata.repository.name @@ -413,38 +521,38 @@ if match_tuples: for match_tuple in match_tuples: repository_id, changeset_revision = match_tuple - clause_list.append( "%s=%d and %s='%s'" % ( self.model_class.table.c.repository_id, + clause_list.append( "%s=%d and %s='%s'" % ( model.RepositoryMetadata.table.c.repository_id, int( repository_id ), - self.model_class.table.c.changeset_revision, + model.RepositoryMetadata.table.c.changeset_revision, changeset_revision ) ) - return trans.sa_session.query( self.model_class ) \ + return trans.sa_session.query( model.RepositoryMetadata ) \ .join( model.Repository ) \ + .filter( model.Repository.table.c.deprecated == False ) \ .join( model.User.table ) \ .filter( or_( *clause_list ) ) \ .order_by( model.Repository.name ) # Return an empty query - return trans.sa_session.query( self.model_class ) \ - .join( model.Repository ) \ - .join( model.User.table ) \ - .filter( self.model_class.table.c.repository_id == 0 ) + return [] -class InstallMatchedRepositoryListGrid( MatchedRepositoryListGrid ): - columns = [ col for col in MatchedRepositoryListGrid.columns ] +class InstallMatchedRepositoryGrid( MatchedRepositoryGrid ): + columns = [ col for col in MatchedRepositoryGrid.columns ] # Override the NameColumn - columns[ 0 ] = MatchedRepositoryListGrid.NameColumn( "Name", - link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ), - attach_popup=False ) + columns[ 0 ] = MatchedRepositoryGrid.NameColumn( "Name", + link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ), + attach_popup=False ) class RepositoryController( BaseUIController, ItemRatings ): - install_matched_repository_list_grid = InstallMatchedRepositoryListGrid() - matched_repository_list_grid = MatchedRepositoryListGrid() - valid_repository_list_grid = ValidRepositoryListGrid() - repository_list_grid = RepositoryListGrid() - email_alerts_repository_list_grid = EmailAlertsRepositoryListGrid() - category_list_grid = CategoryListGrid() - valid_category_list_grid = ValidCategoryListGrid() - writable_repository_list_grid = WritableRepositoryListGrid() + install_matched_repository_grid = InstallMatchedRepositoryGrid() + matched_repository_grid = MatchedRepositoryGrid() + valid_repository_grid = ValidRepositoryGrid() + repository_grid = RepositoryGrid() + email_alerts_repository_grid = EmailAlertsRepositoryGrid() + category_grid = CategoryGrid() + valid_category_grid = ValidCategoryGrid() + my_writable_repositories_grid = MyWritableRepositoriesGrid() + repositories_i_own_grid = RepositoriesIOwnGrid() + deprecated_repositories_i_own_grid = DeprecatedRepositoriesIOwnGrid() def __add_hgweb_config_entry( self, trans, repository, repository_path ): # Add an entry in the hgweb.config file for a new repository. An entry looks something like: @@ -470,8 +578,8 @@ def browse_categories( self, trans, **kwd ): # The request came from the tool shed. if 'f-free-text-search' in kwd: - # Trick to enable searching repository name, description from the CategoryListGrid. What we've done is rendered the search box for the - # RepositoryListGrid on the grid.mako template for the CategoryListGrid. See ~/templates/webapps/community/category/grid.mako. Since we + # Trick to enable searching repository name, description from the CategoryGrid. What we've done is rendered the search box for the + # RepositoryGrid on the grid.mako template for the CategoryGrid. See ~/templates/webapps/community/category/grid.mako. Since we # are searching repositories and not categories, redirect to browse_repositories(). if 'id' in kwd and 'f-free-text-search' in kwd and kwd[ 'id' ] == kwd[ 'f-free-text-search' ]: # The value of 'id' has been set to the search string, which is a repository name. We'll try to get the desired encoded repository id to pass on. @@ -491,7 +599,7 @@ return trans.response.send_redirect( web.url_for( controller='repository', action='browse_repositories', **kwd ) ) - return self.category_list_grid( trans, **kwd ) + return self.category_grid( trans, **kwd ) @web.expose def browse_invalid_tools( self, trans, **kwd ): params = util.Params( kwd ) @@ -564,13 +672,27 @@ for k, v in kwd.items(): if k.startswith( 'f-' ): del kwd[ k ] - kwd[ 'f-email' ] = trans.user.email + return self.repositories_i_own_grid( trans, **kwd ) + elif operation == "deprecated_repositories_i_own": + # Eliminate the current filters if any exist. + for k, v in kwd.items(): + if k.startswith( 'f-' ): + del kwd[ k ] + return self.deprecated_repositories_i_own_grid( trans, **kwd ) + elif operation in [ 'mark as deprecated', 'mark as not deprecated' ]: + # Eliminate the current filters if any exist. + for k, v in kwd.items(): + if k.startswith( 'f-' ): + del kwd[ k ] + kwd[ 'mark_deprecated' ] = operation == 'mark as deprecated' + return trans.response.send_redirect( web.url_for( controller='repository', + action='deprecate', + **kwd ) ) elif operation == "reviewed_repositories_i_own": return trans.response.send_redirect( web.url_for( controller='repository_review', action='reviewed_repositories_i_own' ) ) - elif operation == "writable_repositories": - kwd[ 'username' ] = trans.user.username - return self.writable_repository_list_grid( trans, **kwd ) + elif operation == "my_writable_repositories": + return self.my_writable_repositories_grid( trans, **kwd ) elif operation == "repositories_by_category": # Eliminate the current filters if any exist. for k, v in kwd.items(): @@ -590,7 +712,7 @@ kwd[ 'message' ] = 'You must be logged in to set email alerts.' kwd[ 'status' ] = 'error' del kwd[ 'operation' ] - # The changeset_revision_select_field in the RepositoryListGrid performs a refresh_on_change + # The changeset_revision_select_field in the RepositoryGrid performs a refresh_on_change # which sends in request parameters like changeset_revison_1, changeset_revision_2, etc. One # of the many select fields on the grid performed the refresh_on_change, so we loop through # all of the received values to see which value is not the repository tip. If we find it, we @@ -607,7 +729,7 @@ operation='view_or_manage_repository', id=trans.security.encode_id( repository.id ), changeset_revision=v ) ) - return self.repository_list_grid( trans, **kwd ) + return self.repository_grid( trans, **kwd ) @web.expose def browse_repository( self, trans, id, **kwd ): params = util.Params( kwd ) @@ -637,7 +759,7 @@ if kwd[ 'f-free-text-search' ] == 'All': # The user performed a search, then clicked the "x" to eliminate the search criteria. new_kwd = {} - return self.valid_category_list_grid( trans, **new_kwd ) + return self.valid_category_grid( trans, **new_kwd ) # Since we are searching valid repositories and not categories, redirect to browse_valid_repositories(). if 'id' in kwd and 'f-free-text-search' in kwd and kwd[ 'id' ] == kwd[ 'f-free-text-search' ]: # The value of 'id' has been set to the search string, which is a repository name. @@ -658,7 +780,7 @@ return trans.response.send_redirect( web.url_for( controller='repository', action='browse_valid_repositories', **kwd ) ) - return self.valid_category_list_grid( trans, **kwd ) + return self.valid_category_grid( trans, **kwd ) @web.expose def browse_valid_repositories( self, trans, **kwd ): galaxy_url = kwd.get( 'galaxy_url', None ) @@ -667,7 +789,7 @@ # The user browsed to a category and then entered a search string, so get the category associated with it's value. category_name = kwd[ 'f-Category.name' ] category = get_category_by_name( trans, category_name ) - # Set the id value in kwd since it is required by the ValidRepositoryListGrid.build_initial_query method. + # Set the id value in kwd since it is required by the ValidRepositoryGrid.build_initial_query method. kwd[ 'id' ] = trans.security.encode_id( category.id ) if galaxy_url: trans.set_cookie( galaxy_url, name='toolshedgalaxyurl' ) @@ -690,7 +812,7 @@ category_id = kwd.get( 'id', None ) category = get_category( trans, category_id ) kwd[ 'f-Category.name' ] = category.name - # The changeset_revision_select_field in the ValidRepositoryListGrid performs a refresh_on_change which sends in request parameters like + # The changeset_revision_select_field in the ValidRepositoryGrid performs a refresh_on_change which sends in request parameters like # changeset_revison_1, changeset_revision_2, etc. One of the many select fields on the grid performed the refresh_on_change, so we loop # through all of the received values to see which value is not the repository tip. If we find it, we know the refresh_on_change occurred # and we have the necessary repository id and change set revision to pass on. @@ -708,11 +830,11 @@ url_args = dict( action='browse_valid_repositories', operation='preview_tools_in_changeset', repository_id=repository_id ) - self.valid_repository_list_grid.operations = [ grids.GridOperation( "Preview and install", + self.valid_repository_grid.operations = [ grids.GridOperation( "Preview and install", url_args=url_args, allow_multiple=False, async_compatible=False ) ] - return self.valid_repository_list_grid( trans, **kwd ) + return self.valid_repository_grid( trans, **kwd ) def __build_allow_push_select_field( self, trans, current_push_list, selected_value='none' ): options = [] for user in trans.sa_session.query( trans.model.User ): @@ -931,6 +1053,27 @@ message=message, status=status ) @web.expose + @web.require_login( "deprecate repository" ) + def deprecate( self, trans, **kwd ): + params = util.Params( kwd ) + message = util.restore_text( params.get( 'message', '' ) ) + status = params.get( 'status', 'done' ) + repository_id = params.get( 'id', None ) + repository = get_repository( trans, repository_id ) + mark_deprecated = util.string_as_bool( params.get( 'mark_deprecated', False ) ) + repository.deprecated = mark_deprecated + trans.sa_session.add( repository ) + trans.sa_session.flush() + if mark_deprecated: + message = 'The repository <b>%s</b> has been marked as deprecated.' % repository.name + else: + message = 'The repository <b>%s</b> has been marked as not deprecated.' % repository.name + trans.response.send_redirect( web.url_for( controller='repository', + action='browse_repositories', + operation='repositories_i_own', + message=message, + status=status ) ) + @web.expose def display_tool( self, trans, repository_id, tool_config, changeset_revision, **kwd ): params = util.Params( kwd ) message = util.restore_text( params.get( 'message', '' ) ) @@ -1049,16 +1192,16 @@ dict( controller='repository', action='find_tools' ) ), grids.GridAction( "Search for workflows", dict( controller='repository', action='find_workflows' ) ) ] - self.install_matched_repository_list_grid.global_actions = global_actions + self.install_matched_repository_grid.global_actions = global_actions install_url_args = dict( controller='repository', action='find_tools' ) operations = [ grids.GridOperation( "Install", url_args=install_url_args, allow_multiple=True, async_compatible=False ) ] - self.install_matched_repository_list_grid.operations = operations - return self.install_matched_repository_list_grid( trans, **kwd ) + self.install_matched_repository_grid.operations = operations + return self.install_matched_repository_grid( trans, **kwd ) else: kwd[ 'message' ] = "tool id: <b>%s</b><br/>tool name: <b>%s</b><br/>tool version: <b>%s</b><br/>exact matches only: <b>%s</b>" % \ ( self.__stringify( tool_ids ), self.__stringify( tool_names ), self.__stringify( tool_versions ), str( exact_matches_checked ) ) - self.matched_repository_list_grid.title = "Repositories with matching tools" - return self.matched_repository_list_grid( trans, **kwd ) + self.matched_repository_grid.title = "Repositories with matching tools" + return self.matched_repository_grid( trans, **kwd ) else: message = "No search performed - each field must contain the same number of comma-separated items." status = "error" @@ -1135,16 +1278,16 @@ dict( controller='repository', action='find_tools' ) ), grids.GridAction( "Search for workflows", dict( controller='repository', action='find_workflows' ) ) ] - self.install_matched_repository_list_grid.global_actions = global_actions + self.install_matched_repository_grid.global_actions = global_actions install_url_args = dict( controller='repository', action='find_workflows' ) operations = [ grids.GridOperation( "Install", url_args=install_url_args, allow_multiple=True, async_compatible=False ) ] - self.install_matched_repository_list_grid.operations = operations - return self.install_matched_repository_list_grid( trans, **kwd ) + self.install_matched_repository_grid.operations = operations + return self.install_matched_repository_grid( trans, **kwd ) else: kwd[ 'message' ] = "workflow name: <b>%s</b><br/>exact matches only: <b>%s</b>" % \ ( self.__stringify( workflow_names ), str( exact_matches_checked ) ) - self.matched_repository_list_grid.title = "Repositories with matching workflows" - return self.matched_repository_list_grid( trans, **kwd ) + self.matched_repository_grid.title = "Repositories with matching workflows" + return self.matched_repository_grid( trans, **kwd ) else: message = "No search performed - each field must contain the same number of comma-separated items." status = "error" @@ -1407,17 +1550,24 @@ status = params.get( 'status', 'done' ) # See if there are any RepositoryMetadata records since menu items require them. repository_metadata = trans.sa_session.query( model.RepositoryMetadata ).first() - # See if the current user owns any repositories that have been reviewed. + current_user = trans.user has_reviewed_repositories = False - current_user = trans.user + has_deprecated_repositories = False if current_user: + # See if the current user owns any repositories that have been reviewed. for repository in current_user.active_repositories: if repository.reviewed_revisions: has_reviewed_repositories = True break + # See if the current user has any repositories that have been marked as deprecated. + for repository in current_user.active_repositories: + if repository.deprecated: + has_deprecated_repositories = True + break return trans.fill_template( '/webapps/community/index.mako', repository_metadata=repository_metadata, has_reviewed_repositories=has_reviewed_repositories, + has_deprecated_repositories=has_deprecated_repositories, message=message, status=status ) @web.expose @@ -1738,7 +1888,8 @@ kwd[ 'message' ] = 'You must be logged in to set email alerts.' kwd[ 'status' ] = 'error' del kwd[ 'operation' ] - return self.email_alerts_repository_list_grid( trans, **kwd ) + self.email_alerts_repository_grid.title = "Set email alerts for repository changes" + return self.email_alerts_repository_grid( trans, **kwd ) def __new_state( self, trans, all_pages=False ): """ Create a new `DefaultToolState` for this tool. It will not be initialized diff -r d0e40dd01870b6b408d64d25c57edbd1f7b39288 -r e26fec7ee17feb082efe7c9f815c5821f07e0f32 lib/galaxy/webapps/community/controllers/repository_review.py --- a/lib/galaxy/webapps/community/controllers/repository_review.py +++ b/lib/galaxy/webapps/community/controllers/repository_review.py @@ -7,7 +7,7 @@ from galaxy.model.orm import * from sqlalchemy.sql.expression import func from common import * -from repository import RepositoryListGrid +from repository import RepositoryGrid from galaxy.util.shed_util import get_configured_ui from galaxy.util.odict import odict @@ -48,36 +48,33 @@ preserve_state = False use_paging = True -class RepositoriesWithReviewsGrid( RepositoryListGrid ): +class RepositoriesWithReviewsGrid( RepositoryGrid ): + # This grid filters out repositories that have been marked as deprecated. class ReviewersColumn( grids.TextColumn ): def get_value( self, trans, grid, repository ): + rval = '' if repository.reviewers: - rval = '' for user in repository.reviewers: - rval += '%s<br/>' % user.username - return rval - return '' - title = "All reviewed Repositories" + rval += '<a class="view-info" href="repository_reviews_by_user?id=%s">' % trans.security.encode_id( user.id ) + rval += '%s</a> | ' % user.username + rval = rval.rstrip( ' | ' ) + return rval + title = "All reviewed repositories" model_class = model.Repository template='/webapps/community/repository_review/grid.mako' default_sort_key = "Repository.name" columns = [ - RepositoryListGrid.NameColumn( "Repository name", - key="name", - link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ), - attach_popup=True ), - RepositoryListGrid.DescriptionColumn( "Synopsis", - key="description", - attach_popup=False ), - RepositoryListGrid.WithReviewsRevisionColumn( "Reviewed revisions" ), - RepositoryListGrid.WithoutReviewsRevisionColumn( "Revisions for review" ), - RepositoryListGrid.UserColumn( "Owner", - attach_popup=False ), - ReviewersColumn( "Reviewers", - attach_popup=False ) + RepositoryGrid.NameColumn( "Repository name", + key="name", + link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ), + attach_popup=True ), + RepositoryGrid.WithReviewsRevisionColumn( "Reviewed revisions" ), + RepositoryGrid.WithoutReviewsRevisionColumn( "Revisions for review" ), + RepositoryGrid.UserColumn( "Owner", attach_popup=False ), + ReviewersColumn( "Reviewers", attach_popup=False ) ] - columns.append( grids.MulticolFilterColumn( "Search repository name, description", - cols_to_filter=[ columns[0], columns[1] ], + columns.append( grids.MulticolFilterColumn( "Search repository name", + cols_to_filter=[ columns[0] ], key="free-text-search", visible=False, filterable="standard" ) ) @@ -89,12 +86,14 @@ ] def build_initial_query( self, trans, **kwd ): return trans.sa_session.query( model.Repository ) \ + .filter( model.Repository.table.c.deprecated == False ) \ .join( ( model.RepositoryReview.table, model.RepositoryReview.table.c.repository_id == model.Repository.table.c.id ) ) \ .join( ( model.User.table, model.User.table.c.id == model.Repository.table.c.user_id ) ) \ .outerjoin( ( model.ComponentReview.table, model.ComponentReview.table.c.repository_review_id == model.RepositoryReview.table.c.id ) ) \ .outerjoin( ( model.Component.table, model.Component.table.c.id == model.ComponentReview.table.c.component_id ) ) class RepositoriesWithoutReviewsGrid( RepositoriesWithReviewsGrid ): + # This grid filters out repositories that have been marked as deprecated. title = "Repositories with no reviews" columns = [ RepositoriesWithReviewsGrid.NameColumn( "Repository name", @@ -119,12 +118,15 @@ async_compatible=False ) ] def build_initial_query( self, trans, **kwd ): return trans.sa_session.query( model.Repository ) \ - .filter( model.Repository.reviews == None ) \ + .filter( and_( model.Repository.table.c.deprecated == False, + model.Repository.reviews == None ) ) \ .join( model.User.table ) class RepositoriesReviewedByMeGrid( RepositoriesWithReviewsGrid ): + # This grid filters out repositories that have been marked as deprecated. def build_initial_query( self, trans, **kwd ): return trans.sa_session.query( model.Repository ) \ + .filter( model.Repository.table.c.deprecated == False ) \ .join( ( model.RepositoryReview.table, model.RepositoryReview.table.c.repository_id == model.Repository.table.c.id ) ) \ .filter( model.RepositoryReview.table.c.user_id == trans.user.id ) \ .join( ( model.User.table, model.User.table.c.id == model.RepositoryReview.table.c.user_id ) ) \ @@ -132,6 +134,7 @@ .outerjoin( ( model.Component.table, model.Component.table.c.id == model.ComponentReview.table.c.component_id ) ) class RepositoryReviewsByUserGrid( grids.Grid ): + # This grid filters out repositories that have been marked as deprecated. class RepositoryNameColumn( grids.TextColumn ): def get_value( self, trans, grid, review ): return review.repository.name @@ -166,33 +169,61 @@ default_sort_key = 'repository_id' columns = [ RepositoryNameColumn( "Repository Name", - model_class=model.Repository, - key="Repository.name", - attach_popup=False ), + model_class=model.Repository, + key="Repository.name", + link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ), + attach_popup=True ), RepositoryDescriptionColumn( "Description", - model_class=model.Repository, - key="Repository.description", - attach_popup=False ), - RevisionColumn( "Revision", - attach_popup=False ), - RatingColumn( "Rating", - attach_popup=False ) + model_class=model.Repository, + key="Repository.description", + attach_popup=False ), + RevisionColumn( "Revision", attach_popup=False ), + RatingColumn( "Rating", attach_popup=False ), ] # Override these default_filter = {} global_actions = [] - operations = [] + operations = [ + grids.GridOperation( "Inspect repository revisions", + allow_multiple=False, + condition=( lambda item: not item.deleted ), + async_compatible=False ) + ] standard_filters = [] num_rows_per_page = 50 preserve_state = False use_paging = True def build_initial_query( self, trans, **kwd ): user_id = trans.security.decode_id( kwd[ 'id' ] ) - return trans.sa_session.query( self.model_class ) \ + return trans.sa_session.query( model.RepositoryReview ) \ .filter( and_( model.RepositoryReview.table.c.deleted == False, \ - model.RepositoryReview.table.c.user_id == user_id ) ) + model.RepositoryReview.table.c.user_id == user_id ) ) \ + .join( ( model.Repository.table, model.RepositoryReview.table.c.repository_id == model.Repository.table.c.id ) ) \ + .filter( model.Repository.table.c.deprecated == False ) class ReviewedRepositoriesIOwnGrid( RepositoriesWithReviewsGrid ): + title = "Reviewed repositories I own" + columns = [ + RepositoriesWithReviewsGrid.NameColumn( "Repository name", + key="name", + link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ), + attach_popup=True ), + RepositoriesWithReviewsGrid.WithReviewsRevisionColumn( "Reviewed revisions" ), + RepositoriesWithReviewsGrid.WithoutReviewsRevisionColumn( "Revisions for review" ), + RepositoriesWithReviewsGrid.ReviewersColumn( "Reviewers", attach_popup=False ), + RepositoryGrid.DeprecatedColumn( "Deprecated" ) + ] + columns.append( grids.MulticolFilterColumn( "Search repository name", + cols_to_filter=[ columns[0] ], + key="free-text-search", + visible=False, + filterable="standard" ) ) + operations = [ + grids.GridOperation( "Inspect repository revisions", + allow_multiple=False, + condition=( lambda item: not item.deleted ), + async_compatible=False ) + ] def build_initial_query( self, trans, **kwd ): return trans.sa_session.query( model.Repository ) \ .join( ( model.RepositoryReview.table, model.RepositoryReview.table.c.repository_id == model.Repository.table.c.id ) ) \ @@ -655,10 +686,26 @@ @web.expose @web.require_login( "repository reviews by user" ) def repository_reviews_by_user( self, trans, **kwd ): - # The user may not be the current user. The value of the received id is the encoded user id. params = util.Params( kwd ) message = util.restore_text( params.get( 'message', '' ) ) status = params.get( 'status', 'done' ) + + if 'operation' in kwd: + operation = kwd['operation'].lower() + # The value of the received id is the encoded review id. + review = get_review( trans, kwd[ 'id' ] ) + repository = review.repository + kwd[ 'id' ] = trans.security.encode_id( repository.id ) + if operation == "inspect repository revisions": + return trans.response.send_redirect( web.url_for( controller='repository_review', + action='manage_repository_reviews', + **kwd ) ) + if operation == "view_or_manage_repository": + kwd[ 'changeset_revision' ] = review.changeset_revision + return trans.response.send_redirect( web.url_for( controller='repository_review', + action='view_or_manage_repository', + **kwd ) ) + # The user may not be the current user. The value of the received id is the encoded user id. user = get_user( trans, kwd[ 'id' ] ) self.repository_reviews_by_user_grid.title = "All repository revision reviews for user '%s'" % user.username return self.repository_reviews_by_user_grid( trans, **kwd ) @@ -668,6 +715,13 @@ params = util.Params( kwd ) message = util.restore_text( params.get( 'message', '' ) ) status = params.get( 'status', 'done' ) + # The value of the received id is the encoded repository id. + if 'operation' in kwd: + operation = kwd['operation'].lower() + if operation == "view_or_manage_repository": + return trans.response.send_redirect( web.url_for( controller='repository_review', + action='view_or_manage_repository', + **kwd ) ) return self.reviewed_repositories_i_own_grid( trans, **kwd ) @web.expose @web.require_login( "select previous review" ) diff -r d0e40dd01870b6b408d64d25c57edbd1f7b39288 -r e26fec7ee17feb082efe7c9f815c5821f07e0f32 lib/galaxy/webapps/community/model/__init__.py --- a/lib/galaxy/webapps/community/model/__init__.py +++ b/lib/galaxy/webapps/community/model/__init__.py @@ -109,7 +109,8 @@ MARKED_FOR_REMOVAL = 'r', MARKED_FOR_ADDITION = 'a', NOT_TRACKED = '?' ) - def __init__( self, name=None, description=None, long_description=None, user_id=None, private=False, email_alerts=None, times_downloaded=0 ): + def __init__( self, name=None, description=None, long_description=None, user_id=None, private=False, email_alerts=None, times_downloaded=0, + deprecated=False ): self.name = name or "Unnamed repository" self.description = description self.long_description = long_description @@ -117,6 +118,7 @@ self.private = private self.email_alerts = email_alerts self.times_downloaded = times_downloaded + self.deprecated = deprecated @property def repo_path( self ): # Repository locations on disk are defined in the hgweb.config file diff -r d0e40dd01870b6b408d64d25c57edbd1f7b39288 -r e26fec7ee17feb082efe7c9f815c5821f07e0f32 lib/galaxy/webapps/community/model/mapping.py --- a/lib/galaxy/webapps/community/model/mapping.py +++ b/lib/galaxy/webapps/community/model/mapping.py @@ -111,7 +111,8 @@ Column( "private", Boolean, default=False ), Column( "deleted", Boolean, index=True, default=False ), Column( "email_alerts", JSONType, nullable=True ), - Column( "times_downloaded", Integer ) ) + Column( "times_downloaded", Integer ), + Column( "deprecated", Boolean, default=False ) ) RepositoryMetadata.table = Table( "repository_metadata", metadata, Column( "id", Integer, primary_key=True ), @@ -218,7 +219,10 @@ properties=dict( children=relation(Tag, backref=backref( 'parent', remote_side=[ Tag.table.c.id ] ) ) ) ) assign_mapper( context, Category, Category.table, - properties=dict( repositories=relation( RepositoryCategoryAssociation ) ) ) + properties=dict( repositories=relation( RepositoryCategoryAssociation, + secondary=Repository.table, + primaryjoin=( Category.table.c.id == RepositoryCategoryAssociation.table.c.category_id ), + secondaryjoin=( ( RepositoryCategoryAssociation.table.c.repository_id == Repository.table.c.id ) & ( Repository.table.c.deprecated == False ) ) ) ) ) assign_mapper( context, Repository, Repository.table, properties = dict( diff -r d0e40dd01870b6b408d64d25c57edbd1f7b39288 -r e26fec7ee17feb082efe7c9f815c5821f07e0f32 lib/galaxy/webapps/community/model/migrate/versions/0014_add_deprecated_column.py --- /dev/null +++ b/lib/galaxy/webapps/community/model/migrate/versions/0014_add_deprecated_column.py @@ -0,0 +1,53 @@ +""" +Migration script to add the deprecated column to the repository table. +""" + +from sqlalchemy import * +from sqlalchemy.orm import * +from migrate import * +from migrate.changeset import * + +# Need our custom types, but don't import anything else from model +from galaxy.model.custom_types import * + +import sys, logging +log = logging.getLogger( __name__ ) +log.setLevel(logging.DEBUG) +handler = logging.StreamHandler( sys.stdout ) +format = "%(name)s %(levelname)s %(asctime)s %(message)s" +formatter = logging.Formatter( format ) +handler.setFormatter( formatter ) +log.addHandler( handler ) + +metadata = MetaData( migrate_engine ) +db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) ) + +def upgrade(): + print __doc__ + metadata.reflect() + # Create and initialize imported column in job table. + Repository_table = Table( "repository", metadata, autoload=True ) + c = Column( "deprecated", Boolean, default=False ) + try: + # Create + c.create( Repository_table ) + assert c is Repository_table.c.deprecated + # Initialize. + if migrate_engine.name == 'mysql' or migrate_engine.name == 'sqlite': + default_false = "0" + elif migrate_engine.name == 'postgres': + default_false = "false" + db_session.execute( "UPDATE repository SET deprecated=%s" % default_false ) + except Exception, e: + print "Adding deprecated column to the repository table failed: %s" % str( e ) + log.debug( "Adding deprecated column to the repository table failed: %s" % str( e ) ) + +def downgrade(): + metadata.reflect() + # Drop email_alerts column from repository table. + Repository_table = Table( "repository", metadata, autoload=True ) + try: + Repository_table.c.deprecated.drop() + except Exception, e: + print "Dropping column deprecated from the repository table failed: %s" % str( e ) + log.debug( "Dropping column deprecated from the repository table failed: %s" % str( e ) ) diff -r d0e40dd01870b6b408d64d25c57edbd1f7b39288 -r e26fec7ee17feb082efe7c9f815c5821f07e0f32 templates/webapps/community/category/grid.mako --- a/templates/webapps/community/category/grid.mako +++ b/templates/webapps/community/category/grid.mako @@ -49,8 +49,8 @@ <%def name="grid_body( grid )"><% - from galaxy.webapps.community.controllers.repository import RepositoryListGrid - repo_grid = RepositoryListGrid() + from galaxy.webapps.community.controllers.repository import RepositoryGrid + repo_grid = RepositoryGrid() %> ${self.make_grid( grid, repo_grid )} </%def> diff -r d0e40dd01870b6b408d64d25c57edbd1f7b39288 -r e26fec7ee17feb082efe7c9f815c5821f07e0f32 templates/webapps/community/category/valid_grid.mako --- a/templates/webapps/community/category/valid_grid.mako +++ b/templates/webapps/community/category/valid_grid.mako @@ -48,8 +48,8 @@ <%def name="grid_body( grid )"><% - from galaxy.webapps.community.controllers.repository import ValidRepositoryListGrid - repo_grid = ValidRepositoryListGrid() + from galaxy.webapps.community.controllers.repository import ValidRepositoryGrid + repo_grid = ValidRepositoryGrid() %> ${self.make_grid( grid, repo_grid )} </%def> diff -r d0e40dd01870b6b408d64d25c57edbd1f7b39288 -r e26fec7ee17feb082efe7c9f815c5821f07e0f32 templates/webapps/community/index.mako --- a/templates/webapps/community/index.mako +++ b/templates/webapps/community/index.mako @@ -78,8 +78,13 @@ <a target="galaxy_main" href="${h.url_for( controller='repository', action='browse_repositories', operation='reviewed_repositories_i_own' )}">Reviewed repositories I own</a></div> %endif + %if has_deprecated_repositories: + <div class="toolTitle"> + <a target="galaxy_main" href="${h.url_for( controller='repository', action='browse_repositories', operation='deprecated_repositories_i_own' )}">Deprecated repositories I own</a> + </div> + %endif <div class="toolTitle"> - <a target="galaxy_main" href="${h.url_for( controller='repository', action='browse_repositories', operation='writable_repositories' )}">My writable repositories</a> + <a target="galaxy_main" href="${h.url_for( controller='repository', action='browse_repositories', operation='my_writable_repositories' )}">My writable repositories</a></div><div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='repository', action='browse_invalid_tools', cntrller='repository' )}">My invalid tools</a> diff -r d0e40dd01870b6b408d64d25c57edbd1f7b39288 -r e26fec7ee17feb082efe7c9f815c5821f07e0f32 templates/webapps/community/repository/manage_repository.mako --- a/templates/webapps/community/repository/manage_repository.mako +++ b/templates/webapps/community/repository/manage_repository.mako @@ -7,22 +7,25 @@ from galaxy.web.framework.helpers import time_ago is_admin = trans.user_is_admin() is_new = repository.is_new + is_deprecated = repository.deprecated can_contact_owner = trans.user and trans.user != repository.user - can_push = trans.app.security_agent.can_push( trans.user, repository ) + can_push = not is_deprecated and trans.app.security_agent.can_push( trans.user, repository ) can_upload = can_push - can_download = not is_new and ( not is_malicious or can_push ) + can_download = not is_deprecated and not is_new and ( not is_malicious or can_push ) can_browse_contents = not is_new - can_set_metadata = not is_new - can_rate = not is_new and trans.user and repository.user != trans.user + can_set_metadata = not is_new and not is_deprecated + can_rate = not is_new and not is_deprecated and trans.user and repository.user != trans.user can_view_change_log = not is_new if can_push: browse_label = 'Browse or delete repository tip files' else: browse_label = 'Browse repository tip files' can_set_malicious = metadata and can_set_metadata and is_admin and changeset_revision == repository.tip - can_reset_all_metadata = is_admin and len( repo ) > 0 + can_deprecate = not is_new and trans.user and ( is_admin or repository.user == trans.user ) and not is_deprecated + can_undeprecate = trans.user and ( is_admin or repository.user == trans.user ) and is_deprecated + can_reset_all_metadata = not is_deprecated and is_admin and len( repo ) > 0 has_readme = metadata and 'readme' in metadata - can_review_repository = trans.app.security_agent.user_can_review_repositories( trans.user ) + can_review_repository = not is_deprecated and trans.app.security_agent.user_can_review_repositories( trans.user ) reviewing_repository = cntrller and cntrller == 'repository_review' if changeset_revision == repository.tip: tip_str = 'repository tip' @@ -87,6 +90,12 @@ %if can_reset_all_metadata: <a class="action-button" href="${h.url_for( controller='repository', action='reset_all_metadata', id=trans.security.encode_id( repository.id ) )}">Reset all repository metadata</a> %endif + %if can_deprecate: + <a class="action-button" href="${h.url_for( controller='repository', action='deprecate', id=trans.security.encode_id( repository.id ), mark_deprecated=True )}">Mark repository as deprecated</a> + %endif + %if can_undeprecate: + <a class="action-button" href="${h.url_for( controller='repository', action='deprecate', id=trans.security.encode_id( repository.id ), mark_deprecated=False )}">Mark repository as not deprecated</a> + %endif %if can_download: <a class="action-button" href="${h.url_for( controller='repository', action='download', repository_id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, file_type='gz' )}">Download as a .tar.gz file</a><a class="action-button" href="${h.url_for( controller='repository', action='download', repository_id=trans.app.security.encode_id( repository.id ), changeset_revision=changeset_revision, file_type='bz2' )}">Download as a .tar.bz2 file</a> @@ -101,6 +110,12 @@ ${render_msg( message, status )} %endif +%if repository.deprecated: + <div class="warningmessage"> + This repository has been marked as deprecated, so some tool shed features may be restricted. + </div> +%endif + %if len( changeset_revision_select_field.options ) > 1: <div class="toolForm"><div class="toolFormTitle">Repository revision</div> @@ -122,7 +137,7 @@ <p/> %endif <div class="toolForm"> - <div class="toolFormTitle">${repository.name}</div> + <div class="toolFormTitle">Repository '${repository.name}'</div><div class="toolFormBody"><form name="edit_repository" id="edit_repository" action="${h.url_for( controller='repository', action='manage_repository', id=trans.security.encode_id( repository.id ) )}" method="post" > %if can_download: diff -r d0e40dd01870b6b408d64d25c57edbd1f7b39288 -r e26fec7ee17feb082efe7c9f815c5821f07e0f32 templates/webapps/community/repository/view_repository.mako --- a/templates/webapps/community/repository/view_repository.mako +++ b/templates/webapps/community/repository/view_repository.mako @@ -6,11 +6,12 @@ <% from galaxy.web.framework.helpers import time_ago is_new = repository.is_new + is_deprecated = repository.deprecated can_contact_owner = trans.user and trans.user != repository.user - can_push = trans.app.security_agent.can_push( trans.user, repository ) - can_rate = not is_new and trans.user and repository.user != trans.user + can_push = not is_deprecated and trans.app.security_agent.can_push( trans.user, repository ) + can_rate = not is_deprecated and not is_new and trans.user and repository.user != trans.user can_upload = can_push - can_download = not is_new and ( not is_malicious or can_push ) + can_download = not is_deprecated and not is_new and ( not is_malicious or can_push ) can_browse_contents = trans.webapp.name == 'community' and not is_new can_view_change_log = trans.webapp.name == 'community' and not is_new if can_push: @@ -19,7 +20,7 @@ browse_label = 'Browse repository tip files' has_readme = metadata and 'readme' in metadata reviewing_repository = cntrller and cntrller == 'repository_review' - can_review_repository = trans.app.security_agent.user_can_review_repositories( trans.user ) + can_review_repository = not is_deprecated and trans.app.security_agent.user_can_review_repositories( trans.user ) %><%! @@ -100,6 +101,12 @@ ${render_msg( message, status )} %endif +%if repository.deprecated: + <div class="warningmessage"> + This repository has been marked as deprecated, so some tool shed features may be restricted. + </div> +%endif + %if len( changeset_revision_select_field.options ) > 1: <div class="toolForm"><div class="toolFormTitle">Repository revision</div> @@ -123,7 +130,7 @@ <p/> %endif <div class="toolForm"> - <div class="toolFormTitle">${repository.name}</div> + <div class="toolFormTitle">Repository '${repository.name}'</div><div class="toolFormBody"> %if can_download: <div class="form-row"> diff -r d0e40dd01870b6b408d64d25c57edbd1f7b39288 -r e26fec7ee17feb082efe7c9f815c5821f07e0f32 templates/webapps/community/repository/view_tool_metadata.mako --- a/templates/webapps/community/repository/view_tool_metadata.mako +++ b/templates/webapps/community/repository/view_tool_metadata.mako @@ -110,7 +110,7 @@ <p/> %if can_download: <div class="toolForm"> - <div class="toolFormTitle">${repository.name}</div> + <div class="toolFormTitle">Repository '${repository.name}'</div><div class="toolFormBody"><div class="form-row"><label>Clone this repository:</label> diff -r d0e40dd01870b6b408d64d25c57edbd1f7b39288 -r e26fec7ee17feb082efe7c9f815c5821f07e0f32 tools/genomespace/genomespace_file_browser.py --- a/tools/genomespace/genomespace_file_browser.py +++ b/tools/genomespace/genomespace_file_browser.py @@ -39,6 +39,7 @@ 'gmt': 'gmt', 'gct': 'gct'} +VALID_CHARS = '.-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ' def chunk_write( source_stream, target_stream, source_method = "read", target_method="write" ): source_method = getattr( source_stream, source_method ) @@ -113,6 +114,7 @@ name = name[len( file_url_prefix ):] file_numbers.append( int( name ) ) file_numbers.sort() + used_filenames = [] for file_num in file_numbers: url_key = "%s%i" % ( file_url_prefix, file_num ) download_url = datasource_params.get( url_key, None ) @@ -135,8 +137,14 @@ parsed_url = urlparse.urlparse( download_url ) query_params = urlparse.parse_qs( parsed_url[4] ) filename = urllib.unquote_plus( parsed_url[2].split( '/' )[-1] ) + if not filename: + filename = download_url if output_filename is None: - output_filename = os.path.join( datasource_params['__new_file_path__'], 'primary_%i_output%i_visible_%s' % ( hda_id, file_num, galaxy_ext ) ) + filename = ''.join( c in VALID_CHARS and c or '-' for c in filename ) + while filename in used_filenames: + filename = "-%s" % filename + used_filenames.append( filename ) + output_filename = os.path.join( datasource_params['__new_file_path__'], 'primary_%i_%s_visible_%s' % ( hda_id, filename, galaxy_ext ) ) else: if dataset_id is not None: metadata_parameter_file.write( "%s\n" % simplejson.dumps( dict( type = 'dataset', diff -r d0e40dd01870b6b408d64d25c57edbd1f7b39288 -r e26fec7ee17feb082efe7c9f815c5821f07e0f32 tools/genomespace/genomespace_importer.py --- a/tools/genomespace/genomespace_importer.py +++ b/tools/genomespace/genomespace_importer.py @@ -43,7 +43,7 @@ 'gmt': 'gmt', 'gct': 'gct'} -VALID_CHARS = '.-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' +VALID_CHARS = '.-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ' def chunk_write( source_stream, target_stream, source_method = "read", target_method="write" ): source_method = getattr( source_stream, source_method ) @@ -112,6 +112,7 @@ datatypes_registry = Registry() datatypes_registry.load_datatypes( root_dir = json_params[ 'job_config' ][ 'GALAXY_ROOT_DIR' ], config = json_params[ 'job_config' ][ 'GALAXY_DATATYPES_CONF_FILE' ] ) url_param = datasource_params.get( file_url_name, None ) + used_filenames = [] for download_url in url_param.split( ',' ): using_temp_file = False parsed_url = urlparse.urlparse( download_url ) @@ -129,6 +130,8 @@ parsed_url = urlparse.urlparse( download_url ) query_params = urlparse.parse_qs( parsed_url[4] ) filename = urllib.unquote_plus( parsed_url[2].split( '/' )[-1] ) + if not filename: + filename = download_url if output_filename is None: #need to use a temp file here, because we do not know the ext yet using_temp_file = True @@ -179,7 +182,11 @@ name = "GenomeSpace importer on %s" % ( filename ) ) ) ) #if using tmp file, move the file to the new file path dir to get scooped up later if using_temp_file: - shutil.move( output_filename, os.path.join( datasource_params['__new_file_path__'], 'primary_%i_output%s_visible_%s' % ( hda_id, ''.join( c in VALID_CHARS and c or '-' for c in filename ), file_type ) ) ) + filename = ''.join( c in VALID_CHARS and c or '-' for c in filename ) + while filename in used_filenames: + filename = "-%s" % filename + used_filenames.append( filename ) + shutil.move( output_filename, os.path.join( datasource_params['__new_file_path__'], 'primary_%i_%s_visible_%s' % ( hda_id, filename, file_type ) ) ) dataset_id = None #only one primary dataset available output_filename = None #only have one filename available 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.