mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-21 13:50:20 +08:00
Merge remote-tracking branch 'upstream/dev' into require_elimination
This commit is contained in:
@@ -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/
|
||||
@@ -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
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
@@ -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;">`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -154,9 +154,9 @@ function show_in_overlay(options) {
|
||||
closeButton: true,
|
||||
title: " ",
|
||||
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"> `;
|
||||
}
|
||||
}
|
||||
|
||||
// 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) == "-" ? "↑" : "↓";
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -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>`;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ var commonLibs = [
|
||||
"libs/farbtastic",
|
||||
"libs/bootstrap",
|
||||
"libs/bootstrap-tour",
|
||||
"libs/vue",
|
||||
"vue",
|
||||
// mvc
|
||||
"libs/underscore",
|
||||
"libs/backbone",
|
||||
|
||||
+23
-19
@@ -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"
|
||||
|
||||
@@ -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,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])
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
@@ -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}"> '
|
||||
'<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
|
||||
|
||||
@@ -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()
|
||||
@@ -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,
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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 = ''
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
#
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user