Merge branch 'release_20.09' into dev

This commit is contained in:
Nicola Soranzo
2020-10-08 14:06:12 +01:00
14 changed files with 203 additions and 75 deletions
@@ -33,9 +33,7 @@
<template v-slot:cell(name)="row">
<b-link href="#" role="button" class="font-weight-bold" @click="row.toggleDetails">
<div v-if="!isLatest(row.item)">
<b-badge variant="danger" class="mb-2">
Newer version available!
</b-badge>
<b-badge variant="danger" class="mb-2"> Newer version available! </b-badge>
</div>
<div class="name">{{ row.item.name }}</div>
</b-link>
@@ -49,9 +47,7 @@
No matching entries found for: <span class="font-weight-bold">{{ this.filter }}</span
>.
</div>
<div v-if="showNotAvailable">
No installed repositories found.
</div>
<div v-if="showNotAvailable">No installed repositories found.</div>
</div>
</div>
</div>
@@ -123,7 +119,7 @@ export default {
load() {
this.loading = true;
this.services
.getInstalledRepositories()
.getInstalledRepositories({ selectLatest: true })
.then((repositories) => {
this.repositories = repositories;
this.nRepositories = repositories.length;
+14 -2
View File
@@ -78,7 +78,7 @@ export class Services {
const url = `${getAppRoot()}api/tool_shed_repositories/?uninstalled=False`;
try {
const response = await axios.get(url);
const repositories = this._groupByNameOwner(response.data, options.filter);
const repositories = this._groupByNameOwner(response.data, options.filter, options.selectLatest);
this._fixToolshedUrls(repositories, Galaxy.config.tool_shed_urls);
return repositories;
} catch (e) {
@@ -125,7 +125,19 @@ export class Services {
rethrowSimple(e);
}
}
_groupByNameOwner(incoming, filter) {
_groupByNameOwner(incoming, filter, selectLatest) {
if (selectLatest) {
const getSortValue = (x, y) => {
return x == y ? 0 : x < y ? -1 : 1;
};
incoming = incoming.sort((a, b) => {
return (
getSortValue(a.name, b.name) ||
getSortValue(a.owner, b.owner) ||
getSortValue(parseInt(b.ctx_rev), parseInt(a.ctx_rev))
);
});
}
const hash = {};
const repositories = [];
incoming.forEach((x) => {
@@ -8,6 +8,7 @@ describe("Toolshed service helpers", () => {
status: "status_0_0",
description: "description_0_0",
tool_shed: "url_1",
ctx_rev: "1",
},
{
name: "name_0",
@@ -15,6 +16,7 @@ describe("Toolshed service helpers", () => {
status: "status_0_1",
description: "description_0_1",
tool_shed: "url_1",
ctx_rev: "3",
},
{
name: "name_1",
@@ -22,6 +24,7 @@ describe("Toolshed service helpers", () => {
status: "status_1",
description: "description_1",
tool_shed: "url_2",
ctx_rev: "42",
},
{
name: "name_2",
@@ -29,6 +32,7 @@ describe("Toolshed service helpers", () => {
status: "Installed",
description: "description_2",
tool_shed: "url_2",
ctx_rev: "1",
},
];
const urls = ["http://url_1.com", "http://url_2.com"];
@@ -36,11 +40,12 @@ describe("Toolshed service helpers", () => {
it("test fix toolshed helper", () => {
const services = new Services();
const filter = (x) => x.status !== "Installed";
const grouped = services._groupByNameOwner(incoming, filter);
const grouped = services._groupByNameOwner(incoming, filter, true);
services._fixToolshedUrls(grouped, urls);
expect(grouped.length).toBe(2);
expect(grouped[0].name).toBe("name_0");
expect(grouped[0].tool_shed_url).toBe("http://url_1.com");
expect(grouped[0].ctx_rev).toBe("3");
expect(grouped[1].name === "name_1");
expect(grouped[1].tool_shed_url).toBe("http://url_2.com");
});
@@ -66,7 +66,7 @@ var FolderView = Backbone.View.extend({
is_admin = Galaxy.user.isAdmin();
}
var template = this.templateFolderPermissions();
this.$el.html(template({ folder: this.model, is_admin: is_admin }));
this.$el.html(template({ folder: this.model, is_admin: is_admin, root_path: getAppRoot() }));
var self = this;
$.get(`${getAppRoot()}api/folders/${self.id}/permissions?scope=current`)
@@ -204,7 +204,7 @@ var FolderView = Backbone.View.extend({
return _.template(
`<div class="library_style_container">
<div>
<a href="/library/folders/<%= folder.get("parent_id") %>">
<a href="<%- root_path %>library/folders/<%= folder.get("parent_id") %>">
<button data-toggle="tooltip" data-placement="top" title="Go back to the parent folder"
class="btn btn-secondary primary-button" type="button">
<span class="fa fa-caret-left fa-lg"/>
@@ -4,6 +4,7 @@ import Backbone from "backbone";
import { Toast } from "ui/toast";
import { getGalaxyInstance } from "app";
import { linkifyHtmlElements } from "mvc/library/library-util";
import { getAppRoot } from "onload/loadConfig";
// galaxy library row view
var LibraryRowView = Backbone.View.extend({
@@ -44,6 +45,7 @@ var LibraryRowView = Backbone.View.extend({
library: library,
button_config: this.element_visibility_config,
edit_mode: this.edit_mode,
root_path: getAppRoot(),
})
);
this.$el.show();
@@ -243,7 +245,7 @@ var LibraryRowView = Backbone.View.extend({
<td style="color:grey;"><%- library.get("name") %></td>
<% } else { %>
<td>
<a href="folders/<%- library.get("root_folder_id") %>"><%- library.get("name") %></a>
<a href="<%- root_path %>library/folders/<%- library.get("root_folder_id") %>"><%- library.get("name") %></a>
</td>
<% } %>
<% if(library.get("description")) { %>
+6 -2
View File
@@ -1,7 +1,11 @@
var gtnWebhookLoaded = false;
function removeOverlay() {
document.getElementById("gtn-container").classList.add("d-none");
document.getElementById("gtn-container").style.visibility = "hidden";
}
function showOverlay() {
document.getElementById("gtn-container").style.visibility = "visible";
}
function addIframe() {
@@ -105,7 +109,7 @@ elementReady("#gtn a").then((el) => {
if (!gtnWebhookLoaded) {
addIframe();
} else {
document.getElementById("gtn-container").classList.toggle("d-none");
showOverlay();
}
});
});
+14 -7
View File
@@ -919,19 +919,26 @@ class JobWrapper(HasResourceParameters):
if use_persisted_destination:
self.job_runner_mapper.cached_job_destination = JobDestination(from_job=job)
# Wrapper holding the info required to restore and clean up from files used for setting metadata externally
try:
metadata_strategy_override = self.get_destination_configuration('metadata_strategy', None)
except JobMappingException:
metadata_strategy_override = None
if job.tasks:
metadata_strategy_override = "directory"
self.external_output_metadata = get_metadata_compute_strategy(self.app.config, job.id, metadata_strategy_override=metadata_strategy_override, tool_id=job.tool_id)
self.__external_output_metadata = None
self.__has_tasks = bool(job.tasks)
self.__commands_in_new_shell = True
self.__user_system_pwent = None
self.__galaxy_system_pwent = None
self.__working_directory = None
@property
def external_output_metadata(self):
if self.__external_output_metadata is None:
try:
metadata_strategy_override = self.get_destination_configuration('metadata_strategy', None)
except JobMappingException:
metadata_strategy_override = None
if self.__has_tasks:
metadata_strategy_override = "directory"
self.__external_output_metadata = get_metadata_compute_strategy(self.app.config, self.job_id, metadata_strategy_override=metadata_strategy_override, tool_id=self.tool.id)
return self.__external_output_metadata
@property
def _job_dataset_path_rewriter(self):
if self._dataset_path_rewriter is None:
+1 -22
View File
@@ -1158,11 +1158,7 @@ class Job(JobLike, UsesCreateAndUpdateTime, Dictifiable, RepresentById):
''', '''
UPDATE workflow_invocation_step
SET update_time = :update_time
WHERE id in (
SELECT workflow_invocation_step.id
FROM workflow_invocation_step wis
WHERE wis.job_id = :job_id
);
WHERE job_id = :job_id;
''']
sa_session = object_session(self)
params = {
@@ -3909,19 +3905,6 @@ class LibraryDatasetDatasetInfoAssociation(RepresentById):
return True # always allow inheriting, used for replacement
class ValidationError(RepresentById):
def __init__(self, message=None, err_type=None, attributes=None):
self.message = message
self.err_type = err_type
self.attributes = attributes
class DatasetToValidationErrorAssociation:
def __init__(self, dataset, validation_error):
self.dataset = dataset
self.validation_error = validation_error
class ImplicitlyConvertedDatasetAssociation(RepresentById):
def __init__(self, id=None, parent=None, dataset=None, file_type=None, deleted=False, purged=False, metadata_safe=True):
@@ -6170,10 +6153,6 @@ class HistoryTagAssociation(ItemTagAssociation, RepresentById):
pass
class DatasetTagAssociation(ItemTagAssociation, RepresentById):
pass
class HistoryDatasetAssociationTagAssociation(ItemTagAssociation, RepresentById):
pass
-24
View File
@@ -333,14 +333,6 @@ model.ImplicitlyConvertedDatasetAssociation.table = Table(
Column("metadata_safe", Boolean, index=True, default=True),
Column("type", TrimmedString(255)))
model.ValidationError.table = Table(
"validation_error", metadata,
Column("id", Integer, primary_key=True),
Column("dataset_id", Integer, ForeignKey("history_dataset_association.id"), index=True),
Column("message", TrimmedString(255)),
Column("err_type", TrimmedString(64)),
Column("attributes", TEXT))
model.Group.table = Table(
"galaxy_group", metadata,
Column("id", Integer, primary_key=True),
@@ -1342,16 +1334,6 @@ model.HistoryTagAssociation.table = Table(
Column("value", TrimmedString(255), index=True),
Column("user_value", TrimmedString(255), index=True))
model.DatasetTagAssociation.table = Table(
"dataset_tag_association", metadata,
Column("id", Integer, primary_key=True),
Column("dataset_id", Integer, ForeignKey("dataset.id"), index=True),
Column("tag_id", Integer, ForeignKey("tag.id"), index=True),
Column("user_id", Integer, ForeignKey("galaxy_user.id"), index=True),
Column("user_tname", TrimmedString(255), index=True),
Column("value", TrimmedString(255), index=True),
Column("user_value", TrimmedString(255), index=True))
model.HistoryDatasetAssociationTagAssociation.table = Table(
"history_dataset_association_tag_association", metadata,
Column("id", Integer, primary_key=True),
@@ -1720,8 +1702,6 @@ mapper(model.CloudAuthz, model.CloudAuthz.table, properties=dict(
backref='cloudauthz')
))
mapper(model.ValidationError, model.ValidationError.table)
simple_mapping(model.DynamicTool)
simple_mapping(model.HistoryDatasetAssociation,
@@ -1785,9 +1765,6 @@ simple_mapping(model.Dataset,
primaryjoin=(
(model.Dataset.table.c.id == model.LibraryDatasetDatasetAssociation.table.c.dataset_id) &
(model.LibraryDatasetDatasetAssociation.table.c.deleted == false()))),
tags=relation(model.DatasetTagAssociation,
order_by=model.DatasetTagAssociation.table.c.id,
backref='datasets')
)
mapper(model.DatasetHash, model.DatasetHash.table, properties=dict(
@@ -2752,7 +2729,6 @@ def tag_mapping(tag_association_class, backref_name):
tag_mapping(model.HistoryTagAssociation, "tagged_histories")
tag_mapping(model.DatasetTagAssociation, "tagged_datasets")
tag_mapping(model.HistoryDatasetAssociationTagAssociation, "tagged_history_dataset_associations")
tag_mapping(model.LibraryDatasetDatasetAssociationTagAssociation, "tagged_library_dataset_dataset_associations")
tag_mapping(model.PageTagAssociation, "tagged_pages")
@@ -0,0 +1,130 @@
"""
Migration script to create missing indexes. Adding new columns to existing tables via SQLAlchemy does not create the index, even if the column definition includes index=True.
"""
import logging
from sqlalchemy import MetaData
from galaxy.model.migrate.versions.util import (
add_index,
drop_index
)
log = logging.getLogger(__name__)
metadata = MetaData()
indexes = [
[
"ix_galaxy_user_activation_token",
"galaxy_user",
"activation_token"
],
[
"ix_workflow_step_dynamic_tool_id",
"workflow_step",
"dynamic_tool_id"
],
[
"ix_history_dataset_association_version",
"history_dataset_association",
"version"
],
[
"ix_workflow_invocation_scheduler",
"workflow_invocation",
"scheduler"
],
[
"ix_page_slug",
"page",
"slug"
],
[
"ix_workflow_invocation_state",
"workflow_invocation",
"state"
],
[
"ix_history_dataset_collection_association_implicit_collection_jobs_id",
"history_dataset_collection_association",
"implicit_collection_jobs_id"
],
[
"ix_workflow_step_subworkflow_id",
"workflow_step",
"subworkflow_id"
],
[
"ix_dynamic_tool_update_time",
"dynamic_tool",
"update_time"
],
[
"ix_library_dataset_dataset_association_extended_metadata_id",
"library_dataset_dataset_association",
"extended_metadata_id"
],
[
"ix_workflow_invocation_step_implicit_collection_jobs_id",
"workflow_invocation_step",
"implicit_collection_jobs_id"
],
[
"ix_workflow_invocation_step_state",
"workflow_invocation_step",
"state"
],
[
"ix_workflow_invocation_history_id",
"workflow_invocation",
"history_id"
],
[
"ix_workflow_parent_workflow_id",
"workflow",
"parent_workflow_id"
],
[
"ix_metadata_file_uuid",
"metadata_file",
"uuid"
],
[
"ix_history_dataset_collection_association_job_id",
"history_dataset_collection_association",
"job_id"
],
[
"ix_galaxy_user_active",
"galaxy_user",
"active"
],
[
"ix_job_dynamic_tool_id",
"job",
"dynamic_tool_id"
],
[
"ix_history_dataset_association_extended_metadata_id",
"history_dataset_association",
"extended_metadata_id"
]
]
def upgrade(migrate_engine):
print(__doc__)
metadata.bind = migrate_engine
metadata.reflect()
for ix, table, col in indexes:
add_index(ix, table, col, metadata)
def downgrade(migrate_engine):
metadata.bind = migrate_engine
metadata.reflect()
for ix, table, col in indexes:
drop_index(ix, table, col, metadata)
+14 -2
View File
@@ -1,3 +1,4 @@
import hashlib
import logging
from sqlalchemy import (
@@ -47,6 +48,15 @@ def localtimestamp(migrate_engine):
raise Exception('Unable to convert data for unknown database type: %s' % migrate_engine.name)
def truncate_index_name(index_name, engine):
# does what sqlalchemy does, see https://github.com/sqlalchemy/sqlalchemy/blob/8455a11bcc23e97afe666873cd872b0f204848d8/lib/sqlalchemy/sql/compiler.py#L4696
max_index_name_length = engine.dialect.max_index_name_length or engine.dialect.max_identifier_length
if len(index_name) > max_index_name_length:
suffix = hashlib.md5(index_name.encode('utf-8')).hexdigest()[-4:]
index_name = "{trunc}_{suffix}".format(trunc=index_name[0 : max_index_name_length - 8], suffix=suffix)
return index_name
def create_table(table):
try:
table.create()
@@ -139,6 +149,7 @@ def add_index(index_name, table, column_name, metadata=None, **kwds):
if not isinstance(table, Table):
assert metadata is not None
table = Table(table, metadata, autoload=True)
index_name = truncate_index_name(index_name, table.metadata.bind)
if index_name not in [ix.name for ix in table.indexes]:
column = table.c[column_name]
# MySQL cannot index a TEXT/BLOB column without specifying mysql_length
@@ -168,8 +179,9 @@ def drop_index(index, table, column_name=None, metadata=None):
if not isinstance(table, Table):
assert metadata is not None
table = Table(table, metadata, autoload=True)
if index in [ix.name for ix in table.indexes]:
index = Index(index, table.c[column_name])
index_name = truncate_index_name(index, table.metadata.bind)
if index_name in [ix.name for ix in table.indexes]:
index = Index(index_name, table.c[column_name])
else:
log.debug("Index '%s' in table '%s' does not exist.", index, table)
return
+3
View File
@@ -368,6 +368,9 @@ class ToolBox(BaseGalaxyToolBox):
# are now using only GALAXY_URL.
tool_ids = listify(tool_id)
for tool_id in tool_ids:
if tool_id.endswith('/'):
# Some data sources send back redirects ending with `/`, this takes care of that case
tool_id = tool_id[:-1]
if get_loaded_tools_by_lineage:
tools = toolbox.get_loaded_tools_by_lineage(tool_id)
else:
@@ -6,7 +6,11 @@ from paste.httpexceptions import (
HTTPBadRequest,
HTTPForbidden
)
from sqlalchemy import and_
from sqlalchemy import (
and_,
cast,
Integer,
)
from galaxy import (
exceptions,
@@ -82,7 +86,8 @@ class ToolShedRepositoriesController(BaseAPIController):
clause_list.append(self.app.install_model.ToolShedRepository.table.c.uninstalled == util.asbool(kwd.get('uninstalled')))
tool_shed_repository_dicts = []
query = trans.install_model.context.query(self.app.install_model.ToolShedRepository) \
.order_by(self.app.install_model.ToolShedRepository.table.c.name)
.order_by(self.app.install_model.ToolShedRepository.table.c.name) \
.order_by(cast(self.app.install_model.ToolShedRepository.ctx_rev, Integer).desc())
if len(clause_list) > 0:
query = query.filter(and_(*clause_list))
for tool_shed_repository in query.all():
@@ -47,9 +47,6 @@ class ToolRunner(BaseUIController):
# tool id not available, redirect to main page
if tool_id is None:
return trans.response.send_redirect(url_for(controller='root', action='welcome'))
if tool_id.endswith('/'):
# Probably caused by a redirect
tool_id = tool_id[:-1]
tool = self.__get_tool(tool_id)
# tool id is not matching, display an error
if not tool: