mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge branch 'release_20.09' into dev
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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")) { %>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user