From 2ed131d206dbf73a141b7baf76c5ffcce6fef206 Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Sat, 14 Jan 2017 20:44:09 +0100 Subject: [PATCH] Fix display of repository dependency status that broke with the introduction of the all-at-once dependency resolution in #3391 and addresses in part #3398. I have only restored the (collapsed) display of dependencies for all requirements in a repository, so breaking the information out on a per tool basis remains on the TODO list. --- lib/galaxy/tools/__init__.py | 4 +-- lib/galaxy/tools/deps/__init__.py | 3 ++ lib/galaxy/tools/deps/requirements.py | 3 ++ lib/galaxy/tools/deps/views.py | 23 ++++++++++++- .../galaxy/controllers/admin_toolshed.py | 2 +- lib/tool_shed/util/shed_util_common.py | 32 ++++--------------- 6 files changed, 38 insertions(+), 29 deletions(-) diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py index 702cecee69c..545d16104a2 100755 --- a/lib/galaxy/tools/__init__.py +++ b/lib/galaxy/tools/__init__.py @@ -1401,7 +1401,7 @@ class Tool( object, Dictifiable ): """ Return all requiremens of type package """ - reqs = [req.to_dict() for req in self.requirements if req.type == 'package'] + reqs = [req for req in self.requirements if req.type == 'package'] return reqs @property @@ -1409,7 +1409,7 @@ class Tool( object, Dictifiable ): """ Return a list of dictionaries for all tool dependencies with their associated status """ - return self._view.get_requirements_status(self.tool_requirements, self.installed_tool_dependencies) + return self._view.get_requirements_status({self.id: self.tool_requirements}, self.installed_tool_dependencies) def build_redirect_url_params( self, param_dict ): """ diff --git a/lib/galaxy/tools/deps/__init__.py b/lib/galaxy/tools/deps/__init__.py index 117b5b49b87..6e03b09d3e2 100644 --- a/lib/galaxy/tools/deps/__init__.py +++ b/lib/galaxy/tools/deps/__init__.py @@ -123,6 +123,7 @@ class DependencyManager( object ): requirement_to_dependency = OrderedDict() index = kwds.get('index', None) require_exact = kwds.get('exact', False) + return_null_dependencies = kwds.get('return_null', False) resolvable_requirements = [] for requirement in requirements: @@ -164,6 +165,8 @@ class DependencyManager( object ): log.debug(dependency.resolver_msg) if not isinstance(dependency, NullDependency): requirement_to_dependency[requirement] = dependency + elif return_null_dependencies and (resolver == self.dependency_resolvers[-1] or i == index): + requirement_to_dependency[requirement] = dependency return requirement_to_dependency diff --git a/lib/galaxy/tools/deps/requirements.py b/lib/galaxy/tools/deps/requirements.py index 0414ea2526a..005fbfac399 100644 --- a/lib/galaxy/tools/deps/requirements.py +++ b/lib/galaxy/tools/deps/requirements.py @@ -28,6 +28,9 @@ class ToolRequirement( object ): def __eq__(self, other): return self.name == other.name and self.type == other.type and self.version == other.version + def __hash__(self): + return hash((self.name, self.type, self.version)) + DEFAULT_CONTAINER_TYPE = "docker" DEFAULT_CONTAINER_RESOLVE_DEPENDENCIES = False diff --git a/lib/galaxy/tools/deps/views.py b/lib/galaxy/tools/deps/views.py index d37fed4fc8f..8ef231dae73 100644 --- a/lib/galaxy/tools/deps/views.py +++ b/lib/galaxy/tools/deps/views.py @@ -45,6 +45,16 @@ class DependencyResolversView(object): def resolver_dependency(self, index, **kwds): return self._dependency(**kwds) + def show_dependencies(self, requirements, installed_tool_dependencies=None): + """ + Resolves dependencies to build a requirements status in the admin panel/API + """ + kwds = {'install': False, + 'return_null': True, + 'installed_tool_dependencies': installed_tool_dependencies} + dependencies_per_tool = {tool: self._dependency_manager.requirements_to_dependencies(dependencies, **kwds) for tool, dependencies in requirements.items()} + return dependencies_per_tool + def install_dependencies(self, requirements): return self._dependency_manager._requirements_to_dependencies_dict(requirements, **{'install': True}) @@ -129,7 +139,18 @@ class DependencyResolversView(object): return [index for index, resolver in enumerate(self._dependency_resolvers) if hasattr(resolver, "install_dependency") and not resolver.disabled ] def get_requirements_status(self, requested_requirements, installed_tool_dependencies=None): - return [self.manager_dependency(installed_tool_dependencies=installed_tool_dependencies, **req) for req in requested_requirements] + dependencies = self.show_dependencies(requested_requirements, installed_tool_dependencies) + # dependencies is a dict keyed on tool_ids, values are lists of ToolRequirements for that tool. + # we collapse requested requirements to single set, + # then use collapsed requirements to get resolved dependencies without duplicates. + flat_tool_requirements = set([r for requirement_list in requested_requirements.values() for r in requirement_list]) + flat_dependencies = [] + for requirements_odict in dependencies.values(): + for requirement in requirements_odict: + if requirement in flat_tool_requirements: + flat_dependencies.append(requirements_odict[requirement]) + flat_tool_requirements.remove(requirement) + return [d.to_dict() for d in flat_dependencies] def clean(self, index=None, **kwds): if index: diff --git a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py index 1c18fa1a5cd..fec0f425713 100644 --- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py +++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py @@ -781,7 +781,7 @@ class AdminToolshed( AdminGalaxy ): reinstalling=False, required_repo_info_dicts=None ) view = views.DependencyResolversView(self.app) - requirements = suc.get_unique_requirements_from_repository(repository) + requirements = suc.get_requirements_from_repository(repository) requirements_status = view.get_requirements_status(requirements, repository.installed_tool_dependencies) return trans.fill_template( '/admin/tool_shed_repository/manage_repository.mako', repository=repository, diff --git a/lib/tool_shed/util/shed_util_common.py b/lib/tool_shed/util/shed_util_common.py index dbf74d9896b..f54fd394ff7 100644 --- a/lib/tool_shed/util/shed_util_common.py +++ b/lib/tool_shed/util/shed_util_common.py @@ -9,6 +9,7 @@ import sqlalchemy.orm.exc from sqlalchemy import and_, false, true from galaxy import util +from galaxy.tools.deps.requirements import ToolRequirement from galaxy.util import checkers from galaxy.web import url_for from tool_shed.util import ( @@ -219,37 +220,18 @@ def get_tool_shed_repo_requirements(app, tool_shed_url, repositories=None, repo_ valid_tools = json_response[1].get('valid_tools', []) if valid_tools: tools.extend(valid_tools) - return get_unique_requirements_from_tools(tools) + return get_requirements_from_tools(tools) -def get_unique_requirements_from_tools(tools): - requirements = [] - for tool in tools: - if tool['requirements']: - requirements.append(tool['requirements']) - return get_unique_requirements(requirements) +def get_requirements_from_tools(tools): + return {tool['id']: [ToolRequirement.from_dict(r) for r in tool['requirements']] for tool in tools} -def get_unique_requirements(requirements): - uniq_reqs = dict() - for tool_requirements in requirements: - for req in tool_requirements: - name = req.get("name", None) - if not name: - continue # A requirement without a name can't be resolved, so let's skip those - version = req.get("version", "versionless") - type = req.get("type", None) - if not type == "package": - continue - uniq_reqs["%s_%s" % (name, version)] = {'name': name, 'version': version, 'type': type} - return list(uniq_reqs.values()) - - -def get_unique_requirements_from_repository(repository): +def get_requirements_from_repository(repository): if not repository.includes_tools: - return [] + return {} else: - return get_unique_requirements_from_tools(repository.metadata.get('tools', [])) + return get_requirements_from_tools(repository.metadata.get('tools', [])) def get_ctx_rev( app, tool_shed_url, name, owner, changeset_revision ):