Merge remote-tracking branch 'upstream/dev' into require_elimination

This commit is contained in:
Dannon Baker
2017-12-15 10:17:06 -05:00
226 changed files with 3866 additions and 2783 deletions
-94
View File
@@ -1,94 +0,0 @@
client/galaxy/style/source_material/circle.py
contrib/
cron/
doc/parse_gx_xsd.py
doc/patch.py
lib/galaxy/actions/
lib/galaxy/auth/
lib/galaxy/config.py
lib/galaxy/containers/
lib/galaxy/dataset_collections/
lib/galaxy/datatypes/
lib/galaxy/dependencies/
lib/galaxy/eggs/
lib/galaxy/exceptions/
lib/galaxy/external_services/
lib/galaxy/forms/
lib/galaxy/jobs/
lib/galaxy/managers/
lib/galaxy/model/
lib/galaxy/objectstore/
lib/galaxy/openid/
lib/galaxy/quota/
lib/galaxy/sample_tracking/
lib/galaxy/security/
lib/galaxy/tags/
lib/galaxy/tools/
lib/galaxy/tours/
lib/galaxy/util/
lib/galaxy/visualization/
lib/galaxy/web/base/
lib/galaxy/web/buildapp.py
lib/galaxy/web/framework/base.py
lib/galaxy/web/framework/decorators.py
lib/galaxy/web/framework/helpers/grids.py
lib/galaxy/web/framework/__init__.py
lib/galaxy/web/framework/middleware/error.py
lib/galaxy/web/framework/middleware/static.py
lib/galaxy/web/framework/middleware/statsd.py
lib/galaxy/web/framework/webapp.py
lib/galaxy/web/__init__.py
lib/galaxy/webapps/galaxy/api/histories.py
lib/galaxy/webapps/galaxy/api/library_datasets.py
lib/galaxy/webapps/galaxy/api/tours.py
lib/galaxy/webapps/galaxy/api/users.py
lib/galaxy/webapps/galaxy/api/workflows.py
lib/galaxy/webapps/galaxy/buildapp.py
lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
lib/galaxy/webapps/galaxy/controllers/external_services.py
lib/galaxy/webapps/galaxy/controllers/forms.py
lib/galaxy/webapps/galaxy/controllers/history.py
lib/galaxy/webapps/galaxy/controllers/library_common.py
lib/galaxy/webapps/galaxy/controllers/search.py
lib/galaxy/webapps/galaxy/controllers/userskeys.py
lib/galaxy/webapps/reports/config.py
lib/galaxy/webapps/reports/controllers/tools.py
lib/galaxy/webapps/reports/__init__.py
lib/galaxy/webapps/tool_shed/config.py
lib/galaxy/webapps/tool_shed/controllers/groups.py
lib/galaxy/webapps/tool_shed/controllers/user.py
lib/galaxy/webapps/tool_shed/framework/middleware/remoteuser.py
lib/galaxy/webapps/tool_shed/__init__.py
lib/galaxy/webapps/tool_shed/model/__init__.py
lib/galaxy/webapps/tool_shed/search/repo_search.py
lib/galaxy/webapps/tool_shed/util/ratings_util.py
lib/galaxy/webhooks/
lib/galaxy/work/
lib/galaxy/workflow/
lib/galaxy_ext/
lib/log_tempfile.py
lib/mimeparse.py
lib/psyco_full.py
lib/tool_shed/
scripts/api/
scripts/auth/
scripts/bootstrap_history.py
scripts/build_toolbox.py
scripts/check_eggs.py
scripts/check_galaxy.py
scripts/check_python.py
scripts/cleanup_datasets/
scripts/communication/
scripts/data_libraries/build_whoosh_index.py
scripts/db_shell.py
scripts/drmaa_external_runner.py
scripts/metagenomics/
scripts/microbes/BeautifulSoup.py
scripts/microbes/harvest_bacteria.py
scripts/secret_decoder_ring.py
scripts/tool_shed/api/common.py
scripts/tool_shed/build_ts_whoosh_index.py
scripts/tool_shed/deprecate_repositories_without_metadata.py
test/
tool_list.py
tools/
+22 -22
View File
@@ -5,13 +5,13 @@ import GalaxyApp from "galaxy";
import AdminPanel from "./panels/admin-panel";
import FormWrapper from "mvc/form/form-wrapper";
import GridView from "mvc/grid/grid-view";
import Ui from "mvc/ui/ui-misc";
import QueryStringParsing from "utils/query-string-parsing";
import Router from "layout/router";
import Utils from "utils/utils";
import Page from "layout/page";
import Vue from "libs/vue";
import UserAPIKeys from "components/UserAPIKeys.vue";
import UserAPIKeys from "components/admin/UserAPIKeys.vue";
import DataTables from "components/admin/DataTables.vue";
import Vue from "vue";
window.app = function app(options, bootstrapped) {
window.Galaxy = new GalaxyApp.GalaxyApp(options, bootstrapped);
@@ -28,10 +28,11 @@ window.app = function app(options, bootstrapped) {
"(/)admin(/)repositories": "show_repositories",
"(/)admin(/)forms": "show_forms",
"(/)admin(/)form(/)(:form_id)": "show_form",
"(/)admin/api_keys": "show_user_api_keys"
"(/)admin/api_keys": "show_user_api_keys",
"(/)admin/data_tables": "show_data_tables"
},
authenticate: function(args, name) {
authenticate: function() {
return Galaxy.user && Galaxy.user.id && Galaxy.user.get("is_admin");
},
@@ -39,8 +40,7 @@ window.app = function app(options, bootstrapped) {
this.page.display(
new GridView({
url_base: `${Galaxy.root}admin/users_list`,
url_data: Galaxy.params,
dict_format: true
url_data: Galaxy.params
})
);
},
@@ -49,8 +49,7 @@ window.app = function app(options, bootstrapped) {
this.page.display(
new GridView({
url_base: `${Galaxy.root}admin/roles_list`,
url_data: Galaxy.params,
dict_format: true
url_data: Galaxy.params
})
);
},
@@ -59,8 +58,7 @@ window.app = function app(options, bootstrapped) {
this.page.display(
new GridView({
url_base: `${Galaxy.root}admin/groups_list`,
url_data: Galaxy.params,
dict_format: true
url_data: Galaxy.params
})
);
},
@@ -69,8 +67,7 @@ window.app = function app(options, bootstrapped) {
this.page.display(
new GridView({
url_base: `${Galaxy.root}admin_toolshed/browse_repositories`,
url_data: Galaxy.params,
dict_format: true
url_data: Galaxy.params
})
);
},
@@ -79,8 +76,7 @@ window.app = function app(options, bootstrapped) {
this.page.display(
new GridView({
url_base: `${Galaxy.root}admin/tool_versions_list`,
url_data: Galaxy.params,
dict_format: true
url_data: Galaxy.params
})
);
},
@@ -89,24 +85,28 @@ window.app = function app(options, bootstrapped) {
this.page.display(
new GridView({
url_base: `${Galaxy.root}admin/quotas_list`,
url_data: Galaxy.params,
dict_format: true
url_data: Galaxy.params
})
);
},
show_user_api_keys: function() {
var vuemount = document.createElement("div");
this.page.display(vuemount);
new Vue(UserAPIKeys).$mount(vuemount);
var vueMount = document.createElement("div");
this.page.display(vueMount);
new Vue(UserAPIKeys).$mount(vueMount);
},
show_data_tables: function() {
var vueMount = document.createElement("div");
this.page.display(vueMount);
new Vue(DataTables).$mount(vueMount);
},
show_forms: function() {
this.page.display(
new GridView({
url_base: `${Galaxy.root}forms/forms_list`,
url_data: Galaxy.params,
dict_format: true
url_data: Galaxy.params
})
);
},
+12 -5
View File
@@ -22,7 +22,7 @@ import Ui from "mvc/ui/ui-misc";
import DatasetError from "mvc/dataset/dataset-error";
import DatasetEditAttributes from "mvc/dataset/dataset-edit-attributes";
import Citations from "components/Citations.vue";
import Vue from "libs/vue";
import Vue from "vue";
/** define the 'Analyze Data'/analysis/main/home page for Galaxy
* * has a masthead
@@ -47,6 +47,7 @@ window.app = function app(options, bootstrapped) {
"(/)tours(/)(:tour_id)": "show_tours",
"(/)user(/)": "show_user",
"(/)user(/)(:form_id)": "show_user_form",
"(/)openids(/)list": "show_openids",
"(/)pages(/)create(/)": "show_pages_create",
"(/)pages(/)edit(/)": "show_pages_edit",
"(/)pages(/)(:action_id)": "show_pages",
@@ -115,8 +116,7 @@ window.app = function app(options, bootstrapped) {
show_workflows_published: function() {
this.page.display(
new GridView({
url_base: `${Galaxy.root}workflow/list_published`,
dict_format: true
url_base: `${Galaxy.root}workflow/list_published`
})
);
},
@@ -157,11 +157,18 @@ window.app = function app(options, bootstrapped) {
);
},
show_openids: function() {
this.page.display(
new GridView({
url_base: `${Galaxy.root}user/openids_list`
})
);
},
show_datasets: function() {
this.page.display(
new GridView({
url_base: `${Galaxy.root}dataset/list`,
dict_format: true
url_base: `${Galaxy.root}dataset/list`
})
);
},
@@ -22,7 +22,8 @@ var AdminPanel = Backbone.View.extend({
},
{
title: _l("Data tables"),
url: "admin/view_tool_data_tables"
url: "admin/data_tables",
target: "__use_router__"
},
{
title: _l("Display applications"),
@@ -0,0 +1,20 @@
<template>
<div v-if="message" :class="[status + 'message']">{{ message }}</div>
</template>
<script>
export default {
name: 'Message',
props: {
message: {
type: String,
required: true
},
status: {
type: String,
default: 'done'
}
}
}
</script>
@@ -0,0 +1,43 @@
<template>
<div class="toolForm">
<div class="toolFormTitle">
<slot name="title">{{ title }}</slot>
</div>
<div class="toolFormBody">
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<slot name="columns">
<th v-for="column in columns" bgcolor="#D8D8D8">{{ column.text }}</th>
</slot>
<slot name="rows">
<template v-for="(row, index) in rows">
<tr :class="[index % 2 === 0 ? 'tr' : 'odd_row']">
<td v-for="column in columns">{{ row[column.dataIndex] }}</td>
</tr>
</template>
</slot>
</table>
<div v-if="isLoaded !== undefined && !isLoaded" :style="{ textAlign: 'center', padding: '7px 0' }">
Loading...
</div>
</div>
</div>
</template>
<script>
export default {
props: {
isLoaded: {
type: Boolean
},
title: {
type: String
},
columns: {
type: Array
},
rows: {
type: Array
}
}
}
</script>
@@ -0,0 +1,47 @@
<template>
<table class="tabletip">
<thead>
<tr>
<th :colspan="dataManagerColumns.length" style="font-size:120%">
Data Manager: {{ dataManagerTableName }}
<a class="icon-btn" href="javascript:void(0)"
@click="handleReloadButtonClick"
:title="`Reload ${dataManagerTableName} tool data table`"
><span class="fa fa-refresh"></span></a>
</th>
</tr>
<tr>
<th v-for="column in dataManagerColumns">{{ column }}</th>
</tr>
</thead>
<tbody v-for="item in dataManagerItems">
<tr>
<td v-for="column in dataManagerColumns">{{ item[column] }}</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
props: {
dataManagerTableName: {
type: String,
required: true
},
dataManagerColumns: {
type: Array,
required: true
},
dataManagerItems: {
type: Array,
required: true
}
},
methods: {
handleReloadButtonClick(event) {
this.$emit('reloaddatamanager', this.dataManagerTableName);
}
}
}
</script>
@@ -0,0 +1,117 @@
<template>
<div>
<message :message="message" :status="status"></message>
<component
:is="currentView"
v-if="status !== 'error'"
v-bind="currentProps"
@changeview="showDataManager"
@reloaddatamanager="reloadDataManager">
</component>
</div>
</template>
<script>
import axios from 'axios';
import Message from '../Message.vue';
import DataTablesGrid from './DataTablesGrid.vue';
import DataManagerGrid from './DataManagerGrid.vue';
export default {
data() {
return {
currentView: 'data-tables-grid',
isLoaded: false,
dataTables: [],
dataManagerTableName: '',
dataManagerColumns: [],
dataManagerItems: [],
message: '',
status: ''
}
},
components: {
message: Message,
'data-tables-grid': DataTablesGrid,
'data-manager-grid': DataManagerGrid
},
computed: {
currentProps() {
let props;
if (this.currentView === 'data-tables-grid') {
props = {
isLoaded: this.isLoaded,
rows: this.dataTables
};
} else {
props = {
dataManagerTableName: this.dataManagerTableName,
dataManagerColumns: this.dataManagerColumns,
dataManagerItems: this.dataManagerItems
};
}
return props;
}
},
methods: {
showDataManager(dataManagerTableName) {
axios.get(`${Galaxy.root}data_manager/tool_data_table_items`, {
params: {
table_name: dataManagerTableName
}
})
.then((response) => {
this.message = response.data.message;
this.status = response.data.status;
if (response.data.status !== 'error' && response.data.status !== 'warning') {
this.dataManagerTableName = dataManagerTableName;
this.dataManagerColumns = response.data.data.columns;
this.dataManagerItems = response.data.data.items;
this.currentView = 'data-manager-grid';
}
})
.catch((error) => {
console.error(error);
});
},
reloadDataManager(dataManagerTableName) {
axios.get(`${Galaxy.root}data_manager/reload_tool_data_table`, {
params: {
table_name: dataManagerTableName
}
})
.then((response) => {
this.message = response.data.message;
this.status = response.data.status;
if (response.data.status !== 'error' && response.data.status !== 'warning') {
this.dataManagerItems = response.data.data.items;
}
})
.catch((error) => {
console.error(error);
});
}
},
created() {
axios.get(`${Galaxy.root}admin/data_tables_list`)
.then((response) => {
this.isLoaded = true;
this.dataTables = response.data.data;
this.message = response.data.message;
this.status = response.data.status;
})
.catch((error) => {
console.error(error);
});
}
}
</script>
@@ -0,0 +1,53 @@
<template>
<base-grid :is-loaded="isLoaded" :columns="columns">
<template slot="title">
Current data table registry contains {{ rows.length }} data tables
</template>
<template slot="rows" v-for="(row, index) in rows">
<tr :class="[index % 2 === 0 ? 'tr' : 'odd_row']">
<td><a href="javascript:void(0)" @click="handleTableNameClick">{{ row.name }}</a></td>
<td>{{ row.filename }}</td>
<td>{{ row.tool_data_path }}</td>
<td>{{ row.errors }}</td>
</tr>
</template>
</base-grid>
</template>
<script>
import BaseGrid from './BaseGrid.vue';
export default {
props: {
isLoaded: {
type: Boolean,
required: true
},
rows: {
type: Array,
required: true
}
},
data() {
return {
columns: [
{ text: 'Name', dataIndex: 'name' },
{ text: 'Filename', dataIndex: 'filename' },
{ text: 'Tool data path', dataIndex: 'tool_data_path' },
{ text: 'Errors', dataIndex: 'errors' }
]
}
},
components: {
'base-grid': BaseGrid
},
methods: {
handleTableNameClick(event) {
this.$emit('changeview', event.target.text);
}
}
}
</script>
@@ -6,9 +6,7 @@
export function append_notebook(url) {
clear_main_area();
$("#main").append(
`<iframe frameBorder="0" seamless="seamless" style="width: 100%; height: 100%; overflow:hidden;" scrolling="no" src="${
url
}"></iframe>`
`<iframe frameBorder="0" seamless="seamless" style="width: 100%; height: 100%; overflow:hidden;" scrolling="no" src="${url}"></iframe>`
);
}
@@ -21,9 +19,7 @@ export function clear_main_area() {
export function display_spinner() {
$("#main").append(
`<img id="spinner" src="${
galaxy_root
}static/style/largespinner.gif" style="position:absolute;margin:auto;top:0;left:0;right:0;bottom:0;">`
`<img id="spinner" src="${galaxy_root}static/style/largespinner.gif" style="position:absolute;margin:auto;top:0;left:0;right:0;bottom:0;">`
);
}
+1 -7
View File
@@ -156,11 +156,7 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
}
show_modal(
"Create Link",
`<div><div><label id='link_attribute_label'>URL <span style='float: right; font-size: 90%'><a href='#' id='set_link_id'>Create in-page anchor</a></span></label><br><input type='text' class='wym_href' value='${
curURL
}' size='40' /></div><div><label>Title</label><br><input type='text' class='wym_title' value='${
curTitle
}' size='40' /></div><div>`,
`<div><div><label id='link_attribute_label'>URL <span style='float: right; font-size: 90%'><a href='#' id='set_link_id'>Create in-page anchor</a></span></label><br><input type='text' class='wym_href' value='${curURL}' size='40' /></div><div><label>Title</label><br><input type='text' class='wym_title' value='${curTitle}' size='40' /></div><div>`,
{
"Make link": function() {
// Get URL, name/title.
@@ -334,7 +330,6 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
var grid = new GridView({
url_base: item_info.list_ajax_url,
dict_format: true,
embedded: true
});
Galaxy.modal.show({
@@ -428,7 +423,6 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
var grid = new GridView({
url_base: item_info.list_ajax_url,
dict_format: true,
embedded: true
});
Galaxy.modal.show({
+3 -3
View File
@@ -154,9 +154,9 @@ function show_in_overlay(options) {
closeButton: true,
title: "&nbsp;",
body: $(
`<div style='margin: -5px;'><iframe style='margin: 0; padding: 0;' src='${options.url}' width='${
width
}' height='${height}' scrolling='${scroll}' frameborder='0'></iframe></div>`
`<div style='margin: -5px;'><iframe style='margin: 0; padding: 0;' src='${
options.url
}' width='${width}' height='${height}' scrolling='${scroll}' frameborder='0'></iframe></div>`
)
});
modal.show({ backdrop: true });
@@ -0,0 +1,114 @@
// legacy grid model, used by reports and toolshed
export default Backbone.Model.extend({
defaults: {
url_base: "",
async: false,
async_ops: [],
categorical_filters: [],
filters: {},
sort_key: null,
show_item_checkboxes: false,
advanced_search: false,
cur_page: 1,
num_pages: 1,
operation: undefined,
item_ids: undefined
},
/**
* Return true if operation can be done asynchronously.
*/
can_async_op: function(op) {
return _.indexOf(this.attributes.async_ops, op) !== -1;
},
/**
* Add filtering criterion.
*/
add_filter: function(key, value, append) {
// Update URL arg with new condition.
if (append) {
// Update or append value.
var cur_val = this.attributes.filters[key];
var new_val;
if (cur_val === null || cur_val === undefined) {
new_val = value;
} else if (typeof cur_val == "string") {
if (cur_val == "All" || cur_val == value) {
new_val = value;
} else {
// Replace string with array.
var values = [];
values[0] = cur_val;
values[1] = value;
new_val = values;
}
} else {
// Current value is an array.
new_val = cur_val;
if (new_val.indexOf(value) === -1) {
new_val.push(value);
}
}
this.attributes.filters[key] = new_val;
} else {
// Replace value.
this.attributes.filters[key] = value;
}
},
/**
* Remove filtering criterion.
*/
remove_filter: function(key, condition) {
var cur_val = this.attributes.filters[key];
if (cur_val === null || cur_val === undefined) {
return false;
}
if (typeof cur_val === "string") {
// overwrite/remove condition.
this.attributes.filters[key] = "";
} else {
// filter contains an array of conditions.
var condition_index = _.indexOf(cur_val, condition);
if (condition_index !== -1) {
cur_val[condition_index] = "";
}
}
},
/**
* Returns URL data for obtaining a new grid.
*/
get_url_data: function() {
var url_data = {
async: this.attributes.async,
sort: this.attributes.sort_key,
page: this.attributes.cur_page,
show_item_checkboxes: this.attributes.show_item_checkboxes,
advanced_search: this.attributes.advanced_search
};
// Add operation, item_ids only if they have values.
if (this.attributes.operation) {
url_data.operation = this.attributes.operation;
}
if (this.attributes.item_ids) {
url_data.id = this.attributes.item_ids;
}
// Add filter arguments to data, placing "f-" in front of all arguments.
var self = this;
_.each(_.pairs(self.attributes.filters), k => {
url_data[`f-${k[0]}`] = k[1];
});
return url_data;
},
// Return URL for obtaining a new grid
get_url: function(args) {
return `${this.get("url_base")}?${$.param(this.get_url_data())}&${$.param(args)}`;
}
});
@@ -0,0 +1,547 @@
// dependencies
import Utils from "utils/utils";
// grid view templates
export default {
// template
grid: function(options) {
var tmpl = "";
if (options.embedded) {
tmpl = this.grid_header(options) + this.grid_table(options);
} else {
tmpl = `<div class="loading-elt-overlay"></div><table><tr><td width="75%">${this.grid_header(
options
)}</td><td></td><td></td></tr><tr><td width="100%" id="grid-message" valign="top"></td><td></td><td></td></tr></table>${this.grid_table(
options
)}`;
}
// add info text
if (options.info_text) {
tmpl += `<br><div class="toolParamHelp" style="clear: both;">${options.info_text}</div>`;
}
// return
return tmpl;
},
// template
grid_table: function() {
return `
<form method="post" onsubmit="return false;">
<table id="grid-table" class="grid">
<thead id="grid-table-header"></thead>
<tbody id="grid-table-body"></tbody>
<tfoot id="grid-table-footer"></tfoot>
</table>
</form>`;
},
// template
grid_header: function(options) {
var tmpl = '<div class="grid-header">';
if (!options.embedded) {
tmpl += `<h2>${options.title}</h2>`;
}
if (options.global_actions) {
tmpl += '<ul class="manage-table-actions">';
var show_popup = options.global_actions.length >= 3;
if (show_popup) {
tmpl +=
'<li><a class="action-button" id="popup-global-actions" class="menubutton">Actions</a></li>' +
'<div popupmenu="popup-global-actions">';
}
for (let action of options.global_actions) {
tmpl += `<li><a class="action-button use-target" target="${action.target}" href="${
action.url_args
}" onclick="return false;" >${action.label}</a></li>`;
}
if (show_popup) {
tmpl += "</div>";
}
tmpl += "</ul>";
}
if (options.insert) {
tmpl += options.insert;
}
// add grid filters
tmpl += this.grid_filters(options);
tmpl += "</div>";
// return template
return tmpl;
},
// template
header: function(options) {
// start
var tmpl = "<tr>";
// add checkbox
if (options.show_item_checkboxes) {
tmpl += "<th>";
if (options.items.length > 0) {
tmpl +=
'<input type="checkbox" id="check_all" name=select_all_checkbox value="true">' +
'<input type="hidden" name=select_all_checkbox value="true">';
}
tmpl += "</th>";
}
// create header elements
for (let column of options.columns) {
if (column.visible) {
tmpl += `<th id="${column.key}-header">`;
if (column.href) {
tmpl += `<a href="${column.href}" class="sort-link" sort_key="${column.key}">${column.label}</a>`;
} else {
tmpl += column.label;
}
tmpl += `<span class="sort-arrow">${column.extra}</span></th>`;
}
}
// finalize
tmpl += "</tr>";
// return template
return tmpl;
},
// template
body: function(options) {
// initialize
var tmpl = "";
var items_length = options.items.length;
// empty grid?
if (items_length === 0) {
// No results.
tmpl += '<tr><td colspan="100"><em>No Items</em></td></tr>';
}
// create rows
for (let item of options.items) {
// Tag current
tmpl += "<tr ";
if (options.current_item_id == item.id) {
tmpl += 'class="current"';
}
tmpl += ">";
// Item selection column
if (options.show_item_checkboxes) {
tmpl += `<td style="width: 1.5em;"><input type="checkbox" name="id" value="${item.encode_id}" id="${
item.encode_id
}" class="grid-row-select-checkbox" /></td>`;
}
// Data columns
for (let column of options.columns) {
if (column.visible) {
// Nowrap
var nowrap = "";
if (column.nowrap) {
nowrap = 'style="white-space:nowrap;"';
}
// get column settings
var column_settings = item.column_config[column.label];
// load attributes
var link = column_settings.link;
var value = column_settings.value;
var target = column_settings.target;
// unescape value
if (jQuery.type(value) === "string") {
value = value.replace(/\/\//g, "/");
}
// Attach popup menu?
var id = "";
var cls = "";
if (column.attach_popup) {
id = `grid-${item.encode_id}-popup`;
cls = "menubutton";
if (link !== "") {
cls += " split";
}
cls += " popup";
}
// Check for row wrapping
tmpl += `<td ${nowrap}>`;
// Link
if (link) {
if (options.operations.length !== 0) {
tmpl += `<div id="${id}" class="${cls}" style="float: left;">`;
}
tmpl += `<a class="menubutton-label use-target" target="${target}" href="${link}" onclick="return false;">${value}</a>`;
if (options.operations.length !== 0) {
tmpl += "</div>";
}
} else {
tmpl += `<div id="${id}" class="${cls}"><label id="${column.label_id_prefix}${
item.encode_id
}" for="${item.encode_id}">${value || ""}</label></div>`;
}
tmpl += "</td>";
}
}
tmpl += "</tr>";
}
return tmpl;
},
// template
footer: function(options) {
// create template string
var tmpl = "";
// paging
if (options.use_paging && options.num_pages > 1) {
// get configuration
var num_page_links = options.num_page_links;
var cur_page_num = options.cur_page_num;
var num_pages = options.num_pages;
// First pass on min page.
var page_link_range = num_page_links / 2;
var min_page = cur_page_num - page_link_range;
var min_offset = 0;
if (min_page <= 0) {
// Min page is too low.
min_page = 1;
min_offset = page_link_range - (cur_page_num - min_page);
}
// Set max page.
var max_range = page_link_range + min_offset;
var max_page = cur_page_num + max_range;
var max_offset;
if (max_page <= num_pages) {
// Max page is fine.
max_offset = 0;
} else {
// Max page is too high.
max_page = num_pages;
// +1 to account for the +1 in the loop below.
max_offset = max_range - (max_page + 1 - cur_page_num);
}
// Second and final pass on min page to add any unused
// offset from max to min.
if (max_offset !== 0) {
min_page -= max_offset;
if (min_page < 1) {
min_page = 1;
}
}
// template header
tmpl += '<tr id="page-links-row">';
if (options.show_item_checkboxes) {
tmpl += "<td></td>";
}
tmpl += '<td colspan="100">' + '<span id="page-link-container">' + "Page:";
if (min_page > 1) {
tmpl +=
'<span class="page-link" id="page-link-1"><a href="javascript:void(0);" page_num="1" onclick="return false;">1</a></span> ...';
}
// create page urls
for (var page_index = min_page; page_index < max_page + 1; page_index++) {
if (page_index == options.cur_page_num) {
tmpl += `<span class="page-link inactive-link" id="page-link-${page_index}">${page_index}</span>`;
} else {
tmpl += `<span class="page-link" id="page-link-${page_index}"><a href="javascript:void(0);" onclick="return false;" page_num="${page_index}">${page_index}</a></span>`;
}
}
// show last page
if (max_page < num_pages) {
tmpl += `...<span class="page-link" id="page-link-${num_pages}"><a href="javascript:void(0);" onclick="return false;" page_num="${num_pages}">${num_pages}</a></span>`;
}
tmpl += "</span>";
// Show all link
tmpl += `
<span class="page-link" id="show-all-link-span"> | <a href="javascript:void(0);" onclick="return false;" page_num="all">Show All</a></span>
</td>
</tr>`;
}
// Grid operations for multiple items.
if (options.show_item_checkboxes) {
// start template
tmpl += `
<tr>
<input type="hidden" id="operation" name="operation" value="">
<td></td>
<td colspan="100">
For <span class="grid-selected-count"></span> selected items:
`;
// configure buttons for operations
for (let operation of options.operations) {
if (operation.allow_multiple) {
tmpl += `<input type="button" value="${
operation.label
}" class="operation-button action-button">&nbsp;`;
}
}
// finalize template
tmpl += "</td>" + "</tr>";
}
// count global operations
var found_global = false;
for (let operation of options.operations) {
if (operation.global_operation) {
found_global = true;
break;
}
}
// add global operations
if (found_global) {
tmpl += "<tr>" + '<td colspan="100">';
for (let operation of options.operations) {
if (operation.global_operation) {
tmpl += `<a class="action-button" href="${operation.global_operation}">${operation.label}</a>`;
}
}
tmpl += "</td>" + "</tr>";
}
// add legend
if (options.legend) {
tmpl += `<tr><td colspan="100">${options.legend}</td></tr>`;
}
// return
return tmpl;
},
// template
message: function(options) {
var status = options.status;
if (["success", "ok"].indexOf(status) != -1) {
status = "done";
}
return `<p><div class="${status}message transient-message">${_.escape(
options.message
)}</div><div style="clear: both"></div></p>`;
},
// template
grid_filters: function(options) {
// get filters
var default_filter_dict = options.default_filter_dict;
var filters = options.filters;
// show advanced search if flag set or if there are filters for advanced search fields
var advanced_search_display = "none";
if (options.advanced_search) {
advanced_search_display = "block";
}
// identify columns with advanced filtering
var show_advanced_search_link = false;
for (let column of options.columns) {
if (column.filterable == "advanced") {
var column_key = column.key;
var f_key = filters[column_key];
var d_key = default_filter_dict[column_key];
if (f_key && d_key && f_key != d_key) {
advanced_search_display = "block";
}
show_advanced_search_link = true;
}
}
// hide standard search if advanced is shown
var standard_search_display = "block";
if (advanced_search_display == "block") {
standard_search_display = "none";
}
//
// standard search
//
var tmpl = `<div id="standard-search" style="display: ${standard_search_display};"><table><tr><td style="padding: 0;"><table>`;
// add standard filters
for (let column of options.columns) {
if (column.filterable == "standard") {
tmpl += this.grid_column_filter(options, column);
}
}
// finalize standard search
tmpl += "</table>" + "</td>" + "</tr>" + "<tr>" + "<td>";
// show advanced search link in standard display
if (show_advanced_search_link) {
tmpl += '<a href="" class="advanced-search-toggle">Advanced Search</a>';
}
// finalize standard search display
tmpl += "</td>" + "</tr>" + "</table>" + "</div>";
//
// advanced search
//
tmpl += `<div id="advanced-search" style="display: ${advanced_search_display}; margin-top: 5px; border: 1px solid #ccc;"><table><tr><td style="text-align: left" colspan="100"><a href="" class="advanced-search-toggle">Close Advanced Search</a></td></tr>`;
// add advanced filters
for (let column of options.columns) {
if (column.filterable == "advanced") {
tmpl += this.grid_column_filter(options, column);
}
}
// finalize advanced search template
tmpl += "</table>" + "</div>";
// return template
return tmpl;
},
// template
grid_column_filter: function(options, column) {
// collect parameters
var filters = options.filters;
var column_label = column.label;
var column_key = column.key;
if (column.filterable == "advanced") {
column_label = column_label.toLowerCase();
}
// start
var tmpl = "<tr>";
if (column.filterable == "advanced") {
tmpl += `<td align="left" style="padding-left: 10px">${column_label}:</td>`;
}
tmpl += '<td style="padding-bottom: 1px;">';
if (column.is_text) {
tmpl += `<form class="text-filter-form" column_key="${column_key}" action="${options.url}" method="get" >`;
// Carry forward filtering criteria with hidden inputs.
for (let column of options.columns) {
var filter_value = filters[column.key];
if (filter_value) {
if (filter_value != "All") {
if (column.is_text) {
filter_value = JSON.stringify(filter_value);
}
tmpl += `<input type="hidden" id="${column.key}" name="f-${
column.key
}" value="${filter_value}"/>`;
}
}
}
// Print current filtering criteria and links to delete.
tmpl += `<span id="${column_key}-filtering-criteria">`;
// add filters
var column_filter = filters[column_key];
if (column_filter) {
// identify type
var type = jQuery.type(column_filter);
// single filter value
if (type == "string") {
if (column_filter != "All") {
// append template
tmpl += this.filter_element(column_key, column_filter);
}
}
// multiple filter values
if (type == "array") {
for (let i in column_filter) {
// copy filters and remove entry
var params = column_filter;
params = params.slice(i);
// append template
tmpl += this.filter_element(column_key, column_filter[i]);
}
}
}
// close span
tmpl += "</span>";
// Set value, size of search input field. Minimum size is 20 characters.
var value = "";
var size = 20;
if (column.filterable == "standard") {
value = column.label.toLowerCase();
if (value.length < 20) {
size = value.length;
}
// +4 to account for space after placeholder
size = size + 4;
}
// print input field for column
tmpl += `
<span class="search-box">
<input class="search-box-input" id="input-${column_key}-filter" name="f-${column_key}" type="text" placeholder="${value}" size="${size}"/>
<button type="submit" style="background: transparent; border: none; padding: 4px; margin: 0px;">
<i class="fa fa-search"></i>
</button>
</span>
</form>`;
} else {
// filter criteria
tmpl += `<span id="${column_key}-filtering-criteria">`;
// add category filters
var seperator = false;
for (var cf_label in options.categorical_filters[column_key]) {
// get category filter
var cf = options.categorical_filters[column_key][cf_label];
// each filter will have only a single argument, so get that single argument
var cf_key = "";
var cf_arg = "";
for (var key in cf) {
cf_key = key;
cf_arg = cf[key];
}
// add seperator
if (seperator) {
tmpl += " | ";
}
seperator = true;
// add category
var filter = filters[column_key];
if (filter && cf[column_key] && filter == cf_arg) {
tmpl += `<span class="categorical-filter ${column_key}-filter current-filter">${cf_label}</span>`;
} else {
tmpl += `<span class="categorical-filter ${column_key}-filter"><a href="javascript:void(0);" filter_key="${cf_key}" filter_val="${cf_arg}">${cf_label}</a></span>`;
}
}
tmpl += "</span>";
}
tmpl += "</td>" + "</tr>";
// return template
return tmpl;
},
// template for filter items
filter_element: function(filter_key, filter_value) {
filter_value = Utils.sanitize(filter_value);
return `<span class="text-filter-val">${filter_value}<a href="javascript:void(0);" filter_key="${filter_key}" filter_val="${filter_value}"><i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/></a></span>`;
}
};
@@ -0,0 +1,677 @@
// This is necessary so that, when nested arrays are used in ajax/post/get methods, square brackets ('[]') are
// not appended to the identifier of a nested array.
jQuery.ajaxSettings.traditional = true;
// dependencies
import Utils from "utils/utils";
import GridModel from "legacy/grid/grid-model";
import Templates from "legacy/grid/grid-template";
import PopupMenu from "mvc/ui/popup-menu";
// grid view
export default Backbone.View.extend({
// model
grid: null,
// Initialize
initialize: function(grid_config) {
this.grid = new GridModel();
this.dict_format = grid_config.dict_format;
this.title = grid_config.title;
var self = this;
window.add_tag_to_grid_filter = (tag_name, tag_value) => {
// Put tag name and value together.
var tag = tag_name + (tag_value !== undefined && tag_value !== "" ? `:${tag_value}` : "");
var advanced_search = $("#advanced-search").is(":visible");
if (!advanced_search) {
$("#standard-search").slideToggle("fast");
$("#advanced-search").slideToggle("fast");
}
self.add_filter_condition("tags", tag);
};
// set element
if (this.dict_format) {
this.setElement("<div/>");
if (grid_config.url_base && !grid_config.items) {
var url_data = grid_config.url_data || {};
_.each(grid_config.filters, (v, k) => {
url_data[`f-${k}`] = v;
});
$.ajax({
url: `${grid_config.url_base}?${$.param(url_data)}`,
success: function(response) {
response.embedded = grid_config.embedded;
response.filters = grid_config.filters || {};
self.init_grid(response);
}
});
} else {
this.init_grid(grid_config);
}
} else {
this.setElement("#grid-container");
this.init_grid(grid_config);
}
// fix padding
if (grid_config.use_panels) {
$("#center").css({
padding: "10px",
overflow: "auto"
});
}
},
// refresh frames
handle_refresh: function(refresh_frames) {
if (refresh_frames) {
if ($.inArray("history", refresh_frames) > -1) {
if (window.top.Galaxy && window.top.Galaxy.currHistoryPanel) {
window.top.Galaxy.currHistoryPanel.loadCurrentHistory();
}
}
}
},
// Initialize
init_grid: function(grid_config) {
this.grid.set(grid_config);
// get options
var options = this.grid.attributes;
if (this.allow_title_display && options.title) {
Utils.setWindowTitle(options.title);
}
// handle refresh requests
this.handle_refresh(options.refresh_frames);
// strip protocol and domain
var url = this.grid.get("url_base");
url = url.replace(/^.*\/\/[^\/]+/, "");
this.grid.set("url_base", url);
// append main template
this.$el.html(Templates.grid(options));
// update div contents
this.$el.find("#grid-table-header").html(Templates.header(options));
this.$el.find("#grid-table-body").html(Templates.body(options));
this.$el.find("#grid-table-footer").html(Templates.footer(options));
// update message
if (options.message) {
this.$el.find("#grid-message").html(Templates.message(options));
var self = this;
if (options.use_hide_message) {
setTimeout(() => {
self.$el.find("#grid-message").html("");
}, 5000);
}
}
// configure elements
this.init_grid_elements();
this.init_grid_controls();
// attach global event handler
// TODO: redundant (the onload/standard page handlers do this) - but needed because these are constructed after page ready
if (window.init_refresh_on_change) {
window.init_refresh_on_change();
}
},
// Initialize grid controls
init_grid_controls: function() {
// link
var self = this;
// Initialize grid operation button.
this.$el.find(".operation-button").each(function() {
$(this).off();
$(this).click(function() {
self.submit_operation(this);
return false;
});
});
// Initialize text filters to select text on click and use normal font when user is typing.
this.$el.find("input[type=text]").each(function() {
$(this).off();
$(this)
.click(function() {
$(this).select();
})
.keyup(function() {
$(this).css("font-style", "normal");
});
});
// Initialize sort links.
this.$el.find(".sort-link").each(function() {
$(this).off();
$(this).click(function() {
self.set_sort_condition($(this).attr("sort_key"));
return false;
});
});
// Initialize text filters.
this.$el.find(".text-filter-form").each(function() {
$(this).off();
$(this).submit(function() {
var column_key = $(this).attr("column_key");
var text_input_obj = $(`#input-${column_key}-filter`);
var text_input = text_input_obj.val();
text_input_obj.val("");
self.add_filter_condition(column_key, text_input);
return false;
});
});
// Initialize categorical filters.
this.$el.find(".text-filter-val > a").each(function() {
$(this).off();
$(this).click(function() {
// Remove visible element.
$(this)
.parent()
.remove();
// Remove filter condition.
self.remove_filter_condition($(this).attr("filter_key"), $(this).attr("filter_val"));
// Return
return false;
});
});
// Initialize categorical filters.
this.$el.find(".categorical-filter > a").each(function() {
$(this).off();
$(this).click(function() {
self.set_categorical_filter($(this).attr("filter_key"), $(this).attr("filter_val"));
return false;
});
});
// Initialize standard, advanced search toggles.
this.$el.find(".advanced-search-toggle").each(function() {
$(this).off();
$(this).click(() => {
self.$el.find("#standard-search").slideToggle("fast");
self.$el.find("#advanced-search").slideToggle("fast");
return false;
});
});
// Add event to check all box
this.$el.find("#check_all").off();
this.$el.find("#check_all").on("click", () => {
self.check_all_items();
});
},
// Initialize grid elements.
init_grid_elements: function() {
// Initialize grid selection checkboxes.
this.$el.find(".grid").each(function() {
var checkboxes = $(this).find("input.grid-row-select-checkbox");
var check_count = $(this).find("span.grid-selected-count");
var update_checked = () => {
check_count.text($(checkboxes).filter(":checked").length);
};
$(checkboxes).each(function() {
$(this).change(update_checked);
});
update_checked();
});
// Initialize ratings.
if (this.$el.find(".community_rating_star").length !== 0) this.$el.find(".community_rating_star").rating({});
// get options
var options = this.grid.attributes;
var self = this;
//
// add page click events
//
this.$el.find(".page-link > a").each(function() {
$(this).click(function() {
self.set_page($(this).attr("page_num"));
return false;
});
});
//
// add inbound/outbound events
//
this.$el.find(".use-target").each(function() {
$(this).click(function() {
self.execute({
href: $(this).attr("href"),
target: $(this).attr("target")
});
return false;
});
});
// empty grid?
var items_length = options.items.length;
if (items_length === 0) {
return;
}
// add operation popup menus
_.each(options.items, (item, index) => {
var button = self.$(`#grid-${item.encode_id}-popup`).off();
var popup = new PopupMenu(button);
_.each(options.operations, operation => {
self._add_operation(popup, operation, item);
});
});
},
/** Add an operation to the items menu */
_add_operation: function(popup, operation, item) {
var self = this;
var settings = item.operation_config[operation.label];
if (settings.allowed && operation.allow_popup) {
popup.addItem({
html: operation.label,
href: settings.url_args,
target: settings.target,
confirmation_text: operation.confirm,
func: function(e) {
e.preventDefault();
var label = $(e.target).html();
if (operation.onclick) {
operation.onclick(item.encode_id);
} else {
self.execute(this.findItemByHtml(label));
}
}
});
}
},
// Add a condition to the grid filter; this adds the condition and refreshes the grid.
add_filter_condition: function(name, value) {
// Do nothing is value is empty.
if (value === "") {
return false;
}
// Add condition to grid.
this.grid.add_filter(name, value, true);
// Add button that displays filter and provides a button to delete it.
var t = $(Templates.filter_element(name, value));
var self = this;
t.click(function() {
// Remove visible element.
$(this).remove();
// Remove filter condition.
self.remove_filter_condition(name, value);
});
// append to container
var container = this.$el.find(`#${name}-filtering-criteria`);
container.append(t);
// execute
this.go_page_one();
this.execute();
},
// Remove a condition to the grid filter; this adds the condition and refreshes the grid.
remove_filter_condition: function(name, value) {
// Remove filter condition.
this.grid.remove_filter(name, value);
// Execute
this.go_page_one();
this.execute();
},
// Set sort condition for grid.
set_sort_condition: function(col_key) {
// Set new sort condition. New sort is col_key if sorting new column; if reversing sort on
// currently sorted column, sort is reversed.
var cur_sort = this.grid.get("sort_key");
var new_sort = col_key;
if (cur_sort.indexOf(col_key) !== -1) {
// Reverse sort.
if (cur_sort.substring(0, 1) !== "-") {
new_sort = `-${col_key}`;
}
}
// Remove sort arrows elements.
this.$el.find(".sort-arrow").remove();
// Add sort arrow element to new sort column.
var sort_arrow = new_sort.substring(0, 1) == "-" ? "&uarr;" : "&darr;";
var t = $(`<span>${sort_arrow}</span>`).addClass("sort-arrow");
// Add to header
this.$el.find(`#${col_key}-header`).append(t);
// Update grid.
this.grid.set("sort_key", new_sort);
this.go_page_one();
this.execute();
},
// Set new value for categorical filter.
set_categorical_filter: function(name, new_value) {
// Update filter hyperlinks to reflect new filter value.
var category_filter = this.grid.get("categorical_filters")[name];
var cur_value = this.grid.get("filters")[name];
var self = this;
this.$el.find(`.${name}-filter`).each(function() {
var text = $.trim($(this).text());
var filter = category_filter[text];
var filter_value = filter[name];
if (filter_value == new_value) {
// Remove filter link since grid will be using this filter. It is assumed that
// this element has a single child, a hyperlink/anchor with text.
$(this).empty();
$(this).addClass("current-filter");
$(this).append(text);
} else if (filter_value == cur_value) {
// Add hyperlink for this filter since grid will no longer be using this filter. It is assumed that
// this element has a single child, a hyperlink/anchor.
$(this).empty();
var t = $(`<a href="#">${text}</a>`);
t.click(() => {
self.set_categorical_filter(name, filter_value);
});
$(this).removeClass("current-filter");
$(this).append(t);
}
});
// Update grid.
this.grid.add_filter(name, new_value);
this.go_page_one();
this.execute();
},
// Set page to view.
set_page: function(new_page) {
// Update page hyperlink to reflect new page.
var self = this;
this.$el.find(".page-link").each(function() {
var id = $(this).attr("id");
var // Id has form 'page-link-<page_num>
page_num = parseInt(id.split("-")[2], 10);
var cur_page = self.grid.get("cur_page");
var text;
if (page_num === new_page) {
// Remove link to page since grid will be on this page. It is assumed that
// this element has a single child, a hyperlink/anchor with text.
text = $(this)
.children()
.text();
$(this).empty();
$(this).addClass("inactive-link");
$(this).text(text);
} else if (page_num === cur_page) {
// Add hyperlink to this page since grid will no longer be on this page. It is assumed that
// this element has a single child, a hyperlink/anchor.
text = $(this).text();
$(this).empty();
$(this).removeClass("inactive-link");
var t = $(`<a href="#">${text}</a>`);
t.click(() => {
self.set_page(page_num);
});
$(this).append(t);
}
});
if (new_page === "all") {
this.grid.set("cur_page", new_page);
} else {
this.grid.set("cur_page", parseInt(new_page, 10));
}
this.execute();
},
// confirmation/submission of operation request
submit_operation: function(operation_button, confirmation_text) {
// identify operation
var operation_name = $(operation_button).val();
// verify any item is selected
var number_of_checked_ids = this.$el.find('input[name="id"]:checked').length;
if (number_of_checked_ids < 1) {
return false;
}
// Check to see if there's grid confirmation text for this operation
var operation = _.findWhere(this.grid.attributes.operations, {
label: operation_name
});
if (operation && !confirmation_text) {
confirmation_text = operation.confirm || "";
}
// collect ids
var item_ids = [];
this.$el.find("input[name=id]:checked").each(function() {
item_ids.push($(this).val());
});
// execute operation
var options = {
operation: operation_name,
id: item_ids,
confirmation_text: confirmation_text
};
if (operation.target == "top" || operation.target == "center") {
options = _.extend(options, {
href: operation.href,
target: operation.target
});
}
this.execute(options);
return true;
},
check_all_items: function() {
var check = this.$(".grid-row-select-checkbox");
var state = this.$("#check_all").prop("checked");
_.each(check, c => {
$(c).prop("checked", state);
});
this.init_grid_elements();
},
// Go back to page one; this is useful when a filter is applied.
go_page_one: function() {
// Need to go back to page 1 if not showing all.
var cur_page = this.grid.get("cur_page");
if (cur_page !== null && cur_page !== undefined && cur_page !== "all") {
this.grid.set("cur_page", 1);
}
},
//
// execute operations and hyperlink requests
//
execute: function(options) {
// get url
var id = null;
var href = null;
var operation = null;
var confirmation_text = null;
var target = null;
// check for options
if (options) {
// get options
href = options.href;
operation = options.operation;
id = options.id;
confirmation_text = options.confirmation_text;
target = options.target;
// check if input contains the operation tag
if (href !== undefined && href.indexOf("operation=") != -1) {
// Get operation, id in hyperlink's href.
var href_parts = href.split("?");
if (href_parts.length > 1) {
var href_parms_str = href_parts[1];
var href_parms = href_parms_str.split("&");
for (var index = 0; index < href_parms.length; index++) {
if (href_parms[index].indexOf("operation") != -1) {
// Found operation parm; get operation value.
operation = href_parms[index].split("=")[1];
operation = operation.replace(/\+/g, " ");
} else if (href_parms[index].indexOf("id") != -1) {
// Found id parm; get id value.
id = href_parms[index].split("=")[1];
}
}
}
}
}
// check for operation details
if (operation && id) {
// show confirmation box
if (
confirmation_text &&
confirmation_text !== "" &&
confirmation_text != "None" &&
confirmation_text != "null"
)
if (!confirm(confirmation_text)) return false;
// use small characters for operation?!
operation = operation.toLowerCase();
// Update grid.
this.grid.set({
operation: operation,
item_ids: id
});
// Do operation. If operation cannot be performed asynchronously, redirect to location.
if (target == "top") {
window.top.location = `${href}?${$.param(this.grid.get_url_data())}`;
} else if (target == "center") {
$("#galaxy_main").attr("src", `${href}?${$.param(this.grid.get_url_data())}`);
} else if (this.grid.can_async_op(operation) || this.dict_format) {
this.update_grid();
} else {
this.go_to(target, href);
}
// done
return false;
}
// refresh grid
if (href) {
this.go_to(target, href);
return false;
}
// refresh grid
if (this.grid.get("async") || this.dict_format) {
this.update_grid();
} else {
this.go_to(target, href);
}
// done
return false;
},
// go to url
go_to: function(target, href) {
// get aysnc status
var async = this.grid.get("async");
this.grid.set("async", false);
// get slide status
var advanced_search = this.$el.find("#advanced-search").is(":visible");
this.grid.set("advanced_search", advanced_search);
// get default url
if (!href) {
href = `${this.grid.get("url_base")}?${$.param(this.grid.get_url_data())}`;
}
// clear grid of transient request attributes.
this.grid.set({
operation: undefined,
item_ids: undefined,
async: async
});
switch (target) {
case "center":
$("#galaxy_main").attr("src", href);
break;
case "top":
window.top.location = href;
break;
default:
window.location = href;
}
},
// Update grid.
update_grid: function() {
// If there's an operation, do POST; otherwise, do GET.
var method = this.grid.get("operation") ? "POST" : "GET";
// Show overlay to indicate loading and prevent user actions.
this.$el.find(".loading-elt-overlay").show();
var self = this;
$.ajax({
type: method,
url: self.grid.get("url_base"),
data: self.grid.get_url_data(),
error: function() {
alert("Grid refresh failed");
},
success: function(response_text) {
// backup
var embedded = self.grid.get("embedded");
var insert = self.grid.get("insert");
var advanced_search = self.$el.find("#advanced-search").is(":visible");
// request new configuration
var json = self.dict_format ? response_text : $.parseJSON(response_text);
// update
json.embedded = embedded;
json.insert = insert;
json.advanced_search = advanced_search;
// Initialize new grid config
self.init_grid(json);
// Hide loading overlay.
self.$el.find(".loading-elt-overlay").hide();
},
complete: function() {
// Clear grid of transient request attributes.
self.grid.set({
operation: undefined,
item_ids: undefined
});
}
});
}
});
@@ -17,13 +17,6 @@ export default Backbone.Model.extend({
item_ids: undefined
},
/**
* Return true if operation can be done asynchronously.
*/
can_async_op: function(op) {
return _.indexOf(this.attributes.async_ops, op) !== -1;
},
/**
* Add filtering criterion.
*/
@@ -10,7 +10,6 @@ var View = Backbone.View.extend({
$.ajax({
url: `${Galaxy.root + this.item}/${this.model.get("action_id")}?${$.param(Galaxy.params)}`,
success: function(response) {
response["dict_format"] = true;
self.model.set(response);
self.render();
}
+11 -38
View File
@@ -112,14 +112,12 @@ export default {
body: function(options) {
// initialize
var tmpl = "";
var num_rows_rendered = 0;
var items_length = options.items.length;
// empty grid?
if (items_length === 0) {
// No results.
tmpl += '<tr><td colspan="100"><em>No Items</em></td></tr>';
num_rows_rendered = 1;
}
// create rows
@@ -180,9 +178,7 @@ export default {
if (options.operations.length !== 0) {
tmpl += `<div id="${id}" class="${cls}" style="float: left;">`;
}
tmpl += `<a class="menubutton-label use-target" target="${target}" href="${
link
}" onclick="return false;">${value}</a>`;
tmpl += `<a class="menubutton-label use-target" target="${target}" href="${link}" onclick="return false;">${value}</a>`;
if (options.operations.length !== 0) {
tmpl += "</div>";
}
@@ -195,7 +191,6 @@ export default {
}
}
tmpl += "</tr>";
num_rows_rendered++;
}
return tmpl;
},
@@ -262,21 +257,13 @@ export default {
if (page_index == options.cur_page_num) {
tmpl += `<span class="page-link inactive-link" id="page-link-${page_index}">${page_index}</span>`;
} else {
tmpl += `<span class="page-link" id="page-link-${
page_index
}"><a href="javascript:void(0);" onclick="return false;" page_num="${page_index}">${
page_index
}</a></span>`;
tmpl += `<span class="page-link" id="page-link-${page_index}"><a href="javascript:void(0);" onclick="return false;" page_num="${page_index}">${page_index}</a></span>`;
}
}
// show last page
if (max_page < num_pages) {
tmpl += `...<span class="page-link" id="page-link-${
num_pages
}"><a href="javascript:void(0);" onclick="return false;" page_num="${num_pages}">${
num_pages
}</a></span>`;
tmpl += `...<span class="page-link" id="page-link-${num_pages}"><a href="javascript:void(0);" onclick="return false;" page_num="${num_pages}">${num_pages}</a></span>`;
}
tmpl += "</span>";
@@ -386,9 +373,7 @@ export default {
//
// standard search
//
var tmpl = `<div id="standard-search" style="display: ${
standard_search_display
};"><table><tr><td style="padding: 0;"><table>`;
var tmpl = `<div id="standard-search" style="display: ${standard_search_display};"><table><tr><td style="padding: 0;"><table>`;
// add standard filters
for (let column of options.columns) {
@@ -411,9 +396,7 @@ export default {
//
// advanced search
//
tmpl += `<div id="advanced-search" style="display: ${
advanced_search_display
}; margin-top: 5px; border: 1px solid #ccc;"><table><tr><td style="text-align: left" colspan="100"><a href="" class="advanced-search-toggle">Close Advanced Search</a></td></tr>`;
tmpl += `<div id="advanced-search" style="display: ${advanced_search_display}; margin-top: 5px; border: 1px solid #ccc;"><table><tr><td style="text-align: left" colspan="100"><a href="" class="advanced-search-toggle">Close Advanced Search</a></td></tr>`;
// add advanced filters
for (let column of options.columns) {
@@ -456,9 +439,9 @@ export default {
if (column.is_text) {
filter_value = JSON.stringify(filter_value);
}
tmpl += `<input type="hidden" id="${column.key}" name="f-${column.key}" value="${
filter_value
}"/>`;
tmpl += `<input type="hidden" id="${column.key}" name="f-${
column.key
}" value="${filter_value}"/>`;
}
}
}
@@ -510,9 +493,7 @@ export default {
// print input field for column
tmpl += `
<span class="search-box">
<input class="search-box-input" id="input-${column_key}-filter" name="f-${
column_key
}" type="text" placeholder="${value}" size="${size}"/>
<input class="search-box-input" id="input-${column_key}-filter" name="f-${column_key}" type="text" placeholder="${value}" size="${size}"/>
<button type="submit" style="background: transparent; border: none; padding: 4px; margin: 0px;">
<i class="fa fa-search"></i>
</button>
@@ -547,11 +528,7 @@ export default {
if (filter && cf[column_key] && filter == cf_arg) {
tmpl += `<span class="categorical-filter ${column_key}-filter current-filter">${cf_label}</span>`;
} else {
tmpl += `<span class="categorical-filter ${
column_key
}-filter"><a href="javascript:void(0);" filter_key="${cf_key}" filter_val="${cf_arg}">${
cf_label
}</a></span>`;
tmpl += `<span class="categorical-filter ${column_key}-filter"><a href="javascript:void(0);" filter_key="${cf_key}" filter_val="${cf_arg}">${cf_label}</a></span>`;
}
}
tmpl += "</span>";
@@ -565,10 +542,6 @@ export default {
// template for filter items
filter_element: function(filter_key, filter_value) {
filter_value = Utils.sanitize(filter_value);
return `<span class="text-filter-val">${filter_value}<a href="javascript:void(0);" filter_key="${
filter_key
}" filter_val="${
filter_value
}"><i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/></a></span>`;
return `<span class="text-filter-val">${filter_value}<a href="javascript:void(0);" filter_key="${filter_key}" filter_val="${filter_value}"><i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/></a></span>`;
}
};
+18 -35
View File
@@ -15,7 +15,6 @@ export default Backbone.View.extend({
// Initialize
initialize: function(grid_config) {
this.grid = new GridModel();
this.dict_format = grid_config.dict_format;
this.title = grid_config.title;
var self = this;
window.add_tag_to_grid_filter = (tag_name, tag_value) => {
@@ -30,26 +29,21 @@ export default Backbone.View.extend({
};
// set element
if (this.dict_format) {
this.setElement("<div/>");
if (grid_config.url_base && !grid_config.items) {
var url_data = grid_config.url_data || {};
_.each(grid_config.filters, (v, k) => {
url_data[`f-${k}`] = v;
});
$.ajax({
url: `${grid_config.url_base}?${$.param(url_data)}`,
success: function(response) {
response.embedded = grid_config.embedded;
response.filters = grid_config.filters || {};
self.init_grid(response);
}
});
} else {
this.init_grid(grid_config);
}
this.setElement("<div/>");
if (grid_config.url_base && !grid_config.items) {
var url_data = grid_config.url_data || {};
_.each(grid_config.filters, (v, k) => {
url_data[`f-${k}`] = v;
});
$.ajax({
url: `${grid_config.url_base}?${$.param(url_data)}`,
success: function(response) {
response.embedded = grid_config.embedded;
response.filters = grid_config.filters || {};
self.init_grid(response);
}
});
} else {
this.setElement("#grid-container");
this.init_grid(grid_config);
}
@@ -570,10 +564,8 @@ export default Backbone.View.extend({
window.top.location = `${href}?${$.param(this.grid.get_url_data())}`;
} else if (target == "center") {
$("#galaxy_main").attr("src", `${href}?${$.param(this.grid.get_url_data())}`);
} else if (this.grid.can_async_op(operation) || this.dict_format) {
this.update_grid();
} else {
this.go_to(target, href);
this.update_grid();
}
// done
@@ -587,11 +579,7 @@ export default Backbone.View.extend({
}
// refresh grid
if (this.grid.get("async") || this.dict_format) {
this.update_grid();
} else {
this.go_to(target, href);
}
this.update_grid();
// done
return false;
@@ -599,10 +587,6 @@ export default Backbone.View.extend({
// go to url
go_to: function(target, href) {
// get aysnc status
var async = this.grid.get("async");
this.grid.set("async", false);
// get slide status
var advanced_search = this.$el.find("#advanced-search").is(":visible");
this.grid.set("advanced_search", advanced_search);
@@ -615,8 +599,7 @@ export default Backbone.View.extend({
// clear grid of transient request attributes.
this.grid.set({
operation: undefined,
item_ids: undefined,
async: async
item_ids: undefined
});
switch (target) {
case "center":
@@ -652,7 +635,7 @@ export default Backbone.View.extend({
var advanced_search = self.$el.find("#advanced-search").is(":visible");
// request new configuration
var json = self.dict_format ? response_text : $.parseJSON(response_text);
var json = response_text;
// update
json.embedded = embedded;
+1 -3
View File
@@ -75,9 +75,7 @@ var HDCAListItemView = _super.extend(
} else if (!jobStatesSummary || !jobStatesSummary.hasDetails()) {
return `
<div class="progress state-progress">
<span class="note">Loading job data for ${
collectionTypeDescription
}.<span class="blinking">..</span></span>
<span class="note">Loading job data for ${collectionTypeDescription}.<span class="blinking">..</span></span>
<div class="progress-bar info" style="width:100%">
</div>`;
} else {
@@ -60,7 +60,6 @@ var View = Backbone.View.extend({
Utils.get({
url: `${Galaxy.root}history/${options.action_id}?${$.param(Galaxy.params)}`,
success: function(response) {
response.dict_format = true;
self.model.set(response);
self.render();
}
@@ -910,9 +910,9 @@ var FolderToolbarView = Backbone.View.extend({
}
var promise = $.when(
$.post(
`${Galaxy.root}api/libraries/datasets?encoded_folder_id=${that.id}&source=${options.source}&path=${
popped_item
}&file_type=${options.file_type}&link_data=${options.link_data}&space_to_tab=${
`${Galaxy.root}api/libraries/datasets?encoded_folder_id=${that.id}&source=${
options.source
}&path=${popped_item}&file_type=${options.file_type}&link_data=${options.link_data}&space_to_tab=${
options.space_to_tab
}&to_posix_lines=${options.to_posix_lines}&dbkey=${options.dbkey}&tag_using_filenames=${
options.tag_using_filenames
@@ -959,13 +959,13 @@ var FolderToolbarView = Backbone.View.extend({
}
var promise = $.when(
$.post(
`${Galaxy.root}api/libraries/datasets?encoded_folder_id=${that.id}&source=${options.source}&path=${
popped_item
}&preserve_dirs=${options.preserve_dirs}&link_data=${options.link_data}&to_posix_lines=${
options.to_posix_lines
}&space_to_tab=${options.space_to_tab}&file_type=${options.file_type}&dbkey=${
options.dbkey
}&tag_using_filenames=${options.tag_using_filenames}`
`${Galaxy.root}api/libraries/datasets?encoded_folder_id=${that.id}&source=${
options.source
}&path=${popped_item}&preserve_dirs=${options.preserve_dirs}&link_data=${
options.link_data
}&to_posix_lines=${options.to_posix_lines}&space_to_tab=${options.space_to_tab}&file_type=${
options.file_type
}&dbkey=${options.dbkey}&tag_using_filenames=${options.tag_using_filenames}`
)
);
promise
@@ -124,9 +124,9 @@ var LibraryView = Backbone.View.extend({
placeholder: "Click to select a role",
container: self.$el.find(`#${id}`),
ajax: {
url: `${Galaxy.root}api/libraries/${self.id}/permissions?scope=available&is_library_access=${
is_library_access
}`,
url: `${Galaxy.root}api/libraries/${
self.id
}/permissions?scope=available&is_library_access=${is_library_access}`,
dataType: "json",
quietMillis: 100,
data: function(term, page) {
@@ -7,7 +7,7 @@ import Deferred from "utils/deferred";
import Ui from "mvc/ui/ui-misc";
import FormBase from "mvc/form/form-view";
import Citations from "components/Citations.vue";
import Vue from "libs/vue";
import Vue from "vue";
export default FormBase.extend({
initialize: function(options) {
var self = this;
+1 -3
View File
@@ -269,9 +269,7 @@ var View = Backbone.View.extend({
} else if (batch_n !== n) {
this.form.highlight(
input_id,
`Please make sure that you select the same number of inputs for all batch mode fields. This field contains <b>${
n
}</b> selection(s) while a previous field contains <b>${batch_n}</b>.`
`Please make sure that you select the same number of inputs for all batch mode fields. This field contains <b>${n}</b> selection(s) while a previous field contains <b>${batch_n}</b>.`
);
return false;
}
@@ -48,7 +48,7 @@ var ToolShedRepositoryView = Backbone.View.extend({
this.options.tps_select_options = this.templatePanelSelectOptions;
this.options.tool_dependencies = models.get("tool_dependencies");
this.options.shed_tool_conf = this.templateShedToolConf({
stc_html: models.get("shed_conf")
shed_tool_confs: models.get("shed_conf")
});
this.options.panel_section_dict = models.get("panel_section_dict");
this.options.api_url = `${Galaxy.root}api/tool_shed_repositories/install?async=True`;
@@ -474,7 +474,11 @@ var ToolShedRepositoryView = Backbone.View.extend({
'<div class="toolFormTitle">Shed tool configuration file:</div>',
'<div class="toolFormBody">',
'<div class="form-row">',
"<%= stc_html %>",
'<select name="shed_tool_conf">',
"<% _.each(shed_tool_confs.options, function(conf) { %>",
'<option value="<%= conf.value %>"><%= conf.label %></option>',
"<% }); %>",
"</select>",
'<div class="toolParamHelp" style="clear: both;">Select the file whose <b>tool_path</b> setting you want used for installing repositories.</div>',
"</div>",
"</div>"
@@ -62,7 +62,7 @@ var Model = Backbone.Model.extend({
description: _l("Associate OpenIDs with your account."),
icon: "fa-openid",
onclick: function() {
window.location.href = `${Galaxy.root}user/openid_manage?cntrller=user&use_panels=True`;
window.location.href = `${Galaxy.root}openids/list`;
}
},
custom_builds: {
@@ -35,9 +35,7 @@ $.extend(Workflow.prototype, {
}
if (!this.canLabelOutputWith(toLabel)) {
Toastr.warning(
`Workflow contains duplicate workflow output labels ${
toLabel
}. This must be fixed before it can be saved.`
`Workflow contains duplicate workflow output labels ${toLabel}. This must be fixed before it can be saved.`
);
}
if (toLabel) {
@@ -277,9 +277,7 @@ export default Backbone.View.extend({
if (upgrade_message) {
window.show_modal(
"Issues loading this workflow",
`Please review the following issues, possibly resulting from tool upgrades or changes.<p><ul>${
upgrade_message
}</ul></p>`,
`Please review the following issues, possibly resulting from tool upgrades or changes.<p><ul>${upgrade_message}</ul></p>`,
{ Continue: hide_modal }
);
} else {
@@ -560,9 +558,7 @@ export default Backbone.View.extend({
if (upgrade_message) {
window.show_modal(
"Subworkflow embedded with changes",
`Problems were encountered loading this workflow (possibly a result of tool upgrades). Please review the following parameters and then save.<ul>${
upgrade_message
}</ul>`,
`Problems were encountered loading this workflow (possibly a result of tool upgrades). Please review the following parameters and then save.<ul>${upgrade_message}</ul>`,
{ Continue: hide_modal }
);
} else {
@@ -103,9 +103,7 @@ var WorkflowItemView = Backbone.View.extend({
} value="${wfId}">`;
var trHtml = `<td><div class="dropdown"><button class="menubutton" type="button" data-toggle="dropdown">${_.escape(
this.model.get("name")
)}<span class="caret"></span></button>${this._templateActions()}</div></td><td><span><div class="${
wfId
} tags-display"></div></td><td>${
)}<span class="caret"></span></button>${this._templateActions()}</div></td><td><span><div class="${wfId} tags-display"></div></td><td>${
this.model.get("owner") === Galaxy.user.attributes.username ? "You" : this.model.get("owner")
}</span></td><td>${this.model.get("number_of_steps")}</td><td>${
this.model.get("published") ? "Yes" : "No"
@@ -323,9 +321,7 @@ var WorkflowListView = Backbone.View.extend({
"<th>Published</th>" +
"<th>Show in tools panel</th>" +
"</tr></thead>";
return `${
tableHtml
}<tbody class="workflow-search "><div class="hidden_description_layer"><p>Drop workflow files here to import</p></tbody></table></div>`;
return `${tableHtml}<tbody class="workflow-search "><div class="hidden_description_layer"><p>Drop workflow files here to import</p></tbody></table></div>`;
},
/** Main template */
-1
View File
@@ -459,7 +459,6 @@ var TracksterView = Backbone.View.extend({
view_in_saved: function(dataset_params) {
var tracks_grid = new GridView({
url_base: `${Galaxy.root}visualization/list_tracks`,
dict_format: true,
embedded: true
});
Galaxy.modal.show({
@@ -46,14 +46,12 @@ var select_datasets = (filters, success_fn) => {
var history_grid = new GridView({
url_base: `${Galaxy.root}visualization/list_history_datasets`,
filters: filters,
dict_format: true,
embedded: true
});
// library dataset selection tab
var library_grid = new GridView({
url_base: `${Galaxy.root}visualization/list_library_datasets`,
dict_format: true,
embedded: true
});
+1 -2
View File
@@ -34,8 +34,7 @@ var paths = {
'jquery-mousewheel': [ 'jquery.mousewheel.js', 'jquery/jquery.mousewheel.js' ],
'raven-js': ['dist/raven.js', 'raven.js'],
'requirejs': [ 'require.js', 'require.js' ],
'underscore': [ 'underscore.js', 'underscore.js' ],
'vue': [ 'dist/vue.js', 'vue.js' ]
'underscore': [ 'underscore.js', 'underscore.js' ]
},
libs: ['galaxy/scripts/libs/**/*.js']
};
+7 -7
View File
@@ -28,7 +28,7 @@
"raven-js": "^3.17.0",
"requirejs": "2",
"underscore": "^1.8.3",
"vue": "^2.5.3"
"vue": "^2.5.9"
},
"scripts": {
"watch": "gulp stage-libs && concurrently \"yarn run webpack-watch\" \"yarn run gulp watch\" \"yarn run style-watch\"",
@@ -55,11 +55,11 @@
"devDependencies": {
"babel-core": "^6.24.0",
"babel-loader": "^7.1.1",
"babel-preset-env": "^1.3.2",
"concurrently": "^3.4.0",
"babel-preset-env": "^1.6.1",
"concurrently": "^3.5.1",
"css-loader": "^0.28.7",
"del": "^3.0.0",
"expose-loader": "^0.7.3",
"expose-loader": "^0.7.4",
"grunt": "^0.4.5",
"grunt-bower-install-simple": "^1.1.0",
"grunt-cli": "^0.1.13",
@@ -87,11 +87,11 @@
"karma-webpack": "^2.0.6",
"phantomjs-polyfill": "0.0.2",
"phantomjs-prebuilt": "^2.1.7",
"prettier": "^1.7.4",
"prettier": "^1.9.1",
"qunitjs": "^2.4.1",
"sinon": "^4.1.2",
"vue-loader": "^13.5.0",
"vue-template-compiler": "^2.5.3",
"webpack": "^3.6.0"
"vue-template-compiler": "^2.5.9",
"webpack": "^3.10.0"
}
}
+1 -1
View File
@@ -25,7 +25,7 @@ var commonLibs = [
"libs/farbtastic",
"libs/bootstrap",
"libs/bootstrap-tour",
"libs/vue",
"vue",
// mvc
"libs/underscore",
"libs/backbone",
+23 -19
View File
@@ -887,9 +887,9 @@ babel-plugin-transform-strict-mode@^6.24.1:
babel-runtime "^6.22.0"
babel-types "^6.24.1"
babel-preset-env@^1.3.2:
version "1.6.0"
resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.6.0.tgz#2de1c782a780a0a5d605d199c957596da43c44e4"
babel-preset-env@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.6.1.tgz#a18b564cc9b9afdf4aae57ae3c1b0d99188e6f48"
dependencies:
babel-plugin-check-es2015-constants "^6.22.0"
babel-plugin-syntax-trailing-function-commas "^6.22.0"
@@ -1659,9 +1659,9 @@ concat-stream@~1.4.6:
readable-stream "~1.1.9"
typedarray "~0.0.5"
concurrently@^3.4.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-3.5.0.tgz#8cf1b7707a6916a78a4ff5b77bb04dec54b379b2"
concurrently@^3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-3.5.1.tgz#ee8b60018bbe86b02df13e5249453c6ececd2521"
dependencies:
chalk "0.5.1"
commander "2.6.0"
@@ -2616,9 +2616,9 @@ expand-tilde@^2.0.2:
dependencies:
homedir-polyfill "^1.0.1"
expose-loader@^0.7.3:
version "0.7.3"
resolved "https://registry.yarnpkg.com/expose-loader/-/expose-loader-0.7.3.tgz#35fbd3659789e4faa81f59de8b7e9fc39e466d51"
expose-loader@^0.7.4:
version "0.7.4"
resolved "https://registry.yarnpkg.com/expose-loader/-/expose-loader-0.7.4.tgz#9bcdd3878b5da9107930b55a03f65afe90b3314a"
extend@^3.0.0, extend@~3.0.0, extend@~3.0.1:
version "3.0.1"
@@ -5894,10 +5894,14 @@ preserve@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
prettier@^1.7.0, prettier@^1.7.4:
prettier@^1.7.0:
version "1.8.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.8.2.tgz#bff83e7fd573933c607875e5ba3abbdffb96aeb8"
prettier@^1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.9.1.tgz#41638a0d47c1efbd1b7d5a742aaa5548eab86d70"
pretty-bytes@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-1.0.4.tgz#0a22e8210609ad35542f8c8d5d2159aff0751c84"
@@ -7495,9 +7499,9 @@ vue-style-loader@^3.0.0:
hash-sum "^1.0.2"
loader-utils "^1.0.2"
vue-template-compiler@^2.5.3:
version "2.5.3"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.3.tgz#ab631b0694e211a6aaf0d800102b37836aae36a4"
vue-template-compiler@^2.5.9:
version "2.5.9"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.9.tgz#7fabc73c8d3d12d32340cd86c5fc33e00e86d686"
dependencies:
de-indent "^1.0.2"
he "^1.1.0"
@@ -7506,9 +7510,9 @@ vue-template-es2015-compiler@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18"
vue@^2.5.3:
version "2.5.3"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.3.tgz#e1a3b1f49b6e93e574ce040b95cbc873912fecc1"
vue@^2.5.9:
version "2.5.9"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.9.tgz#b2380cd040915dca69881dafd121d760952e65f7"
walk-sync@0.3.1:
version "0.3.1"
@@ -7577,9 +7581,9 @@ webpack@^1.14.0:
watchpack "^0.2.1"
webpack-core "~0.6.9"
webpack@^3.6.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.7.0.tgz#9081d0b1ba4950e34ddc54195721eba4bf205e83"
webpack@^3.10.0:
version "3.10.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.10.0.tgz#5291b875078cf2abf42bdd23afe3f8f96c17d725"
dependencies:
acorn "^5.0.0"
acorn-dynamic-import "^2.0.0"
+10
View File
@@ -52,6 +52,16 @@
</object_store>
-->
<!-- Sample Cloud Object Store
<object_store type="cloud">
<auth access_key="..." secret_key="..." />
<bucket name="..." use_reduced_redundancy="False" />
<cache path="database/object_store_cache" size="1000" />
<extra_dir type="job_work" path="database/job_working_directory_s3"/>
<extra_dir type="temp" path="database/tmp_s3"/>
</object_store>
-->
</backends>
</object_store>
-1
View File
@@ -1,7 +1,6 @@
<?xml version="1.0"?>
<openid>
<provider file="genomespace.xml" />
<provider file="yahoo.xml" />
<provider file="aol.xml" />
<provider file="launchpad.xml" />
</openid>
@@ -174,7 +174,7 @@ class TourGenerator(object):
elif input.type == 'select':
params = []
if name in test_inputs:
for option in self._tool.inputs[name].static_options:
for option in input.static_options:
for test_option in self._test.inputs[name]:
if test_option == option[1]:
params.append(option[0])
-3
View File
@@ -371,9 +371,6 @@ class Configuration(object):
# Beta containers interface used by GIEs
self.enable_beta_containers_interface = string_as_bool(kwargs.get('enable_beta_containers_interface', 'False'))
# Deprecated API for sample tracking
self.enable_legacy_sample_tracking_api = string_as_bool(kwargs.get('enable_legacy_sample_tracking_api', 'False'))
# Certain modules such as the pause module will automatically cause
# workflows to be scheduled in job handlers the way all workflows will
# be someday - the following two properties can also be used to force this
@@ -46,7 +46,7 @@ SWARM_MANAGER_CONF_DEFAULTS = {
'service_wait_count_limit': 0,
'service_wait_time_limit': 5,
'slots_min_limit': 0,
'slots_max_limit': sys.maxint,
'slots_max_limit': sys.maxsize,
'slots_min_spare': 0,
'node_idle_limit': 120,
'limits': [],
@@ -137,7 +137,7 @@ class SwarmManager(object):
state = node_state['state']
if not nodes:
nodes = self._docker_interface.nodes()
node = (filter(lambda x: x.name == name, nodes) + [None])[0]
node = ([x for x in nodes if x.name == name] + [None])[0]
if not node:
if elapsed > self._conf.spawn_wait_time:
log.warning("spawning node '%s' not found in `docker node ls` and spawn_wait_time exceeded! %d seconds have elapsed", name, elapsed)
+2 -2
View File
@@ -449,7 +449,7 @@ class ImzML(Binary):
def generate_primary_file(self, dataset=None):
rval = ['<html><head><title>imzML Composite Dataset </title></head><p/>']
rval.append('<div>This composite dataset is composed of the following files:<p/><ul>')
for composite_name, composite_file in self.get_composite_files(dataset=dataset).iteritems():
for composite_name, composite_file in self.get_composite_files(dataset=dataset).items():
fn = composite_name
opt_text = ''
if composite_file.get('description'):
@@ -493,7 +493,7 @@ class Analyze75(Binary):
def generate_primary_file(self, dataset=None):
rval = ['<html><head><title>Analyze75 Composite Dataset.</title></head><p/>']
rval.append('<div>This composite dataset is composed of the following files:<p/><ul>')
for composite_name, composite_file in self.get_composite_files(dataset=dataset).iteritems():
for composite_name, composite_file in self.get_composite_files(dataset=dataset).items():
fn = composite_name
opt_text = ''
if composite_file.optional:
+3
View File
@@ -112,6 +112,9 @@ class ConditionalDependencies(object):
def check_azure_storage(self):
return 'azure_blob' in self.object_stores
def check_cloudbridge(self):
return 'cloud' in self.object_stores
def check_kamaki(self):
return 'pithos' in self.object_stores
@@ -13,6 +13,7 @@ graphitesend
azure-storage==0.32.0
# PyRods not in PyPI
python-ldap==2.4.44
cloudbridge==0.3.3
# Chronos client
chronos-python==0.38.0
@@ -35,7 +35,7 @@ SQLAlchemy==1.0.15 --hash:sha256=7cbac295f87d2af82aac08346b52f72d0b0c00712290495
--hash:sha256=f819742f33ae543ca2c48bc43e35bd75372f7c3e379e4881f9040549d3df94b6 \
--hash:sha256=fce29753f720f8a6920185bd5a4d1670fa6b6936819475197b2bce7644243766 \
--hash:sha256=aa8843df6869f6c999400ae32de5480fba1b7711fe535c168a14225ffd1d3de4
mercurial==3.7.3 --hash:sha256=49f596820f005ac6f94266b89a0dbd815c0ee0aacd3aa86545dc39fb349ea4bf \
mercurial==3.7.3; python_version < '3.0' --hash:sha256=49f596820f005ac6f94266b89a0dbd815c0ee0aacd3aa86545dc39fb349ea4bf \
--hash:sha256=75f5b6b708bec2d6e0e68ec8912725a5dcac42df00b122fa73a8c4bd21495039 \
--hash:sha256=8c07cb3404d26641d8829099bd1047582b76def49eca067ba6bea9a73d261775 \
--hash:sha256=6a05bf22101b79b5f56421f7a1c6d301000f759f0114827f37cc9f847c1ab352 \
@@ -7,7 +7,7 @@ MarkupSafe==1.0
PyYAML==3.12
SQLAlchemy==1.0.15
sqlalchemy-utils==0.32.19
mercurial==3.7.3
mercurial==3.7.3; python_version < '3.0'
pycrypto==2.6.1
uWSGI==2.0.15
# Flexible BAM index naming is new to core pysam
@@ -72,7 +72,7 @@ psutil==4.1.0
pulsar-galaxy-lib==0.8.0
# sqlalchemy-migrate and dependencies
sqlalchemy-migrate==0.10.0
sqlalchemy-migrate==0.11.0
decorator==4.0.2
Tempita==0.5.3dev
sqlparse==0.1.16
+1 -1
View File
@@ -5,7 +5,7 @@ bx-python
MarkupSafe
PyYAML
SQLAlchemy
mercurial
mercurial; python_version < '3.0'
pycrypto
# Flexible BAM index naming is new to main pysam
pysam>=0.13
+4 -4
View File
@@ -5,11 +5,11 @@ import datetime
import logging
import os
import time
from Queue import (
from six.moves.queue import (
Empty,
Queue
)
from sqlalchemy.sql.expression import (
and_,
func,
@@ -343,7 +343,7 @@ class JobHandlerQueue(Monitors, object):
if not self.track_jobs_in_database:
self.waiting_jobs = new_waiting_jobs
# Remove cached wrappers for any jobs that are no longer being tracked
for id in self.job_wrappers.keys():
for id in list(self.job_wrappers.keys()):
if id not in new_waiting_jobs:
del self.job_wrappers[id]
# Flush, if we updated the state
@@ -896,7 +896,7 @@ class DefaultJobDispatcher(object):
job_wrapper.fail(DEFAULT_JOB_PUT_FAILURE_MESSAGE)
def shutdown(self):
for runner in self.job_runners.itervalues():
for runner in self.job_runners.values():
try:
runner.shutdown()
except Exception:
+2 -2
View File
@@ -232,7 +232,7 @@ class ChronosJobRunner(AsynchronousJobRunner):
def parse_destination_params(self, params):
parsed_params = {}
for k, spec in self.DESTINATION_PARAMS_SPEC.iteritems():
for k, spec in self.DESTINATION_PARAMS_SPEC.items():
value = params.get(k, spec.get('default'))
map_to = spec.get('map_name')
mapper = spec.get('map')
@@ -264,7 +264,7 @@ class ChronosJobRunner(AsynchronousJobRunner):
def _retrieve_job(self, job_id):
jobs = self._chronos_client.list()
job = filter((lambda x: x['name'] == job_id), jobs)
job = [x for x in jobs if x['name'] == job_id]
if len(job) > 1:
msg = 'Multiple jobs found with name {name!r}'.format(name=job_id)
LOGGER.error(msg)
+5 -3
View File
@@ -1,5 +1,6 @@
"""
"""
import json
from glob import glob
from os import getcwd
from os.path import (
@@ -56,9 +57,10 @@ class CliInterface(object):
def get_shell_plugin(self, shell_params):
shell_plugin = shell_params.get('plugin', DEFAULT_SHELL_PLUGIN)
if shell_plugin not in self.active_cli_shells:
self.active_cli_shells[shell_plugin] = self.cli_shells[shell_plugin](**shell_params)
return self.active_cli_shells[shell_plugin]
requested_shell_settings = json.dumps(shell_params, sort_keys=True)
if requested_shell_settings not in self.active_cli_shells:
self.active_cli_shells[requested_shell_settings] = self.cli_shells[shell_plugin](**shell_params)
return self.active_cli_shells[requested_shell_settings]
def get_job_interface(self, job_params):
job_plugin = job_params.get('plugin', None)
@@ -3,8 +3,8 @@ import time
import paramiko
from galaxy.util.bunch import Bunch
from .local import LocalShell
from ....util import Bunch
log = logging.getLogger(__name__)
logging.getLogger("paramiko").setLevel(logging.WARNING) # paramiko logging is very verbose
+2 -2
View File
@@ -373,10 +373,10 @@ class HDASerializer( # datasets._UnflattenedMetadataDatasetAssociationSerialize
Return dictionary containing new-style display app urls.
"""
display_apps = []
for display_app in hda.get_display_applications(trans).itervalues():
for display_app in hda.get_display_applications(trans).values():
app_links = []
for link_app in display_app.links.itervalues():
for link_app in display_app.links.values():
app_links.append({
'target': link_app.url.get('target_frame', '_blank'),
'href': link_app.get_display_url(hda, trans),
+4 -4
View File
@@ -413,7 +413,7 @@ class WorkflowContentsManager(UsesAnnotations):
else:
inputs = step.module.get_runtime_inputs(connections=step.output_connections)
step_model = {
'inputs' : [input.to_dict(trans) for input in inputs.itervalues()]
'inputs' : [input.to_dict(trans) for input in inputs.values()]
}
step_model['step_type'] = step.type
step_model['step_label'] = step.label
@@ -713,7 +713,7 @@ class WorkflowContentsManager(UsesAnnotations):
# tools. This should be removed at some point. Mirrored
# hack in _workflow_from_dict should never be removed so
# existing workflow exports continue to function.
for input_name, input_conn in dict(input_conn_dict).iteritems():
for input_name, input_conn in dict(input_conn_dict).items():
if len(input_conn) == 1:
input_conn_dict[input_name] = input_conn[0]
step_dict['input_connections'] = input_conn_dict
@@ -790,7 +790,7 @@ class WorkflowContentsManager(UsesAnnotations):
supplied_steps = data['steps']
# Try to iterate through imported workflow in such a way as to
# preserve step order.
step_indices = supplied_steps.keys()
step_indices = list(supplied_steps.keys())
try:
step_indices = sorted(step_indices, key=int)
except ValueError:
@@ -927,7 +927,7 @@ class WorkflowContentsManager(UsesAnnotations):
"""
for step in steps:
# Input connections
for input_name, conn_list in step.temp_input_connections.iteritems():
for input_name, conn_list in step.temp_input_connections.items():
if not conn_list:
continue
if not isinstance(conn_list, list): # Older style singleton connection
+3 -270
View File
@@ -5,7 +5,6 @@ Naming: try to use class names that have a distinct plural form so that
the relationship cardinalities are obvious (e.g. prefer Dataset to Data)
"""
import errno
import json
import logging
import numbers
import operator
@@ -30,8 +29,8 @@ from galaxy.managers import tags
from galaxy.model.item_attrs import UsesAnnotations
from galaxy.model.util import pgcalc
from galaxy.security import get_permitted_actions
from galaxy.util import (directory_hash_id, Params, ready_name_for_url,
restore_text, unicodify, unique_id)
from galaxy.util import (directory_hash_id, ready_name_for_url,
unicodify, unique_id)
from galaxy.util.bunch import Bunch
from galaxy.util.dictifiable import Dictifiable
from galaxy.util.hash_util import new_secure_hash
@@ -2667,32 +2666,6 @@ class Library(object, Dictifiable, HasName):
active_folders.extend(self.get_active_folders(active_folder, folders))
return sort_by_attr(active_folders, 'id')
def get_info_association(self, restrict=False, inherited=False):
if self.info_association:
if not inherited or self.info_association[0].inheritable:
return self.info_association[0], inherited
else:
return None, inherited
return None, inherited
def get_template_widgets(self, trans, get_contents=True):
# See if we have any associated templates - the returned value for
# inherited is not applicable at the library level. The get_contents
# param is passed by callers that are inheriting a template - these
# are usually new library datsets for which we want to include template
# fields on the upload form, but not necessarily the contents of the
# inherited template saved for the parent.
info_association, inherited = self.get_info_association()
if info_association:
template = info_association.template
if get_contents:
# See if we have any field contents
info = info_association.info
if info:
return template.get_widgets(trans.user, contents=info.content)
return template.get_widgets(trans.user)
return []
def get_access_roles(self, trans):
roles = []
for lp in self.actions:
@@ -2723,51 +2696,6 @@ class LibraryFolder(object, Dictifiable, HasName):
folder.order_id = self.item_count
self.item_count += 1
def get_info_association(self, restrict=False, inherited=False):
# If restrict is True, we will return this folder's info_association, not inheriting.
# If restrict is False, we'll return the next available info_association in the
# inheritable hierarchy if it is "inheritable". True is also returned if the
# info_association was inherited and False if not. This enables us to eliminate
# displaying any contents of the inherited template.
if self.info_association:
if not inherited or self.info_association[0].inheritable:
return self.info_association[0], inherited
else:
return None, inherited
if restrict:
return None, inherited
if self.parent:
return self.parent.get_info_association(inherited=True)
if self.library_root:
return self.library_root[0].get_info_association(inherited=True)
return None, inherited
def get_template_widgets(self, trans, get_contents=True):
# See if we have any associated templates. The get_contents
# param is passed by callers that are inheriting a template - these
# are usually new library datsets for which we want to include template
# fields on the upload form.
info_association, inherited = self.get_info_association()
if info_association:
if inherited:
template = info_association.template.current.latest_form
else:
template = info_association.template
# See if we have any field contents, but only if the info_association was
# not inherited ( we do not want to display the inherited contents ).
# (gvk: 8/30/10) Based on conversations with Dan, we agreed to ALWAYS inherit
# contents. We'll use this behavior until we hear from the community that
# contents should not be inherited. If we don't hear anything for a while,
# eliminate the old commented out behavior.
# if not inherited and get_contents:
if get_contents:
info = info_association.info
if info:
return template.get_widgets(trans.user, info.content)
else:
return template.get_widgets(trans.user)
return []
@property
def activatable_library_datasets(self):
# This needs to be a list
@@ -2775,13 +2703,6 @@ class LibraryFolder(object, Dictifiable, HasName):
def to_dict(self, view='collection', value_mapper=None):
rval = super(LibraryFolder, self).to_dict(view=view, value_mapper=value_mapper)
info_association, inherited = self.get_info_association()
if info_association:
if inherited:
template = info_association.template.current.latest_form
else:
template = info_association.template
rval['data_template'] = template.name
rval['library_path'] = self.library_path
rval['parent_library_id'] = self.parent_library.id
return rval
@@ -2855,15 +2776,6 @@ class LibraryDataset(object):
# display in other objects, we can't use the simpler method used by
# other model classes.
ldda = self.library_dataset_dataset_association
template_data = {}
for temp_info in ldda.info_association:
template = temp_info.template
content = temp_info.info.content
tmp_dict = {}
for field in template.fields:
tmp_dict[field['label']] = content[field['name']]
template_data[template.name] = tmp_dict
rval = dict(id=self.id,
ldda_id=ldda.id,
parent_library_id=self.folder.parent_library.id,
@@ -2881,8 +2793,7 @@ class LibraryDataset(object):
genome_build=ldda.dbkey,
misc_info=ldda.info,
misc_blurb=ldda.blurb,
peek=(lambda ldda: ldda.display_peek() if ldda.peek and ldda.peek != 'no peek' else None)(ldda),
template_data=template_data)
peek=(lambda ldda: ldda.display_peek() if ldda.peek and ldda.peek != 'no peek' else None)(ldda))
if ldda.dataset.uuid is None:
rval['uuid'] = None
else:
@@ -2987,19 +2898,6 @@ class LibraryDatasetDatasetAssociation(DatasetInstance, HasName):
def has_manage_permissions_roles(self, trans):
return self.dataset.has_manage_permissions_roles(trans)
def get_info_association(self, restrict=False, inherited=False):
# If restrict is True, we will return this ldda's info_association whether it
# exists or not ( in which case None will be returned ). If restrict is False,
# we'll return the next available info_association in the inheritable hierarchy.
# True is also returned if the info_association was inherited, and False if not.
# This enables us to eliminate displaying any contents of the inherited template.
# SM: Accessing self.info_association can cause a query to be emitted
if self.info_association:
return self.info_association[0], inherited
if restrict:
return None, inherited
return self.library_dataset.folder.get_info_association(inherited=True)
def to_dict(self, view='collection'):
# Since this class is a proxy to rather complex attributes we want to
# display in other objects, we can't use the simpler method used by
@@ -3044,55 +2942,6 @@ class LibraryDatasetDatasetAssociation(DatasetInstance, HasName):
rval['metadata_' + name] = val
return rval
def get_template_widgets(self, trans, get_contents=True):
# See if we have any associated templatesThe get_contents
# param is passed by callers that are inheriting a template - these
# are usually new library datsets for which we want to include template
# fields on the upload form, but not necessarily the contents of the
# inherited template saved for the parent.
info_association, inherited = self.get_info_association()
if info_association:
if inherited:
template = info_association.template.current.latest_form
else:
template = info_association.template
# See if we have any field contents, but only if the info_association was
# not inherited ( we do not want to display the inherited contents ).
# (gvk: 8/30/10) Based on conversations with Dan, we agreed to ALWAYS inherit
# contents. We'll use this behavior until we hear from the community that
# contents should not be inherited. If we don't hear anything for a while,
# eliminate the old commented out behavior.
# if not inherited and get_contents:
if get_contents:
info = info_association.info
if info:
return template.get_widgets(trans.user, info.content)
else:
return template.get_widgets(trans.user)
return []
def templates_dict(self, use_name=False):
"""
Returns a dict of template info
"""
# TODO: Should have a method that allows names and labels to be returned together in a structured way
template_data = {}
for temp_info in self.info_association:
template = temp_info.template
content = temp_info.info.content
tmp_dict = {}
for field in template.fields:
if use_name:
name = field['name']
else:
name = field['label']
tmp_dict[name] = content.get(field['name'])
template_data[template.name] = tmp_dict
return template_data
def templates_json(self, use_name=False):
return json.dumps(self.templates_dict(use_name=use_name))
class ExtendedMetadata(object):
def __init__(self, data):
@@ -4450,98 +4299,6 @@ class FormDefinition(object, Dictifiable):
gridfields[i] = f
return gridfields
def get_widgets(self, user, contents={}, **kwd):
'''
Return the list of widgets that comprise a form definition,
including field contents if any.
'''
params = Params(kwd)
widgets = []
for index, field in enumerate(self.fields):
field_type = field['type']
if 'name' in field:
field_name = field['name']
else:
# Default to names like field_0, field_1, etc for backward compatibility
# (not sure this is necessary)...
field_name = 'field_%i' % index
# Determine the value of the field
if field_name in kwd:
# The form was submitted via refresh_on_change
if field_type == 'CheckboxField':
value = CheckboxField.is_checked(params.get(field_name, False))
else:
value = restore_text(params.get(field_name, ''))
elif contents:
try:
# This field has a saved value.
value = str(contents[field['name']])
except Exception:
# If there was an error getting the saved value, we'll still
# display the widget, but it will be empty.
if field_type == 'AddressField':
value = 'none'
elif field_type == 'CheckboxField':
# Since we do not have contents, set checkbox value to False
value = False
else:
# Set other field types to empty string
value = ''
else:
# If none of the above, then leave the field empty
if field_type == 'AddressField':
value = 'none'
elif field_type == 'CheckboxField':
# Since we do not have contents, set checkbox value to False
value = False
else:
# Set other field types to the default value of the field
value = field.get('default', '')
# Create the field widget
field_widget = eval(field_type)(field_name)
if field_type in ['TextField', 'PasswordField']:
field_widget.set_size(40)
field_widget.value = value
elif field_type == 'TextArea':
field_widget.set_size(3, 40)
field_widget.value = value
elif field_type in ['AddressField', 'WorkflowField', 'WorkflowMappingField', 'HistoryField']:
field_widget.user = user
field_widget.value = value
field_widget.params = params
elif field_type == 'SelectField':
for option in field['selectlist']:
if option == value:
field_widget.add_option(option, option, selected=True)
else:
field_widget.add_option(option, option)
elif field_type == 'CheckboxField':
field_widget.set_checked(value)
if field['required'] == 'required':
req = 'Required'
else:
req = 'Optional'
if field['helptext']:
helptext = '%s (%s)' % (field['helptext'], req)
else:
helptext = '(%s)' % req
widgets.append(dict(label=field['label'], widget=field_widget, helptext=helptext))
return widgets
def field_as_html(self, field):
"""Generates disabled html for a field"""
type = field['type']
form_field = None
for field_type in self.supported_field_types:
if type == field_type.__name__:
# Name it AddressField, CheckboxField, etc.
form_field = field_type(type)
break
if form_field:
return form_field.get_html(disabled=True)
# Return None if unsupported field type
return None
class FormDefinitionCurrent(object):
def __init__(self, form_definition=None):
@@ -4581,30 +4338,6 @@ class UserAddress(object):
'country' : sanitize_html(self.country),
'phone' : sanitize_html(self.phone)}
def get_html(self):
# This should probably be deprecated eventually. It should currently
# sanitize.
# TODO Find out where else uses this and replace with
# templates
html = ''
if self.name:
html = html + sanitize_html(self.name)
if self.institution:
html = html + '<br/>' + sanitize_html(self.institution)
if self.address:
html = html + '<br/>' + sanitize_html(self.address)
if self.city:
html = html + '<br/>' + sanitize_html(self.city)
if self.state:
html = html + ' ' + sanitize_html(self.state)
if self.postal_code:
html = html + ' ' + sanitize_html(self.postal_code)
if self.country:
html = html + '<br/>' + sanitize_html(self.country)
if self.phone:
html = html + '<br/>' + 'phone: ' + sanitize_html(self.phone)
return html
class UserOpenID(object):
def __init__(self, user=None, session=None, openid=None):
-69
View File
@@ -227,29 +227,6 @@ class MetadataParameter(object):
other_values = other_values or {}
return form_builder.TextField(self.spec.name, value=value)
def get_html(self, value, context=None, other_values=None, **kwd):
"""
The "context" is simply the metadata collection/bunch holding
this piece of metadata. This is passed in to allow for
metadata to validate against each other (note: this could turn
into a huge, recursive mess if not done with care). For
example, a column assignment should validate against the
number of columns in the dataset.
"""
context = context or {}
other_values = other_values or {}
if self.spec.get("readonly"):
return value
if self.spec.get("optional"):
checked = False
if value:
checked = "true"
checkbox = form_builder.CheckboxField("is_" + self.spec.name, checked=checked)
return checkbox.get_html() + self.get_field(value=value, context=context, other_values=other_values, **kwd).get_html()
else:
return self.get_field(value=value, context=context, other_values=other_values, **kwd).get_html()
def to_string(self, value):
return str(value)
@@ -390,16 +367,6 @@ class SelectParameter(MetadataParameter):
field.add_option(val, label, selected=False)
return field
def get_html(self, value, context=None, other_values=None, values=None, **kwd):
context = context or {}
other_values = other_values or {}
if self.spec.get("readonly"):
if value in [None, []]:
return str(self.spec.no_value)
return ", ".join(map(str, value))
return MetadataParameter.get_html(self, value, context=context, other_values=other_values, values=values, **kwd)
def wrap(self, value, session):
# do we really need this (wasteful)? - yes because we are not sure that
# all existing selects have been stored previously as lists. Also this
@@ -433,15 +400,6 @@ class DBKeyParameter(SelectParameter):
pass
return super(DBKeyParameter, self).get_field(value, context, other_values, values, **kwd)
def get_html(self, value=None, context=None, other_values=None, values=None, **kwd):
context = context or {}
other_values = other_values or {}
try:
values = kwd['trans'].app.genome_builds.get_genome_build_names(kwd['trans'])
except KeyError:
pass
return super(DBKeyParameter, self).get_html(value, context, other_values, values, **kwd)
class RangeParameter(SelectParameter):
@@ -460,14 +418,6 @@ class RangeParameter(SelectParameter):
values = list(zip(range(self.min, self.max, self.step), range(self.min, self.max, self.step)))
return SelectParameter.get_field(self, value=value, context=context, other_values=other_values, values=values, **kwd)
def get_html(self, value, context=None, other_values=None, values=None, **kwd):
context = context or {}
other_values = other_values or {}
if values is None:
values = list(zip(range(self.min, self.max, self.step), range(self.min, self.max, self.step)))
return SelectParameter.get_html(self, value, context=context, other_values=other_values, values=values, **kwd)
@classmethod
def marshal(cls, value):
value = SelectParameter.marshal(value)
@@ -486,15 +436,6 @@ class ColumnParameter(RangeParameter):
values = list(zip(column_range, column_range))
return RangeParameter.get_field(self, value=value, context=context, other_values=other_values, values=values, **kwd)
def get_html(self, value, context=None, other_values=None, values=None, **kwd):
context = context or {}
other_values = other_values or {}
if values is None and context:
column_range = range(1, (context.columns or 0) + 1, 1)
values = list(zip(column_range, column_range))
return RangeParameter.get_html(self, value, context=context, other_values=other_values, values=values, **kwd)
class ColumnTypesParameter(MetadataParameter):
@@ -530,11 +471,6 @@ class PythonObjectParameter(MetadataParameter):
other_values = other_values or {}
return form_builder.TextField(self.spec.name, value=self._to_string(value))
def get_html(self, value=None, context=None, other_values=None, **kwd):
context = context or {}
other_values = other_values or {}
return str(self)
@classmethod
def marshal(cls, value):
return value
@@ -556,11 +492,6 @@ class FileParameter(MetadataParameter):
other_values = other_values or {}
return form_builder.TextField(self.spec.name, value=str(value.id))
def get_html(self, value=None, context=None, other_values=None, **kwd):
context = context or {}
other_values = other_values or {}
return "<div>No display available for Metadata Files</div>"
def wrap(self, value, session):
if value is None:
return None
+3
View File
@@ -723,6 +723,9 @@ def build_object_store_from_config(config, fsmon=False, config_xml=None):
elif store == 's3':
from .s3 import S3ObjectStore
return S3ObjectStore(config=config, config_xml=config_xml)
elif store == 'cloud':
from .cloud import Cloud
return Cloud(config=config, config_xml=config_xml)
elif store == 'swift':
from .s3 import SwiftObjectStore
return SwiftObjectStore(config=config, config_xml=config_xml)
+576
View File
@@ -0,0 +1,576 @@
"""
Object Store plugin for Cloud storage.
"""
import logging
import multiprocessing
import os
import shutil
import subprocess
import threading
import time
from datetime import datetime
from galaxy.exceptions import ObjectInvalid, ObjectNotFound
from galaxy.util import (
directory_hash_id,
safe_relpath,
string_as_bool,
umask_fix_perms,
)
from galaxy.util.sleeper import Sleeper
from ..objectstore import convert_bytes, ObjectStore
try:
from cloudbridge.cloud.factory import CloudProviderFactory, ProviderList
except ImportError:
CloudProviderFactory = None
ProviderList = None
log = logging.getLogger(__name__)
NO_CLOUDBRIDGE_ERROR_MESSAGE = (
"Cloud ObjectStore is configured, but no CloudBridge dependency available."
"Please install CloudBridge or modify ObjectStore configuration."
)
class Cloud(ObjectStore):
"""
Object store that stores objects as items in an cloud storage. A local
cache exists that is used as an intermediate location for files between
Galaxy and the cloud storage.
"""
def __init__(self, config, config_xml):
super(Cloud, self).__init__(config)
if CloudProviderFactory is None:
raise Exception(NO_CLOUDBRIDGE_ERROR_MESSAGE)
self.staging_path = self.config.file_path
self.transfer_progress = 0
self._parse_config_xml(config_xml)
self._configure_connection()
self.bucket = self._get_bucket(self.bucket)
# Clean cache only if value is set in galaxy.ini
if self.cache_size != -1:
# Convert GBs to bytes for comparison
self.cache_size = self.cache_size * 1073741824
# Helper for interruptable sleep
self.sleeper = Sleeper()
self.cache_monitor_thread = threading.Thread(target=self.__cache_monitor)
self.cache_monitor_thread.start()
log.info("Cache cleaner manager started")
# Test if 'axel' is available for parallel download and pull the key into cache
try:
subprocess.call('axel')
self.use_axel = True
except OSError:
self.use_axel = False
def _configure_connection(self):
log.debug("Configuring AWS-S3 Connection")
aws_config = {'aws_access_key': self.access_key,
'aws_secret_key': self.secret_key}
self.conn = CloudProviderFactory().create_provider(ProviderList.AWS, aws_config)
def _parse_config_xml(self, config_xml):
try:
a_xml = config_xml.findall('auth')[0]
self.access_key = a_xml.get('access_key')
self.secret_key = a_xml.get('secret_key')
b_xml = config_xml.findall('bucket')[0]
self.bucket = b_xml.get('name')
self.max_chunk_size = int(b_xml.get('max_chunk_size', 250))
cn_xml = config_xml.findall('connection')
if not cn_xml:
cn_xml = {}
else:
cn_xml = cn_xml[0]
self.host = cn_xml.get('host', None)
self.port = int(cn_xml.get('port', 6000))
self.multipart = string_as_bool(cn_xml.get('multipart', 'True'))
self.is_secure = string_as_bool(cn_xml.get('is_secure', 'True'))
self.conn_path = cn_xml.get('conn_path', '/')
c_xml = config_xml.findall('cache')[0]
self.cache_size = float(c_xml.get('size', -1))
self.staging_path = c_xml.get('path', self.config.object_store_cache_path)
for d_xml in config_xml.findall('extra_dir'):
self.extra_dirs[d_xml.get('type')] = d_xml.get('path')
log.debug("Object cache dir: %s", self.staging_path)
log.debug(" job work dir: %s", self.extra_dirs['job_work'])
except Exception:
# Toss it back up after logging, we can't continue loading at this point.
log.exception("Malformed ObjectStore Configuration XML -- unable to continue")
raise
def __cache_monitor(self):
time.sleep(2) # Wait for things to load before starting the monitor
while self.running:
total_size = 0
# Is this going to be too expensive of an operation to be done frequently?
file_list = []
for dirpath, _, filenames in os.walk(self.staging_path):
for filename in filenames:
filepath = os.path.join(dirpath, filename)
file_size = os.path.getsize(filepath)
total_size += file_size
# Get the time given file was last accessed
last_access_time = time.localtime(os.stat(filepath)[7])
# Compose a tuple of the access time and the file path
file_tuple = last_access_time, filepath, file_size
file_list.append(file_tuple)
# Sort the file list (based on access time)
file_list.sort()
# Initiate cleaning once within 10% of the defined cache size?
cache_limit = self.cache_size * 0.9
if total_size > cache_limit:
log.info("Initiating cache cleaning: current cache size: %s; clean until smaller than: %s",
convert_bytes(total_size), convert_bytes(cache_limit))
# How much to delete? If simply deleting up to the cache-10% limit,
# is likely to be deleting frequently and may run the risk of hitting
# the limit - maybe delete additional #%?
# For now, delete enough to leave at least 10% of the total cache free
delete_this_much = total_size - cache_limit
self.__clean_cache(file_list, delete_this_much)
self.sleeper.sleep(30) # Test cache size every 30 seconds?
def __clean_cache(self, file_list, delete_this_much):
""" Keep deleting files from the file_list until the size of the deleted
files is greater than the value in delete_this_much parameter.
:type file_list: list
:param file_list: List of candidate files that can be deleted. This method
will start deleting files from the beginning of the list so the list
should be sorted accordingly. The list must contains 3-element tuples,
positioned as follows: position 0 holds file last accessed timestamp
(as time.struct_time), position 1 holds file path, and position 2 has
file size (e.g., (<access time>, /mnt/data/dataset_1.dat), 472394)
:type delete_this_much: int
:param delete_this_much: Total size of files, in bytes, that should be deleted.
"""
# Keep deleting datasets from file_list until deleted_amount does not
# exceed delete_this_much; start deleting from the front of the file list,
# which assumes the oldest files come first on the list.
deleted_amount = 0
for entry in enumerate(file_list):
if deleted_amount < delete_this_much:
deleted_amount += entry[2]
os.remove(entry[1])
# Debugging code for printing deleted files' stats
# folder, file_name = os.path.split(f[1])
# file_date = time.strftime("%m/%d/%y %H:%M:%S", f[0])
# log.debug("%s. %-25s %s, size %s (deleted %s/%s)" \
# % (i, file_name, convert_bytes(f[2]), file_date, \
# convert_bytes(deleted_amount), convert_bytes(delete_this_much)))
else:
log.debug("Cache cleaning done. Total space freed: %s", convert_bytes(deleted_amount))
return
def _get_bucket(self, bucket_name):
try:
bucket = self.conn.object_store.get(bucket_name)
if bucket is None:
log.debug("Bucket not found, creating a bucket with handle '%s'", bucket_name)
bucket = self.conn.object_store.create(bucket_name)
log.debug("Using cloud ObjectStore with bucket '%s'", bucket.name)
return bucket
except Exception:
# These two generic exceptions will be replaced by specific exceptions
# once proper exceptions are exposed by CloudBridge.
log.exception("Could not get bucket '%s'.", bucket_name)
raise Exception
def _fix_permissions(self, rel_path):
""" Set permissions on rel_path"""
for basedir, _, files in os.walk(rel_path):
umask_fix_perms(basedir, self.config.umask, 0o777, self.config.gid)
for filename in files:
path = os.path.join(basedir, filename)
# Ignore symlinks
if os.path.islink(path):
continue
umask_fix_perms(path, self.config.umask, 0o666, self.config.gid)
def _construct_path(self, obj, base_dir=None, dir_only=None, extra_dir=None, extra_dir_at_root=False, alt_name=None,
obj_dir=False, **kwargs):
# extra_dir should never be constructed from provided data but just
# make sure there are no shenannigans afoot
if extra_dir and extra_dir != os.path.normpath(extra_dir):
log.warning('extra_dir is not normalized: %s', extra_dir)
raise ObjectInvalid("The requested object is invalid")
# ensure that any parent directory references in alt_name would not
# result in a path not contained in the directory path constructed here
if alt_name:
if not safe_relpath(alt_name):
log.warning('alt_name would locate path outside dir: %s', alt_name)
raise ObjectInvalid("The requested object is invalid")
# alt_name can contain parent directory references, but S3 will not
# follow them, so if they are valid we normalize them out
alt_name = os.path.normpath(alt_name)
rel_path = os.path.join(*directory_hash_id(obj.id))
if extra_dir is not None:
if extra_dir_at_root:
rel_path = os.path.join(extra_dir, rel_path)
else:
rel_path = os.path.join(rel_path, extra_dir)
# for JOB_WORK directory
if obj_dir:
rel_path = os.path.join(rel_path, str(obj.id))
if base_dir:
base = self.extra_dirs.get(base_dir)
return os.path.join(base, rel_path)
# S3 folders are marked by having trailing '/' so add it now
rel_path = '%s/' % rel_path
if not dir_only:
rel_path = os.path.join(rel_path, alt_name if alt_name else "dataset_%s.dat" % obj.id)
return rel_path
def _get_cache_path(self, rel_path):
return os.path.abspath(os.path.join(self.staging_path, rel_path))
def _get_transfer_progress(self):
return self.transfer_progress
def _get_size_in_cloud(self, rel_path):
try:
obj = self.bucket.get(rel_path)
if obj:
return obj.size
except Exception:
log.exception("Could not get size of key '%s' from S3", rel_path)
return -1
def _key_exists(self, rel_path):
exists = False
try:
# A hackish way of testing if the rel_path is a folder vs a file
is_dir = rel_path[-1] == '/'
if is_dir:
keyresult = self.bucket.list(prefix=rel_path)
if len(keyresult) > 0:
exists = True
else:
exists = False
else:
exists = True if self.bucket.get(rel_path) is not None else False
except Exception:
log.exception("Trouble checking existence of S3 key '%s'", rel_path)
return False
if rel_path[0] == '/':
raise
return exists
def _in_cache(self, rel_path):
""" Check if the given dataset is in the local cache and return True if so. """
# log.debug("------ Checking cache for rel_path %s" % rel_path)
cache_path = self._get_cache_path(rel_path)
return os.path.exists(cache_path)
def _pull_into_cache(self, rel_path):
# Ensure the cache directory structure exists (e.g., dataset_#_files/)
rel_path_dir = os.path.dirname(rel_path)
if not os.path.exists(self._get_cache_path(rel_path_dir)):
os.makedirs(self._get_cache_path(rel_path_dir))
# Now pull in the file
file_ok = self._download(rel_path)
self._fix_permissions(self._get_cache_path(rel_path_dir))
return file_ok
def _transfer_cb(self, complete, total):
self.transfer_progress += 10
def _download(self, rel_path):
try:
log.debug("Pulling key '%s' into cache to %s", rel_path, self._get_cache_path(rel_path))
key = self.bucket.get(rel_path)
# Test if cache is large enough to hold the new file
if self.cache_size > 0 and key.size > self.cache_size:
log.critical("File %s is larger (%s) than the cache size (%s). Cannot download.",
rel_path, key.size, self.cache_size)
return False
if self.use_axel:
log.debug("Parallel pulled key '%s' into cache to %s", rel_path, self._get_cache_path(rel_path))
ncores = multiprocessing.cpu_count()
url = key.generate_url(7200)
ret_code = subprocess.call("axel -a -n %s '%s'" % (ncores, url))
if ret_code == 0:
return True
else:
log.debug("Pulled key '%s' into cache to %s", rel_path, self._get_cache_path(rel_path))
self.transfer_progress = 0 # Reset transfer progress counter
with open(self._get_cache_path(rel_path), "w+") as downloaded_file_handle:
key.save_content(downloaded_file_handle)
return True
except Exception:
log.exception("Problem downloading key '%s' from S3 bucket '%s'", rel_path, self.bucket.name)
return False
def _push_to_os(self, rel_path, source_file=None, from_string=None):
"""
Push the file pointed to by ``rel_path`` to the object store naming the key
``rel_path``. If ``source_file`` is provided, push that file instead while
still using ``rel_path`` as the key name.
If ``from_string`` is provided, set contents of the file to the value of
the string.
"""
try:
source_file = source_file if source_file else self._get_cache_path(rel_path)
if os.path.exists(source_file):
if os.path.getsize(source_file) == 0 and (self.bucket.get(rel_path) is not None):
log.debug("Wanted to push file '%s' to S3 key '%s' but its size is 0; skipping.", source_file,
rel_path)
return True
if from_string:
if not self.bucket.get(rel_path):
created_obj = self.bucket.create_object(rel_path)
created_obj.upload(source_file)
else:
self.bucket.get(rel_path).upload(source_file)
log.debug("Pushed data from string '%s' to key '%s'", from_string, rel_path)
else:
start_time = datetime.now()
log.debug("Pushing cache file '%s' of size %s bytes to key '%s'", source_file,
os.path.getsize(source_file), rel_path)
self.transfer_progress = 0 # Reset transfer progress counter
if not self.bucket.get(rel_path):
created_obj = self.bucket.create_object(rel_path)
created_obj.upload_from_file(source_file)
else:
self.bucket.get(rel_path).upload_from_file(source_file)
end_time = datetime.now()
log.debug("Pushed cache file '%s' to key '%s' (%s bytes transfered in %s sec)",
source_file, rel_path, os.path.getsize(source_file), end_time - start_time)
return True
else:
log.error("Tried updating key '%s' from source file '%s', but source file does not exist.",
rel_path, source_file)
except Exception:
log.exception("Trouble pushing S3 key '%s' from file '%s'", rel_path, source_file)
return False
def file_ready(self, obj, **kwargs):
"""
A helper method that checks if a file corresponding to a dataset is
ready and available to be used. Return ``True`` if so, ``False`` otherwise.
"""
rel_path = self._construct_path(obj, **kwargs)
# Make sure the size in cache is available in its entirety
if self._in_cache(rel_path):
if os.path.getsize(self._get_cache_path(rel_path)) == self._get_size_in_cloud(rel_path):
return True
log.debug("Waiting for dataset %s to transfer from OS: %s/%s", rel_path,
os.path.getsize(self._get_cache_path(rel_path)), self._get_size_in_cloud(rel_path))
return False
def exists(self, obj, **kwargs):
in_cache = False
rel_path = self._construct_path(obj, **kwargs)
# Check cache
if self._in_cache(rel_path):
in_cache = True
# Check cloud
in_cloud = self._key_exists(rel_path)
# log.debug("~~~~~~ File '%s' exists in cache: %s; in s3: %s" % (rel_path, in_cache, in_s3))
# dir_only does not get synced so shortcut the decision
dir_only = kwargs.get('dir_only', False)
base_dir = kwargs.get('base_dir', None)
if dir_only:
if in_cache or in_cloud:
return True
# for JOB_WORK directory
elif base_dir:
if not os.path.exists(rel_path):
os.makedirs(rel_path)
return True
else:
return False
# TODO: Sync should probably not be done here. Add this to an async upload stack?
if in_cache and not in_cloud:
self._push_to_os(rel_path, source_file=self._get_cache_path(rel_path))
return True
elif in_cloud:
return True
else:
return False
def create(self, obj, **kwargs):
if not self.exists(obj, **kwargs):
# Pull out locally used fields
extra_dir = kwargs.get('extra_dir', None)
extra_dir_at_root = kwargs.get('extra_dir_at_root', False)
dir_only = kwargs.get('dir_only', False)
alt_name = kwargs.get('alt_name', None)
# Construct hashed path
rel_path = os.path.join(*directory_hash_id(obj.id))
# Optionally append extra_dir
if extra_dir is not None:
if extra_dir_at_root:
rel_path = os.path.join(extra_dir, rel_path)
else:
rel_path = os.path.join(rel_path, extra_dir)
# Create given directory in cache
cache_dir = os.path.join(self.staging_path, rel_path)
if not os.path.exists(cache_dir):
os.makedirs(cache_dir)
if not dir_only:
rel_path = os.path.join(rel_path, alt_name if alt_name else "dataset_%s.dat" % obj.id)
open(os.path.join(self.staging_path, rel_path), 'w').close()
self._push_to_os(rel_path, from_string='')
def empty(self, obj, **kwargs):
if self.exists(obj, **kwargs):
return bool(self.size(obj, **kwargs) > 0)
else:
raise ObjectNotFound('objectstore.empty, object does not exist: %s, kwargs: %s'
% (str(obj), str(kwargs)))
def size(self, obj, **kwargs):
rel_path = self._construct_path(obj, **kwargs)
if self._in_cache(rel_path):
try:
return os.path.getsize(self._get_cache_path(rel_path))
except OSError as ex:
log.info("Could not get size of file '%s' in local cache, will try cloud. Error: %s", rel_path, ex)
elif self.exists(obj, **kwargs):
return self._get_size_in_cloud(rel_path)
log.warning("Did not find dataset '%s', returning 0 for size", rel_path)
return 0
def delete(self, obj, entire_dir=False, **kwargs):
rel_path = self._construct_path(obj, **kwargs)
extra_dir = kwargs.get('extra_dir', None)
base_dir = kwargs.get('base_dir', None)
dir_only = kwargs.get('dir_only', False)
obj_dir = kwargs.get('obj_dir', False)
try:
# Remove temparory data in JOB_WORK directory
if base_dir and dir_only and obj_dir:
shutil.rmtree(os.path.abspath(rel_path))
return True
# For the case of extra_files, because we don't have a reference to
# individual files/keys we need to remove the entire directory structure
# with all the files in it. This is easy for the local file system,
# but requires iterating through each individual key in S3 and deleing it.
if entire_dir and extra_dir:
shutil.rmtree(self._get_cache_path(rel_path))
results = self.bucket.list(prefix=rel_path)
for key in results:
log.debug("Deleting key %s", key.name)
key.delete()
return True
else:
# Delete from cache first
os.unlink(self._get_cache_path(rel_path))
# Delete from S3 as well
if self._key_exists(rel_path):
key = self.bucket.get(rel_path)
log.debug("Deleting key %s", key.name)
key.delete()
return True
except Exception:
log.exception("Could not delete key '%s' from cloud", rel_path)
except OSError:
log.exception('%s delete error', self.get_filename(obj, **kwargs))
return False
def get_data(self, obj, start=0, count=-1, **kwargs):
rel_path = self._construct_path(obj, **kwargs)
# Check cache first and get file if not there
if not self._in_cache(rel_path):
self._pull_into_cache(rel_path)
# Read the file content from cache
data_file = open(self._get_cache_path(rel_path), 'r')
data_file.seek(start)
content = data_file.read(count)
data_file.close()
return content
def get_filename(self, obj, **kwargs):
base_dir = kwargs.get('base_dir', None)
dir_only = kwargs.get('dir_only', False)
obj_dir = kwargs.get('obj_dir', False)
rel_path = self._construct_path(obj, **kwargs)
# for JOB_WORK directory
if base_dir and dir_only and obj_dir:
return os.path.abspath(rel_path)
cache_path = self._get_cache_path(rel_path)
# S3 does not recognize directories as files so cannot check if those exist.
# So, if checking dir only, ensure given dir exists in cache and return
# the expected cache path.
# dir_only = kwargs.get('dir_only', False)
# if dir_only:
# if not os.path.exists(cache_path):
# os.makedirs(cache_path)
# return cache_path
# Check if the file exists in the cache first
if self._in_cache(rel_path):
return cache_path
# Check if the file exists in persistent storage and, if it does, pull it into cache
elif self.exists(obj, **kwargs):
if dir_only: # Directories do not get pulled into cache
return cache_path
else:
if self._pull_into_cache(rel_path):
return cache_path
# For the case of retrieving a directory only, return the expected path
# even if it does not exist.
# if dir_only:
# return cache_path
raise ObjectNotFound('objectstore.get_filename, no cache_path: %s, kwargs: %s'
% (str(obj), str(kwargs)))
# return cache_path # Until the upload tool does not explicitly create the dataset, return expected path
def update_from_file(self, obj, file_name=None, create=False, **kwargs):
if create:
self.create(obj, **kwargs)
if self.exists(obj, **kwargs):
rel_path = self._construct_path(obj, **kwargs)
# Chose whether to use the dataset file itself or an alternate file
if file_name:
source_file = os.path.abspath(file_name)
# Copy into cache
cache_file = self._get_cache_path(rel_path)
try:
if source_file != cache_file:
# FIXME? Should this be a `move`?
shutil.copy2(source_file, cache_file)
self._fix_permissions(cache_file)
except OSError:
log.exception("Trouble copying source file '%s' to cache '%s'", source_file, cache_file)
else:
source_file = self._get_cache_path(rel_path)
# Update the file on cloud
self._push_to_os(rel_path, source_file)
else:
raise ObjectNotFound('objectstore.update_from_file, object does not exist: %s, kwargs: %s'
% (str(obj), str(kwargs)))
def get_object_url(self, obj, **kwargs):
if self.exists(obj, **kwargs):
rel_path = self._construct_path(obj, **kwargs)
try:
key = self.bucket.get(rel_path)
return key.generate_url(expires_in=86400) # 24hrs
except Exception:
log.exception("Trouble generating URL for dataset '%s'", rel_path)
return None
def get_store_usage_percent(self):
return 0.0
+5 -3
View File
@@ -4,6 +4,8 @@ Contains OpenID provider functionality
import logging
import os
import six
from galaxy.util import parse_xml, string_as_bool
from galaxy.util.odict import odict
@@ -87,7 +89,7 @@ class OpenIDProvider(object):
def post_authentication(self, trans, openid_manager, info):
sreg_attributes = openid_manager.get_sreg(info)
for store_pref_name, store_pref_value_name in self.store_user_preference.iteritems():
for store_pref_name, store_pref_value_name in self.store_user_preference.items():
if store_pref_value_name in (self.sreg_optional + self.sreg_required):
trans.user.preferences[store_pref_name] = sreg_attributes.get(store_pref_value_name)
else:
@@ -129,10 +131,10 @@ class OpenIDProviders(object):
self.providers = providers
else:
self.providers = odict()
self._banned_identifiers = [provider.op_endpoint_url for provider in self.providers.itervalues() if provider.never_associate_with_user]
self._banned_identifiers = [provider.op_endpoint_url for provider in self.providers.values() if provider.never_associate_with_user]
def __iter__(self):
for provider in self.providers.itervalues():
for provider in six.itervalues(self.providers):
yield provider
def get(self, name, default=None):
+2 -6
View File
@@ -309,11 +309,9 @@ class ToolBox(BaseGalaxyToolBox):
def __build_tool_version_select_field(self, tools, tool_id, set_selected):
"""Build a SelectField whose options are the ids for the received list of tools."""
options = []
refresh_on_change_values = []
for tool in tools:
options.insert(0, (tool.version, tool.id))
refresh_on_change_values.append(tool.id)
select_field = SelectField(name='tool_id', refresh_on_change=True, refresh_on_change_values=refresh_on_change_values)
select_field = SelectField(name='tool_id')
for option_tup in options:
selected = set_selected and option_tup[1] == tool_id
if selected:
@@ -341,7 +339,7 @@ class DefaultToolState(object):
"""
self.inputs = {}
context = ExpressionContext(self.inputs)
for input in tool.inputs.itervalues():
for input in tool.inputs.values():
self.inputs[input.name] = input.get_initial_value(trans, context)
def encode(self, tool, app, nested=False):
@@ -1042,8 +1040,6 @@ class Tool(object, Dictifiable):
group.default_file_type = elem.get('default_file_type', group.default_file_type)
group.metadata_ref = elem.get('metadata_ref', group.metadata_ref)
rval[group.file_type_name].refresh_on_change = True
rval[group.file_type_name].refresh_on_change_values = \
self.app.datatypes_registry.get_composite_extensions()
group_page_source = XmlPageSource(elem)
group.inputs = self.parse_input_elem(group_page_source, enctypes, context)
rval[group.name] = group
+1 -1
View File
@@ -448,7 +448,7 @@ def create_paramfile(trans, uploaded_datasets):
auto_decompress=getattr(uploaded_dataset, "auto_decompress", True),
purge_source=purge_source,
space_to_tab=uploaded_dataset.space_to_tab,
in_place=trans.app.config.external_chown_script is None,
run_as_real_user=trans.app.config.external_chown_script is None,
check_content=trans.app.config.check_upload_content,
path=uploaded_dataset.path)
# TODO: This will have to change when we start bundling inputs.
+4 -4
View File
@@ -78,9 +78,9 @@ def to_cwl_job(tool, param_dict, local_working_directory):
else:
return str(param_dict_value)
for input_name, input in inputs.iteritems():
for input_name, input in inputs.items():
if input.type == "repeat":
only_input = input.inputs.values()[0]
only_input = next(iter(input.inputs.values()))
array_value = []
for instance in param_dict[input_name]:
array_value.append(simple_value(only_input, instance[input_name[:-len("_repeat")]]))
@@ -112,7 +112,7 @@ def to_galaxy_parameters(tool, as_dict):
else:
return param_dict_value
for input_name, input in inputs.iteritems():
for input_name, input in inputs.items():
as_dict_value = as_dict.get(input_name, NOT_PRESENT)
galaxy_input_type = input.type
@@ -120,7 +120,7 @@ def to_galaxy_parameters(tool, as_dict):
if input_name not in as_dict:
continue
only_input = input.inputs.values()[0]
only_input = next(iter(input.inputs.values()))
for index, value in enumerate(as_dict_value):
key = "%s_repeat_0|%s" % (input_name, only_input.name)
galaxy_value = from_simple_value(only_input, value)
+1 -1
View File
@@ -240,7 +240,7 @@ class JobImportHistoryArchiveWrapper(object, UsesAnnotations):
# Set parameters. May be useful to look at metadata.py for creating parameters.
# TODO: there may be a better way to set parameters, e.g.:
# for name, value in tool.params_to_strings( incoming, trans.app ).iteritems():
# for name, value in tool.params_to_strings( incoming, trans.app ).items():
# job.add_parameter( name, value )
# to make this work, we'd need to flesh out the HDA objects. The code below is
# relatively similar.
-1
View File
@@ -75,7 +75,6 @@ class ToolParameter(object, Dictifiable):
def __init__(self, tool, input_source, context=None):
input_source = ensure_input_source(input_source)
self.tool = tool
self.refresh_on_change_values = []
self.argument = input_source.get("argument")
self.name = self.__class__.parse_name(input_source)
self.type = input_source.get("type")
+4 -5
View File
@@ -39,13 +39,12 @@ def expand_multi_inputs(inputs, classifier, key_filter=None):
def __split_inputs(inputs, classifier, key_filter):
key_filter = key_filter or (lambda x: True)
input_keys = filter(key_filter, inputs)
single_inputs = {}
matched_multi_inputs = {}
multiplied_multi_inputs = {}
for input_key in input_keys:
for input_key in filter(key_filter, inputs):
input_type, expanded_val = classifier(input_key)
if input_type == input_classification.SINGLE:
single_inputs[input_key] = expanded_val
@@ -73,14 +72,14 @@ def __extend_with_matched_combos(single_inputs, multi_inputs):
matched_multi_inputs = []
first_multi_input_key = multi_inputs.keys()[0]
first_multi_input_key = next(iter(multi_inputs.keys()))
first_multi_value = multi_inputs.get(first_multi_input_key)
for value in first_multi_value:
new_inputs = __copy_and_extend_inputs(single_inputs, first_multi_input_key, value)
matched_multi_inputs.append(new_inputs)
for multi_input_key, multi_input_values in multi_inputs.iteritems():
for multi_input_key, multi_input_values in multi_inputs.items():
if multi_input_key == first_multi_input_key:
continue
if len(multi_input_values) != len(first_multi_value):
@@ -95,7 +94,7 @@ def __extend_with_matched_combos(single_inputs, multi_inputs):
def __extend_with_multiplied_combos(input_combos, multi_inputs):
combos = input_combos
for multi_input_key, multi_input_value in multi_inputs.iteritems():
for multi_input_key, multi_input_value in multi_inputs.items():
iter_combos = []
for combo in combos:
+1 -1
View File
@@ -42,7 +42,7 @@ def _app_properties(args):
def _arg_parser():
parser = argparse.ArgumentParser(description=DESCRIPTION)
parser.add_argument('action', metavar='ACTION', type=str,
choices=ACTIONS.keys(),
choices=list(ACTIONS.keys()),
default=DEFAULT_ACTION,
nargs='?' if DEFAULT_ACTION is not None else None,
help='action to perform')
@@ -37,7 +37,7 @@ class BaseDataProvider(object):
"""
raise Exception("Unimplemented Function")
def get_data(self, chrom, start, end, start_val=0, max_vals=sys.maxint, **kwargs):
def get_data(self, chrom, start, end, start_val=0, max_vals=sys.maxsize, **kwargs):
"""
Returns data as specified by kwargs. start_val is the first element to
return and max_vals indicates the number of values to return.
@@ -44,8 +44,8 @@ def get_bounds(reads, start_pos_index, end_pos_index):
'''
Returns the minimum and maximum position for a set of reads.
'''
max_low = sys.maxint
max_high = -sys.maxint
max_low = sys.maxsize
max_high = -sys.maxsize
for read in reads:
if read[start_pos_index] < max_low:
max_low = read[start_pos_index]
@@ -182,7 +182,7 @@ class GenomeDataProvider(BaseDataProvider):
"""
raise Exception("Unimplemented Function")
def get_data(self, chrom=None, low=None, high=None, start_val=0, max_vals=sys.maxint, **kwargs):
def get_data(self, chrom=None, low=None, high=None, start_val=0, max_vals=sys.maxsize, **kwargs):
"""
Returns data in region defined by chrom, start, and end. start_val and
max_vals are used to denote the data to return: start_val is the first element to
@@ -233,7 +233,7 @@ class GenomeDataProvider(BaseDataProvider):
column_names = self.original_dataset.datatype.column_names
except AttributeError:
try:
column_names = range(self.original_dataset.metadata.columns)
column_names = list(range(self.original_dataset.metadata.columns))
except Exception: # Give up
return []
@@ -511,7 +511,7 @@ class BedDataProvider(GenomeDataProvider):
if length >= 12:
block_sizes = [int(n) for n in feature[10].split(',') if n != '']
block_starts = [int(n) for n in feature[11].split(',') if n != '']
blocks = zip(block_sizes, block_starts)
blocks = list(zip(block_sizes, block_starts))
payload.append([(int(feature[1]) + block[1], int(feature[1]) + block[1] + block[0]) for block in blocks])
# Score (filter data)
@@ -1022,7 +1022,7 @@ class BamDataProvider(GenomeDataProvider, FilterableMixin):
count += 1
# Take care of reads whose mates are out of range.
for qname, read in paired_pending.iteritems():
for qname, read in paired_pending.items():
if read['mate_start'] < read['start']:
# Mate is before read.
read_start = read['mate_start']
@@ -1286,7 +1286,7 @@ class IntervalIndexDataProvider(FilterableMixin, GenomeDataProvider):
out.write(line)
else:
reader = GFFReaderWrapper(source, fix_strand=True)
feature = reader.next()
feature = next(reader)
for interval in feature.intervals:
out.write('\t'.join(interval.fields) + '\n')
@@ -1328,7 +1328,7 @@ class IntervalIndexDataProvider(FilterableMixin, GenomeDataProvider):
# GFF dataset.
reader = GFFReaderWrapper(source, fix_strand=True)
feature = reader.next()
feature = next(reader)
payload = package_gff_feature(feature, no_detail, filter_cols)
payload.insert(0, offset)
@@ -1604,7 +1604,7 @@ class ChromatinInteractionsDataProvider(GenomeDataProvider):
class ChromatinInteractionsTabixDataProvider(TabixDataProvider, ChromatinInteractionsDataProvider):
def get_iterator(self, data_file, chrom, start=0, end=sys.maxint, interchromosomal=False, **kwargs):
def get_iterator(self, data_file, chrom, start=0, end=sys.maxsize, interchromosomal=False, **kwargs):
"""
"""
# Modify start as needed to get earlier interactions with start region.
@@ -1661,7 +1661,7 @@ def package_gff_feature(feature, no_detail=False, filter_cols=[]):
# Add blocks.
block_sizes = [(interval.end - interval.start) for interval in feature_intervals]
block_starts = [(interval.start - feature.start) for interval in feature_intervals]
blocks = zip(block_sizes, block_starts)
blocks = list(zip(block_sizes, block_starts))
payload.append([(feature.start + block[1], feature.start + block[1] + block[0]) for block in blocks])
# Add filter data to payload.
+2 -1
View File
@@ -4,6 +4,7 @@ import re
import sys
from json import loads
import six
from bx.seq.twobit import TwoBitFile
from galaxy.util.bunch import Bunch
@@ -26,7 +27,7 @@ messages = Bunch(
def decode_dbkey(dbkey):
""" Decodes dbkey and returns tuple ( username, dbkey )"""
if isinstance(dbkey, str) and ':' in dbkey:
if isinstance(dbkey, six.string_types) and ':' in dbkey:
return dbkey.split(':')
else:
return None, dbkey
-4
View File
@@ -23,10 +23,6 @@ from .framework.decorators import (
require_admin,
require_login
)
from .framework.formbuilder import (
form,
FormBuilder
)
__all__ = ('url_for', 'error', 'expose', 'json', 'json_pretty',
'require_admin', 'require_login', 'expose_api', 'expose_api_anonymous',
-15
View File
@@ -1289,21 +1289,6 @@ class UsesFormDefinitionsMixin:
else:
return [fdc.latest_form for fdc in fdc_list if fdc.latest_form.type == form_type]
def field_param_values_ok(self, widget_name, widget_type, **kwd):
# Make sure required fields have contents, etc
params = util.Params(kwd)
if widget_type == 'AddressField':
if not util.restore_text(params.get('%s_short_desc' % widget_name, '')) \
or not util.restore_text(params.get('%s_name' % widget_name, '')) \
or not util.restore_text(params.get('%s_institution' % widget_name, '')) \
or not util.restore_text(params.get('%s_address' % widget_name, '')) \
or not util.restore_text(params.get('%s_city' % widget_name, '')) \
or not util.restore_text(params.get('%s_state' % widget_name, '')) \
or not util.restore_text(params.get('%s_postal_code' % widget_name, '')) \
or not util.restore_text(params.get('%s_country' % widget_name, '')):
return False
return True
def save_widget_field(self, trans, field_obj, widget_name, **kwd):
# Save a form_builder field object
params = util.Params(kwd)
+10 -527
View File
@@ -4,14 +4,9 @@ Classes for generating HTML forms
from __future__ import print_function
import logging
from cgi import escape
from six import string_types
from galaxy.util import (
asbool,
restore_text,
unicodify
asbool
)
log = logging.getLogger(__name__)
@@ -29,16 +24,6 @@ class BaseField(object):
self.optional = kwds.get('required', 'optional') == 'optional'
self.help = kwds.get('helptext')
def get_html(self, prefix=""):
"""Returns the html widget corresponding to the parameter"""
raise TypeError("Abstract Method")
def get_disabled_str(self, disabled=False):
if disabled:
return ' disabled="disabled"'
else:
return ''
def to_dict(self):
return {
'name' : self.name,
@@ -53,25 +38,7 @@ class BaseField(object):
class TextField(BaseField):
"""
A standard text input box.
>>> print(TextField( "foo" ).get_html())
<input type="text" name="foo" size="10" value="">
>>> print(TextField( "bins", size=4, value="default" ).get_html())
<input type="text" name="bins" size="4" value="default">
"""
def __init__(self, name, size=None, value=None, **kwds):
super(TextField, self).__init__(name, value, **kwds)
self.size = int(size or 10)
def get_html(self, prefix="", disabled=False):
value = unicodify(self.value or "")
return unicodify('<input type="text" name="%s%s" size="%d" value="%s"%s>'
% (prefix, self.name, self.size, escape(value, quote=True), self.get_disabled_str(disabled)))
def set_size(self, size):
self.size = int(size)
def to_dict(self):
d = super(TextField, self).to_dict()
d['type'] = 'text'
@@ -81,26 +48,7 @@ class TextField(BaseField):
class PasswordField(BaseField):
"""
A password input box. text appears as "******"
>>> print(PasswordField( "foo" ).get_html())
<input type="password" name="foo" size="10" value="">
>>> print(PasswordField( "bins", size=4, value="default" ).get_html())
<input type="password" name="bins" size="4" value="default">
"""
def __init__(self, name, size=None, value=None, **kwds):
super(PasswordField, self).__init__(name, value, **kwds)
self.name = name
self.size = int(size or 10)
self.value = value or ""
def get_html(self, prefix="", disabled=False):
return unicodify('<input type="password" name="%s%s" size="%d" value="%s"%s>'
% (prefix, self.name, self.size, escape(str(self.value), quote=True), self.get_disabled_str(disabled)))
def set_size(self, size):
self.size = int(size)
def to_dict(self):
d = super(PasswordField, self).to_dict()
d['type'] = 'password'
@@ -110,31 +58,7 @@ class PasswordField(BaseField):
class TextArea(BaseField):
"""
A standard text area box.
>>> print(TextArea( "foo" ).get_html())
<textarea name="foo" rows="5" cols="25"></textarea>
>>> print(TextArea( "bins", size="4x5", value="default" ).get_html())
<textarea name="bins" rows="4" cols="5">default</textarea>
"""
_DEFAULT_SIZE = "5x25"
def __init__(self, name, size=None, value=None, **kwds):
super(TextArea, self).__init__(name, value, **kwds)
self.name = name
size = size or self._DEFAULT_SIZE
self.size = size.split("x")
self.rows = int(self.size[0])
self.cols = int(self.size[-1])
self.value = value or ""
def get_html(self, prefix="", disabled=False):
return unicodify('<textarea name="%s%s" rows="%d" cols="%d"%s>%s</textarea>'
% (prefix, self.name, self.rows, self.cols, self.get_disabled_str(disabled), escape(str(self.value), quote=True)))
def set_size(self, rows, cols):
self.rows = rows
self.cols = cols
def to_dict(self):
d = super(TextArea, self).to_dict()
d['type'] = 'text'
@@ -145,46 +69,12 @@ class TextArea(BaseField):
class CheckboxField(BaseField):
"""
A checkbox (boolean input)
>>> print(CheckboxField( "foo" ).get_html())
<input type="checkbox" id="foo" name="foo" value="__CHECKED__"><input type="hidden" name="foo" value="__NOTHING__">
>>> print(CheckboxField( "bar", checked="yes" ).get_html())
<input type="checkbox" id="bar" name="bar" value="__CHECKED__" checked="checked"><input type="hidden" name="bar" value="__NOTHING__">
"""
def __init__(self, name, checked=None, refresh_on_change=False, refresh_on_change_values=None, value=None, **kwds):
super(CheckboxField, self).__init__(name, value, **kwds)
self.name = name
self.checked = (checked is True) or (isinstance(checked, string_types) and (checked.lower() in ("yes", "true", "on")))
self.refresh_on_change = refresh_on_change
self.refresh_on_change_values = refresh_on_change_values or []
if self.refresh_on_change:
self.refresh_on_change_text = ' refresh_on_change="true" '
if self.refresh_on_change_values:
self.refresh_on_change_text = '%s refresh_on_change_values="%s" ' % (self.refresh_on_change_text, ",".join(self.refresh_on_change_values))
else:
self.refresh_on_change_text = ''
def get_html(self, prefix="", disabled=False):
if self.checked:
checked_text = ' checked="checked"'
else:
checked_text = ''
id_name = prefix + self.name
return unicodify('<input type="checkbox" id="%s" name="%s" value="__CHECKED__"%s%s%s><input type="hidden" name="%s" value="__NOTHING__"%s>'
% (id_name, id_name, checked_text, self.get_disabled_str(disabled), self.refresh_on_change_text, id_name, self.get_disabled_str(disabled)))
@staticmethod
def is_checked(value):
if value in [True, "true"]:
if value in [True, "True", "true"]:
return True
return isinstance(value, list) and ('__CHECKED__' in value or len(value) == 2)
def set_checked(self, value):
if isinstance(value, string_types):
self.checked = value.lower() in ["yes", "true", "on"]
else:
self.checked = value
return False
def to_dict(self):
d = super(CheckboxField, self).to_dict()
@@ -192,122 +82,17 @@ class CheckboxField(BaseField):
return d
class FileField(BaseField):
"""
A file upload input.
>>> print(FileField( "foo" ).get_html())
<input type="file" name="foo">
>>> print(FileField( "foo", ajax = True ).get_html())
<input type="file" name="foo" galaxy-ajax-upload="true">
"""
def __init__(self, name, value=None, ajax=False, **kwds):
super(FileField, self).__init__(name, value, **kwds)
self.name = name
self.ajax = ajax
self.value = value
def get_html(self, prefix=""):
value_text = ""
if self.value:
value_text = ' value="%s"' % escape(str(self.value), quote=True)
ajax_text = ""
if self.ajax:
ajax_text = ' galaxy-ajax-upload="true"'
return unicodify('<input type="file" name="%s%s"%s%s>' % (prefix, self.name, ajax_text, value_text))
class GenomespaceFileField(BaseField):
"""
A genomspace file browser field.
"""
def __init__(self, name, value=None):
self.name = name
self.value = value or ""
def get_html(self, prefix=""):
return unicodify('<script src="https://gsui.genomespace.org/jsui/upload/gsuploadwindow.js"></script>'
'<input type="text" name="{0}{1}" value="{2}">&nbsp;'
'<a href="javascript:gsLocationByGet({{ successCallback: function(config)'
' {{ selector_name = \'{0}{1}\'; selector = \'input[name=\' + selector_name.replace(\'|\', \'\\\\|\') + \']\';'
' $(selector).val(config.destination + \'^\' + config.token); }} }});">'
'Browse</a>'.format(prefix, self.name, escape(str(self.value), quote=True)))
def to_dict(self):
return dict(name=self.name,
token_field=self.token_field)
class HiddenField(BaseField):
"""
A hidden field.
>>> print(HiddenField( "foo", 100 ).get_html())
<input type="hidden" name="foo" value="100">
"""
def __init__(self, name, value=None, **kwds):
super(HiddenField, self).__init__(name, value, **kwds)
self.name = name
self.value = value or ""
def get_html(self, prefix=""):
return unicodify('<input type="hidden" name="%s%s" value="%s">' % (prefix, self.name, escape(str(self.value), quote=True)))
def to_dict(self):
d = super(HiddenField, self).to_dict()
d['type'] = 'hidden'
d['hidden'] = True
return d
class SelectField(BaseField):
"""
A select field.
>>> t = SelectField( "foo", multiple=True )
>>> t.add_option( "tuti", 1 )
>>> t.add_option( "fruity", "x" )
>>> print(t.get_html())
<select name="foo" multiple>
<option value="1">tuti</option>
<option value="x">fruity</option>
</select>
>>> t = SelectField( "bar" )
>>> t.add_option( "automatic", 3 )
>>> t.add_option( "bazooty", 4, selected=True )
>>> print(t.get_html())
<select name="bar" last_selected_value="4">
<option value="3">automatic</option>
<option value="4" selected>bazooty</option>
</select>
>>> t = SelectField( "foo", display="radio" )
>>> t.add_option( "tuti", 1 )
>>> t.add_option( "fruity", "x" )
>>> print(t.get_html())
<div><input type="radio" name="foo" value="1" id="foo|1"><label class="inline" for="foo|1">tuti</label></div>
<div><input type="radio" name="foo" value="x" id="foo|x"><label class="inline" for="foo|x">fruity</label></div>
>>> t = SelectField( "bar", multiple=True, display="checkboxes" )
>>> t.add_option( "automatic", 3 )
>>> t.add_option( "bazooty", 4, selected=True )
>>> print(t.get_html())
<div class="checkUncheckAllPlaceholder" checkbox_name="bar"></div>
<div><input type="checkbox" name="bar" value="3" id="bar|3"><label class="inline" for="bar|3">automatic</label></div>
<div><input type="checkbox" name="bar" value="4" id="bar|4" checked='checked'><label class="inline" for="bar|4">bazooty</label></div>
"""
def __init__(self, name, multiple=None, display=None, refresh_on_change=False, refresh_on_change_values=None, size=None, field_id=None, value=None, selectlist=None, **kwds):
def __init__(self, name, multiple=None, display=None, field_id=None, value=None, selectlist=None, refresh_on_change=False, **kwds):
super(SelectField, self).__init__(name, value, **kwds)
self.name = name
self.field_id = field_id
self.multiple = multiple or False
self.refresh_on_change = refresh_on_change
self.selectlist = selectlist or []
self.value = value
self.size = size
self.options = list()
if display == "checkboxes":
assert multiple, "Checkbox display only supported for multiple select"
@@ -316,136 +101,10 @@ class SelectField(BaseField):
elif display is not None:
raise Exception("Unknown display type: %s" % display)
self.display = display
self.refresh_on_change = refresh_on_change
self.refresh_on_change_values = refresh_on_change_values or []
if self.refresh_on_change:
self.refresh_on_change_text = ' refresh_on_change="true"'
if self.refresh_on_change_values:
self.refresh_on_change_text = '%s refresh_on_change_values="%s"' % (self.refresh_on_change_text, escape(",".join(self.refresh_on_change_values), quote=True))
else:
self.refresh_on_change_text = ''
def add_option(self, text, value, selected=False):
self.options.append((text, value, selected))
def get_html(self, prefix="", disabled=False, extra_attr=None):
if extra_attr is not None:
self.extra_attributes = ' %s' % ' '.join(['%s="%s"' % (k, escape(v)) for k, v in extra_attr.items()])
else:
self.extra_attributes = ''
if self.display == "checkboxes":
return self.get_html_checkboxes(prefix, disabled)
elif self.display == "radio":
return self.get_html_radio(prefix, disabled)
else:
return self.get_html_default(prefix, disabled)
def get_html_checkboxes(self, prefix="", disabled=False):
rval = []
ctr = 0
if len(self.options) > 1:
rval.append('<div class="checkUncheckAllPlaceholder" checkbox_name="%s%s"></div>' % (prefix, self.name)) # placeholder for the insertion of the Select All/Unselect All buttons
for text, value, selected in self.options:
style = ""
text = unicodify(text)
escaped_value = escape(unicodify(value), quote=True)
uniq_id = "%s%s|%s" % (prefix, self.name, escaped_value)
if len(self.options) > 2 and ctr % 2 == 1:
style = " class=\"odd_row\""
selected_text = ""
if selected:
selected_text = " checked='checked'"
rval.append('<div%s><input type="checkbox" name="%s%s" value="%s" id="%s"%s%s%s><label class="inline" for="%s">%s</label></div>'
% (style, prefix, self.name, escaped_value, uniq_id, selected_text, self.get_disabled_str(disabled), self.extra_attributes, uniq_id, escape(text, quote=True)))
ctr += 1
return unicodify("\n".join(rval))
def get_html_radio(self, prefix="", disabled=False):
rval = []
ctr = 0
for text, value, selected in self.options:
style = ""
escaped_value = escape(str(value), quote=True)
uniq_id = "%s%s|%s" % (prefix, self.name, escaped_value)
if len(self.options) > 2 and ctr % 2 == 1:
style = " class=\"odd_row\""
selected_text = ""
if selected:
selected_text = " checked='checked'"
rval.append('<div%s><input type="radio" name="%s%s"%s value="%s" id="%s"%s%s%s><label class="inline" for="%s">%s</label></div>'
% (style,
prefix,
self.name,
self.refresh_on_change_text,
escaped_value,
uniq_id,
selected_text,
self.get_disabled_str(disabled),
self.extra_attributes,
uniq_id,
text))
ctr += 1
return unicodify("\n".join(rval))
def get_html_default(self, prefix="", disabled=False):
if self.multiple:
multiple = " multiple"
else:
multiple = ""
if self.size:
size = ' size="%s"' % str(self.size)
else:
size = ''
rval = []
last_selected_value = ""
for text, value, selected in self.options:
if selected:
selected_text = " selected"
last_selected_value = value
if not isinstance(last_selected_value, string_types):
last_selected_value = str(last_selected_value)
else:
selected_text = ""
rval.append('<option value="%s"%s>%s</option>' % (escape(unicodify(value), quote=True), selected_text, escape(unicodify(text), quote=True)))
if last_selected_value:
last_selected_value = ' last_selected_value="%s"' % escape(unicodify(last_selected_value), quote=True)
if self.field_id is not None:
id_string = ' id="%s"' % self.field_id
else:
id_string = ''
rval.insert(0, '<select name="%s%s"%s%s%s%s%s%s%s>'
% (prefix, self.name, multiple, size, self.refresh_on_change_text, last_selected_value, self.get_disabled_str(disabled), id_string, self.extra_attributes))
rval.append('</select>')
return unicodify("\n".join(rval))
def get_selected(self, return_label=False, return_value=False, multi=False):
'''
Return the currently selected option's label, value or both as a tuple. For
multi-select lists, a list is returned.
'''
if multi:
selected_options = []
for label, value, selected in self.options:
if selected:
if return_label and return_value:
if multi:
selected_options.append((label, value))
else:
return (label, value)
elif return_label:
if multi:
selected_options.append(label)
else:
return label
elif return_value:
if multi:
selected_options.append(value)
else:
return value
if multi:
return selected_options
return None
def to_dict(self):
d = super(SelectField, self).to_dict()
d['type'] = 'select'
@@ -470,63 +129,10 @@ class AddressField(BaseField):
("country", "Country", ""),
("phone", "Phone", "")]
def __init__(self, name, user=None, value=None, params=None, security=None, **kwds):
def __init__(self, name, user=None, value=None, security=None, **kwds):
super(AddressField, self).__init__(name, value, **kwds)
self.user = user
self.security = security
self.select_address = None
self.params = params
def get_html(self, disabled=False):
address_html = ''
add_ids = ['none']
if self.user:
for a in self.user.addresses:
add_ids.append(str(a.id))
add_ids.append('new')
self.select_address = SelectField(self.name,
refresh_on_change=True,
refresh_on_change_values=add_ids)
if self.value == 'none':
self.select_address.add_option('Select one', 'none', selected=True)
else:
self.select_address.add_option('Select one', 'none')
if self.user:
for a in self.user.addresses:
if not a.deleted:
if self.value == str(a.id):
self.select_address.add_option(a.desc, str(a.id), selected=True)
# Display this address
address_html += '''
<div class="form-row">
%s
</div>
''' % a.get_html()
else:
self.select_address.add_option(a.desc, str(a.id))
if self.value == 'new':
self.select_address.add_option('Add a new address', 'new', selected=True)
for field_name, label, help_text in self.fields():
add_field = TextField(self.name + '_' + field_name,
40,
restore_text(self.params.get(self.name + '_' + field_name, '')))
address_html += '''
<div class="form-row">
<label>%s</label>
%s
''' % (label, add_field.get_html(disabled=disabled))
if help_text:
address_html += '''
<div class="toolParamHelp" style="clear: both;">
%s
</div>
''' % help_text
address_html += '''
</div>
'''
else:
self.select_address.add_option('Add a new address', 'new')
return self.select_address.get_html(disabled=disabled) + address_html
def to_dict(self):
d = super(AddressField, self).to_dict()
@@ -540,29 +146,11 @@ class AddressField(BaseField):
class WorkflowField(BaseField):
def __init__(self, name, user=None, value=None, params=None, security=None, **kwds):
def __init__(self, name, user=None, value=None, security=None, **kwds):
super(WorkflowField, self).__init__(name, value, **kwds)
self.name = name
self.user = user
self.value = value
self.security = security
self.select_workflow = None
self.params = params
def get_html(self, disabled=False):
self.select_workflow = SelectField(self.name)
if self.value == 'none':
self.select_workflow.add_option('Select one', 'none', selected=True)
else:
self.select_workflow.add_option('Select one', 'none')
if self.user:
for a in self.user.stored_workflows:
if not a.deleted:
if str(self.value) == str(a.id):
self.select_workflow.add_option(a.name, str(a.id), selected=True)
else:
self.select_workflow.add_option(a.name, str(a.id))
return self.select_workflow.get_html(disabled=disabled)
def to_dict(self):
d = super(WorkflowField, self).to_dict()
@@ -576,70 +164,17 @@ class WorkflowField(BaseField):
class WorkflowMappingField(BaseField):
def __init__(self, name, user=None, value=None, params=None, **kwd):
# DBTODO integrate this with the new __build_workflow approach in requests_common. As it is, not particularly useful.
self.name = name
def __init__(self, name, user=None, value=None, **kwds):
super(WorkflowMappingField, self).__init__(name, value, **kwds)
self.user = user
self.value = value
self.select_workflow = None
self.params = params
self.workflow_inputs = []
def get_html(self, disabled=False):
self.select_workflow = SelectField(self.name, refresh_on_change=True)
workflow_inputs = []
if self.value == 'none':
self.select_workflow.add_option('Select one', 'none', selected=True)
else:
self.select_workflow.add_option('Select one', 'none')
if self.user:
for a in self.user.stored_workflows:
if not a.deleted:
if str(self.value) == str(a.id):
self.select_workflow.add_option(a.name, str(a.id), selected=True)
else:
self.select_workflow.add_option(a.name, str(a.id))
if self.value and self.value != 'none':
# Workflow selected. Find all inputs.
for workflow in self.user.stored_workflows:
if workflow.id == int(self.value):
for step in workflow.latest_workflow.steps:
if step.type == 'data_input':
if step.tool_inputs and "name" in step.tool_inputs:
workflow_inputs.append((step.tool_inputs['name'], TextField('%s_%s' % (self.name, step.id), 20)))
# Do something more appropriate here and allow selection of inputs
return self.select_workflow.get_html(disabled=disabled) + ''.join(['<div class="form-row"><label>%s</label>%s</div>' % (s[0], s[1].get_html()) for s in workflow_inputs])
class HistoryField(BaseField):
def __init__(self, name, user=None, value=None, params=None, security=None, **kwds):
def __init__(self, name, user=None, value=None, security=None, **kwds):
super(HistoryField, self).__init__(name, value, **kwds)
self.name = name
self.user = user
self.value = value
self.security = security
self.select_history = None
self.params = params
def get_html(self, disabled=False):
self.select_history = SelectField(self.name)
if self.value == 'none':
self.select_history.add_option('No Import', 'none', selected=True)
self.select_history.add_option('New History', 'new')
else:
self.select_history.add_option('No Import', 'none')
if self.value == 'new':
self.select_history.add_option('New History', 'new', selected=True)
else:
self.select_history.add_option('New History', 'new')
if self.user:
for a in self.user.histories:
if not a.deleted:
if str(self.value) == str(a.id):
self.select_history.add_option(a.name, str(a.id), selected=True)
else:
self.select_history.add_option(a.name, str(a.id))
return self.select_history.get_html(disabled=disabled)
def to_dict(self):
d = super(HistoryField, self).to_dict()
@@ -657,55 +192,3 @@ def get_suite():
import doctest
import sys
return doctest.DocTestSuite(sys.modules[__name__])
# --------- Utility methods -----------------------------
def build_select_field(trans, objs, label_attr, select_field_name, initial_value='none',
selected_value='none', refresh_on_change=False, multiple=False, display=None, size=None):
"""
Build a SelectField given a set of objects. The received params are:
- objs: the set of objects used to populate the option list
- label_attr: the attribute of each obj (e.g., name, email, etc ) whose value is used to populate each option label.
- If the string 'self' is passed as label_attr, each obj in objs is assumed to be a string, so the obj itself is used
- select_field_name: the name of the SelectField
- initial_value: the value of the first option in the SelectField - allows for an option telling the user to select something
- selected_value: the value of the currently selected option
- refresh_on_change: True if the SelectField should perform a refresh_on_change
"""
if initial_value == 'none':
values = [initial_value]
else:
values = []
for obj in objs:
if label_attr == 'self':
# Each obj is a string
values.append(obj)
else:
values.append(trans.security.encode_id(obj.id))
if refresh_on_change:
refresh_on_change_values = values
else:
refresh_on_change_values = []
select_field = SelectField(name=select_field_name,
multiple=multiple,
display=display,
refresh_on_change=refresh_on_change,
refresh_on_change_values=refresh_on_change_values,
size=size)
for obj in objs:
if label_attr == 'self':
# Each obj is a string
if str(selected_value) == str(obj):
select_field.add_option(obj, obj, selected=True)
else:
select_field.add_option(obj, obj)
else:
label = getattr(obj, label_attr)
if str(selected_value) == str(obj.id) or str(selected_value) == trans.security.encode_id(obj.id):
select_field.add_option(label, trans.security.encode_id(obj.id), selected=True)
else:
select_field.add_option(label, trans.security.encode_id(obj.id))
return select_field
-91
View File
@@ -1,91 +0,0 @@
import logging
from galaxy.util import bunch
log = logging.getLogger(__name__)
def form(*args, **kwargs):
return FormBuilder(*args, **kwargs)
class FormBuilder(object):
"""
Simple class describing an HTML form
"""
def __init__(self, action="", title="", name="form", submit_text="submit", use_panels=False):
self.title = title
self.name = name
self.action = action
self.submit_text = submit_text
self.inputs = []
self.use_panels = use_panels
def add_input(self, type, name, label, value=None, error=None, help=None, use_label=True):
self.inputs.append(FormInput(type, label, name, value, error, help, use_label))
return self
def add_checkbox(self, name, label, value=None, error=None, help=None):
return self.add_input('checkbox', label, name, value, error, help)
def add_text(self, name, label, value=None, error=None, help=None):
return self.add_input('text', label, name, value, error, help)
def add_password(self, name, label, value=None, error=None, help=None):
return self.add_input('password', label, name, value, error, help)
def add_select(self, name, label, value=None, options=[], error=None, help=None, use_label=True):
self.inputs.append(SelectInput(name, label, value=value, options=options, error=error, help=help, use_label=use_label))
return self
class FormInput(object):
"""
Simple class describing a form input element
"""
def __init__(self, type, name, label, value=None, error=None, help=None, use_label=True, extra_attributes={}, **kwargs):
self.type = type
self.name = name
self.label = label
self.value = value
self.error = error
self.help = help
self.use_label = use_label
self.extra_attributes = extra_attributes
class DatalistInput(FormInput):
""" Data list input """
def __init__(self, name, *args, **kwargs):
if 'extra_attributes' not in kwargs:
kwargs['extra_attributes'] = {}
kwargs['extra_attributes']['list'] = name
FormInput.__init__(self, None, name, *args, **kwargs)
self.options = kwargs.get('options', {})
def body_html(self):
options = "".join(["<option value='%s'>%s</option>" % (key, value) for key, value in self.options.iteritems()])
return """<datalist id="%s">%s</datalist>""" % (self.name, options)
class SelectInput(FormInput):
""" A select form input. """
def __init__(self, name, label, value=None, options=[], error=None, help=None, use_label=True):
FormInput.__init__(self, "select", name, label, value=value, error=error, help=help, use_label=use_label)
self.options = options
class FormData(object):
"""
Class for passing data about a form to a template, very rudimentary, could
be combined with the tool form handling to build something more general.
"""
def __init__(self):
# TODO: galaxy's two Bunchs are defined differently. Is this right?
self.values = bunch.Bunch()
self.errors = bunch.Bunch()
+1 -61
View File
@@ -10,7 +10,6 @@ from galaxy.model.item_attrs import RuntimeException, UsesAnnotations, UsesItemR
from galaxy.util import restore_text, sanitize_text, unicodify
from galaxy.util.odict import odict
from galaxy.web.framework import decorators, url_for
from galaxy.web.framework.helpers import iff
log = logging.getLogger(__name__)
@@ -21,12 +20,8 @@ class Grid(object):
Specifies the content and format of a grid (data table).
"""
title = ""
exposed = True
model_class = None
show_item_checkboxes = False
template = "grid_base.mako"
async_template = "grid_base_async.mako"
use_async = False
use_hide_message = True
global_actions = []
columns = []
@@ -35,14 +30,12 @@ class Grid(object):
# Any columns that are filterable (either standard or advanced) should have a default value set in the default filter.
default_filter = {}
default_sort_key = None
preserve_state = False
use_paging = False
num_rows_per_page = 25
num_page_links = 10
# Set preference names.
cur_filter_pref_name = ".filter"
cur_sort_key_pref_name = ".sort_key"
pass_through_operations = {}
legend = None
info_text = None
@@ -65,7 +58,6 @@ class Grid(object):
# FIXME: pretty sure this is only here to pass along, can likely be eliminated
status = kwargs.get('status', None)
message = kwargs.get('message', None)
dict_format = kwargs.get('dict_format', False)
# Build a base filter and sort key that is the combination of the saved state and defaults.
# Saved state takes preference over defaults.
base_filter = {}
@@ -73,14 +65,6 @@ class Grid(object):
# default_filter is a dictionary that provides a default set of filters based on the grid's columns.
base_filter = self.default_filter.copy()
base_sort_key = self.default_sort_key
if self.preserve_state:
pref_name = text_type(self.__class__.__name__ + self.cur_filter_pref_name)
if pref_name in trans.get_user().preferences:
saved_filter = loads(trans.get_user().preferences[pref_name])
base_filter.update(saved_filter)
pref_name = text_type(self.__class__.__name__ + self.cur_sort_key_pref_name)
if pref_name in trans.get_user().preferences:
base_sort_key = loads(trans.get_user().preferences[pref_name])
# Build initial query
query = self.build_initial_query(trans, **kwargs)
query = self.apply_query_filter(trans, query, **kwargs)
@@ -228,26 +212,11 @@ class Grid(object):
# There are some places in grid templates where it's useful for a grid
# to have its current filter.
self.cur_filter_dict = cur_filter_dict
# Preserve grid state: save current filter and sort key.
if self.preserve_state:
pref_name = text_type(self.__class__.__name__ + self.cur_filter_pref_name)
trans.get_user().preferences[pref_name] = text_type(dumps(cur_filter_dict))
if sort_key:
pref_name = text_type(self.__class__.__name__ + self.cur_sort_key_pref_name)
trans.get_user().preferences[pref_name] = text_type(dumps(sort_key))
trans.sa_session.flush()
# Log grid view.
context = text_type(self.__class__.__name__)
params = cur_filter_dict.copy()
params['sort'] = sort_key
params['async'] = ('async' in kwargs)
# TODO:??
# commenting this out; when this fn calls session.add( action ) and session.flush the query from this fn
# is effectively 'wiped' out. Nate believes it has something to do with our use of session( autocommit=True )
# in mapping.py. If you change that to False, the log_action doesn't affect the query
# Below, I'm rendering the template first (that uses query), then calling log_action, then returning the page
# trans.log_action( trans.get_user(), text_type( "grid.view" ), context, params )
# Render grid.
def url(*args, **kwargs):
@@ -280,43 +249,14 @@ class Grid(object):
self.use_panels = (kwargs.get('use_panels', False) in [True, 'True', 'true'])
self.advanced_search = (kwargs.get('advanced_search', False) in [True, 'True', 'true'])
async_request = ((self.use_async) and (kwargs.get('async', False) in [True, 'True', 'true']))
# Currently, filling the template returns a str object; this requires decoding the string into a
# unicode object within mako templates. What probably should be done is to return the template as
# utf-8 unicode; however, this would require encoding the object as utf-8 before returning the grid
# results via a controller method, which is require substantial changes. Hence, for now, return grid
# as str.
if not dict_format:
page = trans.fill_template(iff(async_request, self.async_template, self.template),
grid=self,
query=query,
cur_page_num=page_num,
num_pages=num_pages,
num_page_links=self.num_page_links,
default_filter_dict=self.default_filter,
cur_filter_dict=cur_filter_dict,
sort_key=sort_key,
current_item=current_item,
ids=kwargs.get('id', []),
url=url,
status=status,
message=message,
info_text=self.info_text,
use_panels=self.use_panels,
use_hide_message=self.use_hide_message,
advanced_search=self.advanced_search,
show_item_checkboxes=(self.show_item_checkboxes or
kwargs.get('show_item_checkboxes', '') in ['True', 'true']),
# Pass back kwargs so that grid template can set and use args without
# grid explicitly having to pass them.
kwargs=kwargs)
trans.log_action(trans.get_user(), text_type("grid.view"), context, params)
return page
grid_config = {
'title' : self.title,
'url_base' : trans.request.path_url,
'async' : self.use_async,
'async_ops' : [],
'categorical_filters' : {},
'filters' : cur_filter_dict,
+1 -1
View File
@@ -23,10 +23,10 @@ import io
import json
import logging
import re
from urlparse import urlparse
import routes
from paste import httpexceptions
from six.moves.urllib.parse import urlparse
log = logging.getLogger(__name__)
@@ -149,7 +149,7 @@ def get_func_list(stats, sel_list):
list = stats.fcn_list[:]
order_message = "Ordered by: " + stats.sort_type
else:
list = stats.stats.keys()
list = list(stats.stats.keys())
order_message = "Random listing order was used"
# Do the selection and accumulate messages
select_message = ""
@@ -192,7 +192,7 @@ class RemoteUser(object):
return self.app(environ, start_response)
else:
log.debug("Unable to identify user. %s not found" % self.remote_user_header)
for k, v in environ.iteritems():
for k, v in environ.items():
log.debug("%s = %s", k, v)
title = "Access to Galaxy is denied"
@@ -5,9 +5,9 @@ Middleware for logging requests, using Apache combined log format
"""
import logging
import time
import urllib
from six import string_types
from six.moves.urllib.parse import quote
class TransLogger(object):
@@ -52,8 +52,8 @@ class TransLogger(object):
def __call__(self, environ, start_response):
start = time.localtime()
req_uri = urllib.quote(environ.get('SCRIPT_NAME', '') +
environ.get('PATH_INFO', ''))
req_uri = quote(environ.get('SCRIPT_NAME', '') +
environ.get('PATH_INFO', ''))
if environ.get('QUERY_STRING'):
req_uri += '?' + environ['QUERY_STRING']
method = environ['REQUEST_METHOD']
-12
View File
@@ -33,7 +33,6 @@ from galaxy.util import (
from galaxy.util.sanitize_html import sanitize_html
from galaxy.web.framework import (
base,
formbuilder,
helpers,
url_for
)
@@ -103,8 +102,6 @@ class WebApplication(base.WebApplication):
return trans.show_message(sanitize_html(e.err_msg), e.type)
def make_body_iterable(self, trans, body):
if isinstance(body, formbuilder.FormBuilder):
body = trans.show_form(body)
return base.WebApplication.make_body_iterable(self, trans, body)
def transaction_chooser(self, environ, galaxy_app, session_cookie):
@@ -875,15 +872,6 @@ class GalaxyWebTransaction(base.DefaultWebTransaction,
"""
return self.show_message(message, 'warning', refresh_frames, use_panels=use_panels, active_view=active_view)
def show_form(self, form, header=None, template="form.mako", use_panels=False, active_view=""):
"""
Convenience method for displaying a simple page with a single HTML
form.
"""
return self.fill_template(template, form=form, header=header,
use_panels=(form.use_panels or use_panels),
active_view=active_view)
@property
def session_csrf_token(self):
token = ''
+1 -1
View File
@@ -98,7 +98,7 @@ class SecurityHelper(object):
if recursive and isinstance(v, dict):
rval[k] = self.encode_all_ids(v, recursive)
elif recursive and isinstance(v, list):
rval[k] = map(lambda el: self.encode_all_ids(el, True), v)
rval[k] = [self.encode_all_ids(el, True) for el in v]
return rval
def decode_id(self, obj_id, kind=None):
+2 -2
View File
@@ -370,8 +370,8 @@ def _do_uwsgi_postfork():
def _mule_fixup():
import urllib2
urllib2._opener = None
from six.moves.urllib.request import install_opener
install_opener(None)
if uwsgi:
+2 -2
View File
@@ -76,7 +76,7 @@ class UWSGIFarmMessageTransport(ApplicationStackTransport):
if num < need:
raise RuntimeError('Need %i uWSGI locks but only %i exist(s): Set `locks = %i` in uWSGI configuration' % (need, num, need - 1))
sys.exit(1)
self._locks.extend(map(lambda x: 'RECV_MSG_FARM_' + x, farms))
self._locks.extend(['RECV_MSG_FARM_' + x for x in farms])
# this would be nice, but in my 2.0.15 uWSGI, the uwsgi module has no set_option function, and I don't know if it'd work even if the function existed as documented
# if len(self.lock_map) > 1:
# uwsgi.set_option('locks', len(self.lock_map))
@@ -136,7 +136,7 @@ class UWSGIFarmMessageTransport(ApplicationStackTransport):
if not uwsgi.in_farm():
raise RuntimeError('Mule %s is not in a farm! Set `farm = <pool_name>:%s` in uWSGI configuration'
% (uwsgi.mule_id(),
','.join(map(str, range(1, len(filter(lambda x: x.endswith('galaxy/main.py'), self.stack._configured_mules)) + 1)))))
','.join(map(str, range(1, len([x for x in self.stack._configured_mules if x.endswith('galaxy/main.py')]) + 1)))))
elif len(self.stack._farms) > 1:
raise RuntimeError('Mule %s is in multiple farms! This configuration is not supported due to locking issues' % uwsgi.mule_id())
# only mules receive messages so don't bother starting the dispatcher if we're not a mule (although
+3 -4
View File
@@ -1,5 +1,4 @@
from __future__ import absolute_import
from __future__ import print_function
from __future__ import absolute_import, print_function
import argparse
import copy
@@ -268,7 +267,7 @@ def main(argv=None):
def _arg_parser():
parser = argparse.ArgumentParser(description=DESCRIPTION)
parser.add_argument('action', metavar='ACTION', type=str,
choices=ACTIONS.keys(),
choices=list(ACTIONS.keys()),
help='action to perform')
parser.add_argument('app', metavar='APP', type=str, nargs="?",
help=APP_DESCRIPTION)
@@ -691,7 +690,7 @@ def _ordered_dump(data, stream=None, Dumper=yaml.Dumper, **kwds):
def _dict_representer(dumper, data):
return dumper.represent_mapping(
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
data.items())
list(data.items()))
OrderedDumper.add_representer(OrderedDict, _dict_representer)
return yaml.dump(data, stream, OrderedDumper, **kwds)
@@ -12,7 +12,8 @@ Returns:
"""
import logging
from base64 import b64decode
from urllib import unquote
from six.moves.urllib.parse import unquote
from galaxy import exceptions
from galaxy.managers import api_keys
@@ -88,7 +88,7 @@ class ConfigurationController(BaseAPIController):
@require_admin
def dynamic_tool_confs(self, trans):
confs = self.app.toolbox.dynamic_confs(include_migrated_tool_conf=True)
return map(_tool_conf_to_dict, confs)
return list(map(_tool_conf_to_dict, confs))
@expose_api
@require_admin
+1 -1
View File
@@ -383,5 +383,5 @@ class DatasetsController(BaseAPIController, UsesVisualizationMixin):
return converted
except model.NoConverterException:
exc_data = dict(source=original.ext, target=target_ext, available=original.get_converter_types().keys())
exc_data = dict(source=original.ext, target=target_ext, available=list(original.get_converter_types().keys()))
raise galaxy_exceptions.RequestParameterInvalidException('Conversion not possible', **exc_data)
+3 -3
View File
@@ -42,7 +42,7 @@ class DatatypesController(BaseAPIController):
if extension in datatypes_registry.datatypes_by_extension:
composite_files = datatypes_registry.datatypes_by_extension[extension].composite_files
if composite_files:
dictionary['composite_files'] = [_.dict() for _ in composite_files.itervalues()]
dictionary['composite_files'] = [_.dict() for _ in composite_files.values()]
rval.append(dictionary)
return rval
except Exception as exception:
@@ -61,7 +61,7 @@ class DatatypesController(BaseAPIController):
try:
ext_to_class_name = dict()
classes = []
for k, v in self._datatypes_registry.datatypes_by_extension.iteritems():
for k, v in self._datatypes_registry.datatypes_by_extension.items():
c = v.__class__
ext_to_class_name[k] = c.__module__ + "." + c.__name__
classes.append(c)
@@ -109,7 +109,7 @@ class DatatypesController(BaseAPIController):
@expose_api_anonymous_and_sessionless
def converters(self, trans, **kwd):
converters = []
for (source_type, targets) in self._datatypes_registry.datatype_converters.iteritems():
for (source_type, targets) in self._datatypes_registry.datatype_converters.items():
for target_type in targets:
converters.append({
'source': source_type,
@@ -92,7 +92,7 @@ class FolderContentsController(BaseAPIController, UsesLibraryMixin, UsesLibraryM
if content_item.description:
return_item.update(dict(description=content_item.description))
if content_item.api_type == 'file':
elif content_item.api_type == 'file':
# Is the dataset public or private?
# When both are False the dataset is 'restricted'
# Access rights are checked on the dataset level, not on the ld or ldda level to maintain consistency
@@ -96,7 +96,7 @@ class HistoryContentsController(BaseAPIController, UsesLibraryMixin, UsesLibrary
contents_kwds = {'types': types}
if ids:
ids = map(lambda id: self.decode_id(id), ids.split(','))
ids = [self.decode_id(id) for id in ids.split(',')]
contents_kwds['ids'] = ids
# If explicit ids given, always used detailed result.
details = 'all'
@@ -209,7 +209,7 @@ class HistoryContentsController(BaseAPIController, UsesLibraryMixin, UsesLibrary
else:
ids = util.listify(ids)
types = util.listify(types)
return map(lambda s: self.encode_all_ids(trans, s), fetch_job_states(self.app, trans.sa_session, ids, types))
return [self.encode_all_ids(trans, s) for s in fetch_job_states(self.app, trans.sa_session, ids, types)]
@expose_api_anonymous
def show_jobs_summary(self, trans, id, history_id, **kwd):
+2 -2
View File
@@ -231,7 +231,7 @@ class JobController(BaseAPIController, UsesLibraryMixinItems):
def __dictify_associations(self, trans, *association_lists):
rval = []
for association_list in association_lists:
rval.extend(map(lambda a: self.__dictify_association(trans, a), association_list))
rval.extend(self.__dictify_association(trans, a) for a in association_list)
return rval
def __dictify_association(self, trans, job_dataset_association):
@@ -293,7 +293,7 @@ class JobController(BaseAPIController, UsesLibraryMixinItems):
raise exceptions.ObjectAttributeMissingException("No inputs defined")
inputs = payload.get('inputs', {})
# Find files coming in as multipart file data and add to inputs.
for k, v in payload.iteritems():
for k, v in payload.items():
if k.startswith('files_') or k.startswith('__files_'):
inputs[k] = v
request_context = WorkRequestContext(app=trans.app, user=trans.user, history=trans.history)
@@ -32,10 +32,6 @@ from galaxy.web.base.controller import (
UsesLibraryMixin,
UsesLibraryMixinItems
)
from galaxy.web.form_builder import (
AddressField,
CheckboxField,
)
log = logging.getLogger(__name__)
@@ -300,18 +296,12 @@ class LibraryContentsController(BaseAPIController, UsesLibraryMixin, UsesLibrary
roles = kwd.get('roles', '')
is_admin = trans.user_is_admin()
current_user_roles = trans.get_current_user_roles()
widgets = []
info_association, inherited = None, None
template_id = "None"
if replace_id not in ['', None, 'None']:
replace_dataset = trans.sa_session.query(trans.app.model.LibraryDataset).get(trans.security.decode_id(replace_id))
self._check_access(trans, is_admin, replace_dataset, current_user_roles)
self._check_modify(trans, is_admin, replace_dataset, current_user_roles)
library = replace_dataset.folder.parent_library
folder = replace_dataset.folder
info_association, inherited = replace_dataset.library_dataset_dataset_association.get_info_association()
if info_association and (not(inherited) or info_association.inheritable):
widgets = replace_dataset.library_dataset_dataset_association.get_template_widgets(trans)
# The name is stored - by the time the new ldda is created, replace_dataset.name
# will point to the new ldda, not the one it's replacing.
if not last_used_build:
@@ -342,56 +332,9 @@ class LibraryContentsController(BaseAPIController, UsesLibraryMixin, UsesLibrary
if error:
return 400, message
else:
# See if we have any inherited templates.
if not info_association:
info_association, inherited = folder.get_info_association(inherited=True)
if info_association and info_association.inheritable:
template_id = str(info_association.template.id)
widgets = folder.get_template_widgets(trans, get_contents=True)
processed_widgets = []
# The list of widgets may include an AddressField which we need to save if it is new
for index, widget_dict in enumerate(widgets):
widget = widget_dict['widget']
if isinstance(widget, AddressField):
value = kwd.get(widget.name, '')
if value == 'new':
if self.field_param_values_ok(widget.name, 'AddressField', **kwd):
# Save the new address
address = trans.app.model.UserAddress(user=trans.user)
self.save_widget_field(trans, address, widget.name, **kwd)
widget.value = str(address.id)
widget_dict['widget'] = widget
processed_widgets.append(widget_dict)
# It is now critical to update the value of 'field_%i', replacing the string
# 'new' with the new address id. This is necessary because the upload_dataset()
# method below calls the handle_library_params() method, which does not parse the
# widget fields, it instead pulls form values from kwd. See the FIXME comments in the
# handle_library_params() method, and the CheckboxField code in the next conditional.
kwd[widget.name] = str(address.id)
else:
# The invalid address won't be saved, but we cannot display error
# messages on the upload form due to the ajax upload already occurring.
# When we re-engineer the upload process ( currently under way ), we
# will be able to check the form values before the ajax upload occurs
# in the background. For now, we'll do nothing...
pass
elif isinstance(widget, CheckboxField):
# We need to check the value from kwd since util.Params would have munged the list if
# the checkbox is checked.
value = kwd.get(widget.name, '')
if CheckboxField.is_checked(value):
widget.value = 'true'
widget_dict['widget'] = widget
processed_widgets.append(widget_dict)
kwd[widget.name] = 'true'
else:
processed_widgets.append(widget_dict)
widgets = processed_widgets
created_outputs_dict = self._upload_dataset(trans,
library_id=trans.security.encode_id(library.id),
folder_id=trans.security.encode_id(folder.id),
template_id=template_id,
widgets=widgets,
replace_dataset=replace_dataset,
**kwd)
if created_outputs_dict:
@@ -253,8 +253,8 @@ class ToolShedRepositoriesController(BaseAPIController):
# this is ever not the case, this code will need to be updated.
tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(self.app, tool_ids[0].split('/')[0])
found_repository = json.loads(util.url_get(tool_shed_url, params=dict(tool_ids=','.join(tool_ids)), pathspec=['api', 'repositories']))
fr_keys = found_repository.keys()
tsr_id = found_repository[fr_keys[0]]['repository_id']
fr_first_key = next(iter(found_repository.keys()))
tsr_id = found_repository[fr_first_key]['repository_id']
repository_data['current_changeset'] = found_repository['current_changeset']
repository_data['repository'] = json.loads(util.url_get(tool_shed_url, pathspec=['api', 'repositories', tsr_id]))
del found_repository['current_changeset']
@@ -263,8 +263,7 @@ class ToolShedRepositoriesController(BaseAPIController):
else:
repository_data['repository'] = json.loads(util.url_get(tool_shed_url, pathspec=['api', 'repositories', tsr_id]))
repository_data['repository']['metadata'] = json.loads(util.url_get(tool_shed_url, pathspec=['api', 'repositories', tsr_id, 'metadata']))
repository_data['shed_conf'] = tool_util.build_shed_tool_conf_select_field(trans.app).get_html().replace('\n', '')
repository_data['panel_section_html'] = tool_panel_section_select_field.get_html(extra_attr={'style': 'width: 30em;'}).replace('\n', '')
repository_data['shed_conf'] = tool_util.build_shed_tool_conf_select_field(trans.app).to_dict()
repository_data['panel_section_dict'] = tool_panel_section_dict
for changeset, metadata in repository_data['repository']['metadata'].items():
if changeset not in tool_dependencies:
@@ -538,7 +537,7 @@ class ToolShedRepositoriesController(BaseAPIController):
id=trans.security.encode_id(tool_shed_repository.id))
return tool_shed_repository_dict
if installed_tool_shed_repositories:
return map(to_dict, installed_tool_shed_repositories)
return list(map(to_dict, installed_tool_shed_repositories))
message = "No repositories were installed, possibly because the selected repository has already been installed."
return dict(status="ok", message=message)
+10 -9
View File
@@ -1,7 +1,8 @@
import logging
import urllib
from json import dumps
from six.moves.urllib.parse import unquote_plus
import galaxy.queue_worker
from galaxy import exceptions, managers, util, web
from galaxy.managers.collections_util import dictify_dataset_collection_instance
@@ -201,7 +202,7 @@ class ToolsController(BaseAPIController, UsesVisualizationMixin):
lineage_dict = None
tool_shed_dependencies = tool.installed_tool_dependencies
if tool_shed_dependencies:
tool_shed_dependencies_dict = map(to_dict, tool_shed_dependencies)
tool_shed_dependencies_dict = list(map(to_dict, tool_shed_dependencies))
else:
tool_shed_dependencies_dict = None
return {
@@ -209,7 +210,7 @@ class ToolsController(BaseAPIController, UsesVisualizationMixin):
"tool_version": tool.version,
"dependency_shell_commands": tool.build_dependency_shell_commands(),
"lineage": lineage_dict,
"requirements": map(to_dict, tool.requirements),
"requirements": list(map(to_dict, tool.requirements)),
"installed_tool_shed_dependencies": tool_shed_dependencies_dict,
"tool_dir": tool.tool_dir,
"tool_shed": tool.tool_shed,
@@ -326,19 +327,19 @@ class ToolsController(BaseAPIController, UsesVisualizationMixin):
# Set up inputs.
inputs = payload.get('inputs', {})
# Find files coming in as multipart file data and add to inputs.
for k, v in payload.iteritems():
for k, v in payload.items():
if k.startswith('files_') or k.startswith('__files_'):
inputs[k] = v
# for inputs that are coming from the Library, copy them into the history
input_patch = {}
for k, v in inputs.iteritems():
for k, v in inputs.items():
if isinstance(v, dict) and v.get('src', '') == 'ldda' and 'id' in v:
ldda = trans.sa_session.query(trans.app.model.LibraryDatasetDatasetAssociation).get(self.decode_id(v['id']))
if trans.user_is_admin() or trans.app.security_agent.can_access_dataset(trans.get_current_user_roles(), ldda.dataset):
input_patch[k] = ldda.to_history_dataset_association(target_history, add_to_history=True)
for k, v in input_patch.iteritems():
for k, v in input_patch.items():
inputs[k] = v
# TODO: encode data ids and decode ids.
@@ -375,7 +376,7 @@ class ToolsController(BaseAPIController, UsesVisualizationMixin):
output_dict['output_name'] = output_name
rval['output_collections'].append(output_dict)
for output_name, collection_instance in vars.get('implicit_collections', {}).iteritems():
for output_name, collection_instance in vars.get('implicit_collections', {}).items():
history = target_history or trans.history
output_dict = dictify_dataset_collection_instance(collection_instance, security=trans.security, parent=history)
output_dict['output_name'] = output_name
@@ -387,7 +388,7 @@ class ToolsController(BaseAPIController, UsesVisualizationMixin):
# -- Helper methods --
#
def _get_tool(self, id, tool_version=None, user=None):
id = urllib.unquote_plus(id)
id = unquote_plus(id)
tool = self.app.toolbox.get_tool(id, tool_version)
if not tool:
raise exceptions.ObjectNotFound("Could not find tool with id '%s'." % id)
@@ -466,7 +467,7 @@ class ToolsController(BaseAPIController, UsesVisualizationMixin):
# TODO: need to handle updates to conditional parameters; conditional
# params are stored in dicts (and dicts within dicts).
new_inputs = payload['inputs']
tool_params.update(dict([(key, dumps(value)) for key, value in new_inputs.items() if key in tool.inputs and new_inputs[key] is not None]))
tool_params.update(dict([(key, dumps(value)) for key, value in new_inputs.items() if key in tool.inputs and value is not None]))
tool_params = tool.params_from_strings(tool_params, self.app)
#
+3 -4
View File
@@ -243,8 +243,8 @@ class ToolShedController(BaseAPIController):
# this is ever not the case, this code will need to be updated.
tool_shed_url = common_util.get_tool_shed_url_from_tool_shed_registry(self.app, tool_ids[0].split('/')[0])
found_repository = json.loads(util.url_get(tool_shed_url, params=dict(tool_ids=','.join(tool_ids)), pathspec=['api', 'repositories']))
fr_keys = found_repository.keys()
repository_id = found_repository[fr_keys[0]]['repository_id']
fr_first_key = next(iter(found_repository.keys()))
repository_id = found_repository[fr_first_key]['repository_id']
repository_data['current_changeset'] = found_repository['current_changeset']
repository_data['repository'] = json.loads(util.url_get(tool_shed_url, pathspec=['api', 'repositories', repository_id]))
del found_repository['current_changeset']
@@ -252,8 +252,7 @@ class ToolShedController(BaseAPIController):
else:
repository_data['repository'] = json.loads(util.url_get(tool_shed_url, pathspec=['api', 'repositories', repository_id]))
repository_data['repository']['metadata'] = json.loads(util.url_get(tool_shed_url, pathspec=['api', 'repositories', repository_id, 'metadata']))
repository_data['shed_conf'] = tool_util.build_shed_tool_conf_select_field(trans.app).get_html().replace('\n', '')
repository_data['panel_section_html'] = tool_panel_section_select_field.get_html(extra_attr={'style': 'width: 30em;'}).replace('\n', '')
repository_data['shed_conf'] = tool_util.build_shed_tool_conf_select_field(trans.app).to_dict()
repository_data['panel_section_dict'] = tool_panel_section_dict
for changeset, metadata in repository_data['repository']['metadata'].items():
if changeset not in tool_dependencies:
+2
View File
@@ -93,6 +93,7 @@ def app_factory(global_conf, load_app_kwds={}, **kwargs):
# The following routes don't bootstrap any information, simply provide the
# base analysis interface at which point the application takes over.
webapp.add_client_route('/admin/data_tables', 'admin')
webapp.add_client_route('/admin/users', 'admin')
webapp.add_client_route('/admin/roles', 'admin')
webapp.add_client_route('/admin/forms', 'admin')
@@ -106,6 +107,7 @@ def app_factory(global_conf, load_app_kwds={}, **kwargs):
webapp.add_client_route('/tours/{tour_id}')
webapp.add_client_route('/user')
webapp.add_client_route('/user/{form_id}')
webapp.add_client_route('/openids/list')
webapp.add_client_route('/visualizations/list_published')
webapp.add_client_route('/visualizations/list')
webapp.add_client_route('/visualizations/edit')
+28 -19
View File
@@ -154,7 +154,6 @@ class UserListGrid(grids.Grid):
grids.GridColumnFilter("All", args=dict(deleted='All'))
]
num_rows_per_page = 50
preserve_state = False
use_paging = True
def get_current_item(self, trans, **kwargs):
@@ -255,7 +254,6 @@ class RoleListGrid(grids.Grid):
grids.GridColumnFilter("All", args=dict(deleted='All'))
]
num_rows_per_page = 50
preserve_state = False
use_paging = True
def apply_query_filter(self, trans, query, **kwargs):
@@ -335,7 +333,6 @@ class GroupListGrid(grids.Grid):
grids.GridColumnFilter("All", args=dict(deleted='All'))
]
num_rows_per_page = 50
preserve_state = False
use_paging = True
@@ -447,7 +444,6 @@ class QuotaListGrid(grids.Grid):
grids.GridColumnFilter("All", args=dict(deleted='All'))
]
num_rows_per_page = 50
preserve_state = False
use_paging = True
@@ -499,7 +495,6 @@ class ToolVersionListGrid(grids.Grid):
standard_filters = []
default_filter = {}
num_rows_per_page = 50
preserve_state = False
use_paging = True
def build_initial_query(self, trans, **kwd):
@@ -529,6 +524,32 @@ class AdminGalaxy(controller.JSAppLauncher, AdminActions, UsesQuotaMixin, QuotaP
}
return self.template(trans, 'admin', settings=settings, message=message, status=status)
@web.expose
@web.json
@web.require_admin
def data_tables_list(self, trans, **kwd):
data = []
message = kwd.get('message', '')
status = kwd.get('status', 'done')
sorted_data_tables = sorted(
trans.app.tool_data_tables.get_tables().items()
)
for data_table_elem_name, data_table in sorted_data_tables:
for filename, file_dict in data_table.filenames.iteritems():
file_missing = ['file missing'] \
if not file_dict.get('found') else []
data.append({
'name': data_table.name,
'filename': filename,
'tool_data_path': file_dict.get('tool_data_path'),
'errors': ', '.join(file_missing + [
error for error in file_dict.get('errors', [])
]),
})
return {'data': data, 'message': message, 'status': status}
@web.expose
@web.json
@web.require_admin
@@ -559,7 +580,6 @@ class AdminGalaxy(controller.JSAppLauncher, AdminActions, UsesQuotaMixin, QuotaP
if message and status:
kwd['message'] = util.sanitize_text(message)
kwd['status'] = status
kwd['dict_format'] = True
return self.user_list_grid(trans, **kwd)
@web.expose_api
@@ -592,7 +612,6 @@ class AdminGalaxy(controller.JSAppLauncher, AdminActions, UsesQuotaMixin, QuotaP
if message:
kwargs['message'] = util.sanitize_text(message)
kwargs['status'] = status or 'done'
kwargs['dict_format'] = True
return self.quota_list_grid(trans, **kwargs)
@web.expose_api
@@ -856,13 +875,6 @@ class AdminGalaxy(controller.JSAppLauncher, AdminActions, UsesQuotaMixin, QuotaP
status = util.restore_text(kwd.get('status', 'done'))
return trans.fill_template('admin/view_datatypes_registry.mako', message=message, status=status)
@web.expose
@web.require_admin
def view_tool_data_tables(self, trans, **kwd):
message = escape(util.restore_text(kwd.get('message', '')))
status = util.restore_text(kwd.get('status', 'done'))
return trans.fill_template('admin/view_data_tables_registry.mako', message=message, status=status)
@web.expose
@web.require_admin
def display_applications(self, trans, **kwd):
@@ -902,7 +914,6 @@ class AdminGalaxy(controller.JSAppLauncher, AdminActions, UsesQuotaMixin, QuotaP
@web.expose_api
@web.require_admin
def tool_versions_list(self, trans, **kwd):
kwd['dict_format'] = True
return self.tool_version_list_grid(trans, **kwd)
@web.expose
@@ -923,7 +934,6 @@ class AdminGalaxy(controller.JSAppLauncher, AdminActions, UsesQuotaMixin, QuotaP
message, status = self._undelete_role(trans, ids)
elif operation == 'purge':
message, status = self._purge_role(trans, ids)
kwargs['dict_format'] = True
if message and status:
kwargs['message'] = util.sanitize_text(message)
kwargs['status'] = status
@@ -1168,7 +1178,6 @@ class AdminGalaxy(controller.JSAppLauncher, AdminActions, UsesQuotaMixin, QuotaP
message, status = self._undelete_group(trans, ids)
elif operation == 'purge':
message, status = self._purge_group(trans, ids)
kwargs['dict_format'] = True
if message and status:
kwargs['message'] = util.sanitize_text(message)
kwargs['status'] = status
@@ -1368,7 +1377,7 @@ class AdminGalaxy(controller.JSAppLauncher, AdminActions, UsesQuotaMixin, QuotaP
if users:
if trans.request.method == 'GET':
return {
'message': 'Changes password(s) for: %s.' % ', '.join([user.email for user in users.itervalues()]),
'message': 'Changes password(s) for: %s.' % ', '.join(user.email for user in users.values()),
'status' : 'info',
'inputs' : [{'name' : 'password', 'label' : 'New password', 'type' : 'password'},
{'name' : 'confirm', 'label' : 'Confirm password', 'type' : 'password'}]
@@ -1380,7 +1389,7 @@ class AdminGalaxy(controller.JSAppLauncher, AdminActions, UsesQuotaMixin, QuotaP
return self.message_exception(trans, 'Use a password of at least 6 characters.')
elif password != confirm:
return self.message_exception(trans, 'Passwords do not match.')
for user in users.itervalues():
for user in users.values():
user.set_password_cleartext(password)
trans.sa_session.add(user)
trans.sa_session.flush()
@@ -88,7 +88,6 @@ class AdminToolshed(AdminGalaxy):
if message and status:
kwd['message'] = util.sanitize_text(message)
kwd['status'] = 'success' if status in ['ok', 'done', 'success'] else 'error'
kwd['dict_format'] = True
return self.installed_repository_grid(trans, **kwd)
@web.expose
@@ -239,7 +238,7 @@ class AdminToolshed(AdminGalaxy):
message += 'Attempting to uninstall tool dependencies resulted in errors: %s' % errors
status = max(status, statuses.index('error'))
status = statuses[status]
remove_from_disk_check_box = CheckboxField('remove_from_disk', checked=remove_from_disk_checked)
remove_from_disk_check_box = CheckboxField('remove_from_disk', value=remove_from_disk_checked)
return trans.fill_template('/admin/tool_shed_repository/deactivate_or_uninstall_repository.mako',
repository=tool_shed_repositories,
remove_from_disk_check_box=remove_from_disk_check_box,
@@ -535,10 +534,10 @@ class AdminToolshed(AdminGalaxy):
else:
install_tool_dependencies_check_box_checked = True
install_tool_dependencies_check_box = CheckboxField('install_tool_dependencies',
checked=install_tool_dependencies_check_box_checked)
value=install_tool_dependencies_check_box_checked)
view = views.DependencyResolversView(self.app)
if view.installable_resolvers:
install_resolver_dependencies_check_box = CheckboxField('install_resolver_dependencies', checked=True)
install_resolver_dependencies_check_box = CheckboxField('install_resolver_dependencies', value=True)
else:
install_resolver_dependencies_check_box = None
return trans.fill_template('/admin/tool_shed_repository/install_tool_dependencies_with_update.mako',
@@ -1054,12 +1053,12 @@ class AdminToolshed(AdminGalaxy):
else:
install_tool_dependencies_check_box_checked = True
install_tool_dependencies_check_box = CheckboxField('install_tool_dependencies',
checked=install_tool_dependencies_check_box_checked)
value=install_tool_dependencies_check_box_checked)
# Handle repository dependencies check box.
install_repository_dependencies_check_box = CheckboxField('install_repository_dependencies', checked=True)
install_repository_dependencies_check_box = CheckboxField('install_repository_dependencies', value=True)
view = views.DependencyResolversView(self.app)
if view.installable_resolvers:
install_resolver_dependencies_check_box = CheckboxField('install_resolver_dependencies', checked=True)
install_resolver_dependencies_check_box = CheckboxField('install_resolver_dependencies', value=True)
else:
install_resolver_dependencies_check_box = None
encoded_repo_info_dicts = encoding_util.encoding_sep.join(encoded_repo_info_dicts)
@@ -1392,7 +1391,7 @@ class AdminToolshed(AdminGalaxy):
else:
original_section_name = ''
tool_panel_section_select_field = tool_util.build_tool_panel_section_select_field(trans.app)
no_changes_check_box = CheckboxField('no_changes', checked=True)
no_changes_check_box = CheckboxField('no_changes', value=True)
if original_section_name:
message += "The tools contained in your <b>%s</b> repository were last loaded into the tool panel section <b>%s</b>. " \
% (escape(tool_shed_repository.name), original_section_name)
@@ -1423,7 +1422,7 @@ class AdminToolshed(AdminGalaxy):
# whether installed or missing.
containers_dict = dd.merge_missing_repository_dependencies_to_installed_container(containers_dict)
# Handle repository dependencies check box.
install_repository_dependencies_check_box = CheckboxField('install_repository_dependencies', checked=True)
install_repository_dependencies_check_box = CheckboxField('install_repository_dependencies', value=True)
# Handle tool dependencies check box.
if trans.app.config.tool_dependency_dir is None:
if includes_tool_dependencies:
@@ -1433,10 +1432,10 @@ class AdminToolshed(AdminGalaxy):
install_tool_dependencies_check_box_checked = False
else:
install_tool_dependencies_check_box_checked = True
install_tool_dependencies_check_box = CheckboxField('install_tool_dependencies', checked=install_tool_dependencies_check_box_checked)
install_tool_dependencies_check_box = CheckboxField('install_tool_dependencies', value=install_tool_dependencies_check_box_checked)
view = views.DependencyResolversView(self.app)
if view.installable_resolvers:
install_resolver_dependencies_check_box = CheckboxField('install_resolver_dependencies', checked=True)
install_resolver_dependencies_check_box = CheckboxField('install_resolver_dependencies', value=True)
else:
install_resolver_dependencies_check_box = None
return trans.fill_template('/admin/tool_shed_repository/reselect_tool_panel_section.mako',
@@ -5,9 +5,9 @@ Upload class
from __future__ import absolute_import
import logging
import urllib
import requests
from six.moves.urllib.parse import urlencode
from galaxy import jobs, web
from galaxy.util import Params
@@ -75,10 +75,10 @@ class ASync(BaseUIController):
# Assume there is exactly one output file possible
TOOL_OUTPUT_TYPE = None
for idx, obj in enumerate(tool.outputs.values()):
for key, obj in tool.outputs.items():
try:
TOOL_OUTPUT_TYPE = obj.format
params[tool.outputs.keys()[idx]] = data.id
params[key] = data.id
break
except Exception:
# exclude outputs different from ToolOutput (e.g. collections) from the previous assumption
@@ -163,7 +163,7 @@ class ASync(BaseUIController):
url_join_char = '&'
else:
url_join_char = '?'
url = "%s%s%s" % (url, url_join_char, urllib.urlencode(params.flatten()))
url = "%s%s%s" % (url, url_join_char, urlencode(params.flatten()))
log.debug("connecting to -> %s" % url)
trans.log_event("Async connecting to -> %s" % url)
text = requests.get(url).text.strip()
@@ -65,7 +65,7 @@ class DataManager(BaseUIController):
data_manager_json = {}
error_messages.append(escape("Unable to obtain data_table info for hda (%s): %s" % (hda.id, e)))
values = []
for key, value in data_manager_json.get('data_tables', {}).iteritems():
for key, value in data_manager_json.get('data_tables', {}).items():
values.append((key, value))
data_manager_output.append(values)
return trans.fill_template("data_manager/view_job.mako", data_manager=data_manager, job=job, view_only=not_is_admin, hdas=hdas, data_manager_output=data_manager_output, message=message, status=status, error_messages=error_messages)
@@ -116,3 +116,57 @@ class DataManager(BaseUIController):
message=message,
status=status)
return trans.response.send_redirect(redirect_url)
@web.expose
@web.json
@web.require_admin
def tool_data_table_items(self, trans, **kwd):
data = {'columns': [], 'items': []}
message = kwd.get('message', '')
status = kwd.get('status', 'info')
table_name = kwd.get('table_name', None)
if not table_name:
return {
'data': data,
'message': 'No Data table name provided.',
'status': 'warning',
}
data_table = trans.app.tool_data_tables.get(table_name, None)
if data_table is None:
return {
'data': data,
'message': 'Invalid Data table (%s) was requested' % table_name,
'status': 'error'
}
columns = data_table.get_column_name_list()
rows = [dict(zip(columns, table_row)) for table_row in data_table.data]
data['columns'] = columns
data['items'] = rows
return {'data': data, 'message': message, 'status': status}
@web.expose
@web.json
@web.require_admin
def reload_tool_data_table(self, trans, **kwd):
table_name = kwd.get('table_name', None)
if not table_name:
return {
'message': 'No data table has been reloaded.',
'status': 'error',
}
redirect_url = web.url_for(
controller='data_manager',
action='tool_data_table_items',
table_name=table_name,
message='The data table "%s" has been reloaded.' % table_name,
status='done',
)
return trans.response.send_redirect(redirect_url)

Some files were not shown because too many files have changed in this diff Show More