mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge pull request #6683 from mvdbeek/expose_workflow_versions
Expose workflow versions
This commit is contained in:
@@ -14,6 +14,7 @@ class Workflow {
|
||||
this.has_changes = false;
|
||||
this.active_form_has_changes = false;
|
||||
this.workflowOutputLabels = {};
|
||||
this.workflow_version = 0;
|
||||
}
|
||||
canLabelOutputWith(label) {
|
||||
if (label) {
|
||||
@@ -215,6 +216,7 @@ class Workflow {
|
||||
var max_id = offset;
|
||||
// First pass, nodes
|
||||
var using_workflow_outputs = false;
|
||||
wf.workflow_version = data.version;
|
||||
$.each(data.steps, (id, step) => {
|
||||
var node = wf.app.prebuildNode(step.type, step.name, step.content_id);
|
||||
// If workflow being copied into another, wipe UUID and let
|
||||
|
||||
@@ -94,6 +94,7 @@ export default Backbone.View.extend({
|
||||
window.document.location = self.urls.workflow_index;
|
||||
}
|
||||
};
|
||||
var workflow_index = self.urls.workflow_index;
|
||||
var save_current_workflow = (eventObj, success_callback) => {
|
||||
show_message("Saving workflow", "progress");
|
||||
self.workflow.check_changes_in_active_form();
|
||||
@@ -126,7 +127,9 @@ export default Backbone.View.extend({
|
||||
self.workflow.name = data.name;
|
||||
self.workflow.has_changes = false;
|
||||
self.workflow.stored = true;
|
||||
self.workflow.workflow_version = data.version;
|
||||
self.showWorkflowParameters();
|
||||
self.build_version_select();
|
||||
if (data.errors) {
|
||||
window.show_modal("Saving workflow", body, {
|
||||
Ok: hide_modal
|
||||
@@ -270,47 +273,106 @@ export default Backbone.View.extend({
|
||||
this.ext_to_type = this.datatypes_mapping.ext_to_class_name;
|
||||
this.type_to_type = this.datatypes_mapping.class_to_classes;
|
||||
|
||||
// Load workflow definition
|
||||
this._workflowLoadAjax(self.options.id, {
|
||||
success: function(data) {
|
||||
self.reset();
|
||||
self.workflow.from_simple(data, true);
|
||||
self.workflow.has_changes = false;
|
||||
self.workflow.fit_canvas_to_nodes();
|
||||
self.scroll_to_nodes();
|
||||
self.canvas_manager.draw_overview();
|
||||
// Determine if any parameters were 'upgraded' and provide message
|
||||
var upgrade_message = "";
|
||||
_.each(data.steps, (step, step_id) => {
|
||||
var details = "";
|
||||
if (step.errors) {
|
||||
details += `<li>${step.errors}</li>`;
|
||||
}
|
||||
_.each(data.upgrade_messages[step_id], m => {
|
||||
details += `<li>${m}</li>`;
|
||||
});
|
||||
if (details) {
|
||||
upgrade_message += `<li>Step ${parseInt(step_id, 10) + 1}: ${
|
||||
self.workflow.nodes[step_id].name
|
||||
}<ul>${details}</ul></li>`;
|
||||
}
|
||||
});
|
||||
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>`,
|
||||
{ Continue: hide_modal }
|
||||
);
|
||||
} else {
|
||||
hide_modal();
|
||||
}
|
||||
self.showWorkflowParameters();
|
||||
},
|
||||
beforeSubmit: function(data) {
|
||||
show_message("Loading workflow", "progress");
|
||||
}
|
||||
});
|
||||
this.get_workflow_versions = function() {
|
||||
let _workflow_version_dropdown = {};
|
||||
let workflow_versions = JSON.parse(
|
||||
$.ajax({
|
||||
url: `${Galaxy.root}api/workflows/${self.options.id}/versions`,
|
||||
async: false,
|
||||
}).responseText
|
||||
);
|
||||
|
||||
for (let i = 0; i < workflow_versions.length; i++) {
|
||||
let current_wf = workflow_versions[i];
|
||||
let version_text = `Version ${current_wf['version']}, ${current_wf['steps']} steps`;
|
||||
let selected = false;
|
||||
if (i == self.workflow.workflow_version) {
|
||||
version_text = `${version_text} (active)`;
|
||||
selected = true;
|
||||
};
|
||||
_workflow_version_dropdown[version_text] = {
|
||||
version: i,
|
||||
selected: selected,
|
||||
}
|
||||
}
|
||||
return _workflow_version_dropdown
|
||||
}
|
||||
|
||||
this.build_version_select = function() {
|
||||
let versions = this.get_workflow_versions();
|
||||
$("#workflow-version-switch").empty();
|
||||
$.each(versions, function(k, v) {
|
||||
$('#workflow-version-switch').append($('<option></option>').html(k).val(v.version).selected(v.selected));
|
||||
});
|
||||
$("#workflow-version-switch").on('change', function () {
|
||||
$('#workflow-version-switch').unbind('change');
|
||||
if (this.value != self.workflow.workflow_version) {
|
||||
if (self.workflow && self.workflow.has_changes) {
|
||||
let r = confirm("There are unsaved changes to your workflow which will be lost. Continue ?");
|
||||
if (r == false) {
|
||||
// We rebuild the version select list, to reset the selected version
|
||||
self.build_version_select();
|
||||
return
|
||||
}
|
||||
}
|
||||
self.load_workflow(self.options.id, this.value);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
this.load_workflow = function load_workflow(id, version) {
|
||||
this._workflowLoadAjax(id, version, {
|
||||
success: function(data) {
|
||||
self.reset();
|
||||
self.workflow.from_simple(data, true);
|
||||
self.workflow.has_changes = false;
|
||||
self.workflow.fit_canvas_to_nodes();
|
||||
self.scroll_to_nodes();
|
||||
self.canvas_manager.draw_overview();
|
||||
// make_popupmenu($("#workflow-versions-switch"), self.get_workflow_versions());
|
||||
self.build_version_select();
|
||||
|
||||
// Determine if any parameters were 'upgraded' and provide message
|
||||
var upgrade_message = "";
|
||||
_.each(data.steps, (step, step_id) => {
|
||||
var details = "";
|
||||
if (step.errors) {
|
||||
details += `<li>${step.errors}</li>`;
|
||||
}
|
||||
_.each(data.upgrade_messages[step_id], m => {
|
||||
details += `<li>${m}</li>`;
|
||||
});
|
||||
if (details) {
|
||||
upgrade_message += `<li>Step ${parseInt(step_id, 10) + 1}: ${
|
||||
self.workflow.nodes[step_id].name
|
||||
}<ul>${details}</ul></li>`;
|
||||
}
|
||||
});
|
||||
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>`,
|
||||
{ Continue: hide_modal }
|
||||
);
|
||||
} else {
|
||||
hide_modal();
|
||||
}
|
||||
self.showWorkflowParameters();
|
||||
},
|
||||
error: function(response) {
|
||||
window.show_modal("Loading workflow failed.", response.err_msg, { Ok: function( response ){
|
||||
window.onbeforeunload = undefined;
|
||||
window.document.location = workflow_index;
|
||||
}})
|
||||
},
|
||||
beforeSubmit: function(data) {
|
||||
show_message("Loading workflow", "progress");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Load workflow definition
|
||||
this.load_workflow(self.options.id, self.options.version)
|
||||
if (window.make_popupmenu) {
|
||||
make_popupmenu($("#workflow-options-button"), {
|
||||
Save: save_current_workflow,
|
||||
@@ -530,7 +592,7 @@ export default Backbone.View.extend({
|
||||
copy_into_workflow: function(workflowId) {
|
||||
// Load workflow definition
|
||||
var self = this;
|
||||
this._workflowLoadAjax(workflowId, {
|
||||
this._workflowLoadAjax(workflowId, None, {
|
||||
success: function(data) {
|
||||
self.workflow.from_simple(data, false);
|
||||
// Determine if any parameters were 'upgraded' and provide message
|
||||
@@ -584,11 +646,11 @@ export default Backbone.View.extend({
|
||||
cc.css({ left: left, top: top });
|
||||
},
|
||||
|
||||
_workflowLoadAjax: function(workflowId, options) {
|
||||
_workflowLoadAjax: function(workflowId, version, options) {
|
||||
$.ajax(
|
||||
Utils.merge(options, {
|
||||
url: this.urls.load_workflow,
|
||||
data: { id: workflowId, _: "true" },
|
||||
data: { id: workflowId, _: "true", version: version },
|
||||
dataType: "json",
|
||||
cache: false
|
||||
})
|
||||
|
||||
@@ -368,7 +368,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
|
||||
return workflow, missing_tool_tups
|
||||
|
||||
def workflow_to_dict(self, trans, stored, style="export"):
|
||||
def workflow_to_dict(self, trans, stored, style="export", version=None):
|
||||
""" Export the workflow contents to a dictionary ready for JSON-ification and to be
|
||||
sent out via API for instance. There are three styles of export allowed 'export', 'instance', and
|
||||
'editor'. The Galaxy team will do its best to preserve the backward compatibility of the
|
||||
@@ -377,22 +377,31 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
option describes the workflow in a context more tied to the current Galaxy instance and includes
|
||||
fields like 'url' and 'url' and actual unencoded step ids instead of 'order_index'.
|
||||
"""
|
||||
if version == '':
|
||||
version = None
|
||||
if version is not None:
|
||||
version = int(version)
|
||||
if style == "editor":
|
||||
return self._workflow_to_dict_editor(trans, stored)
|
||||
wf_dict = self._workflow_to_dict_editor(trans, stored, version=version)
|
||||
elif style == "legacy":
|
||||
return self._workflow_to_dict_instance(stored, legacy=True)
|
||||
wf_dict = self._workflow_to_dict_instance(stored, legacy=True, version=version)
|
||||
elif style == "instance":
|
||||
return self._workflow_to_dict_instance(stored, legacy=False)
|
||||
wf_dict = self._workflow_to_dict_instance(stored, legacy=False, version=version)
|
||||
elif style == "run":
|
||||
return self._workflow_to_dict_run(trans, stored)
|
||||
wf_dict = self._workflow_to_dict_run(trans, stored, version=version)
|
||||
else:
|
||||
return self._workflow_to_dict_export(trans, stored)
|
||||
wf_dict = self._workflow_to_dict_export(trans, stored, version=version)
|
||||
if version:
|
||||
wf_dict['version'] = version
|
||||
else:
|
||||
wf_dict['version'] = len(stored.workflows) - 1
|
||||
return wf_dict
|
||||
|
||||
def _workflow_to_dict_run(self, trans, stored):
|
||||
def _workflow_to_dict_run(self, trans, stored, version=None):
|
||||
"""
|
||||
Builds workflow dictionary used by run workflow form
|
||||
"""
|
||||
workflow = stored.latest_workflow
|
||||
workflow = stored.get_internal_version(version)
|
||||
if len(workflow.steps) == 0:
|
||||
raise exceptions.MessageException('Workflow cannot be run because it does not have any steps.')
|
||||
if attach_ordered_steps(workflow, workflow.steps):
|
||||
@@ -476,8 +485,8 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
"""
|
||||
return self._resource_mapper_function(trans=trans, stored_workflow=stored, workflow=workflow)
|
||||
|
||||
def _workflow_to_dict_editor(self, trans, stored):
|
||||
workflow = stored.latest_workflow
|
||||
def _workflow_to_dict_editor(self, trans, stored, version=None):
|
||||
workflow = stored.get_internal_version(version)
|
||||
# Pack workflow data into a dictionary and return
|
||||
data = {}
|
||||
data['name'] = workflow.name
|
||||
@@ -584,12 +593,12 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
data['steps'][step.order_index] = step_dict
|
||||
return data
|
||||
|
||||
def _workflow_to_dict_export(self, trans, stored=None, workflow=None):
|
||||
def _workflow_to_dict_export(self, trans, stored=None, workflow=None, version=None):
|
||||
""" Export the workflow contents to a dictionary ready for JSON-ification and export.
|
||||
"""
|
||||
if workflow is None:
|
||||
assert stored is not None
|
||||
workflow = stored.latest_workflow
|
||||
workflow = stored.get_internal_version(version)
|
||||
|
||||
annotation_str = ""
|
||||
tag_str = ""
|
||||
@@ -755,11 +764,11 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
data['steps'][step.order_index] = step_dict
|
||||
return data
|
||||
|
||||
def _workflow_to_dict_instance(self, stored, legacy=True):
|
||||
def _workflow_to_dict_instance(self, stored, legacy=True, version=None):
|
||||
encode = self.app.security.encode_id
|
||||
sa_session = self.app.model.context
|
||||
item = stored.to_dict(view='element', value_mapper={'id': encode})
|
||||
workflow = stored.latest_workflow
|
||||
workflow = stored.get_internal_version(version)
|
||||
item['url'] = url_for('workflow', id=item['id'])
|
||||
item['owner'] = stored.user.username
|
||||
inputs = {}
|
||||
|
||||
@@ -3906,6 +3906,13 @@ class StoredWorkflow(HasTags, Dictifiable):
|
||||
self.latest_workflow_id = None
|
||||
self.workflows = []
|
||||
|
||||
def get_internal_version(self, version):
|
||||
if version is None:
|
||||
return self.latest_workflow
|
||||
if len(self.workflows) <= version:
|
||||
raise Exception("Version does not exist")
|
||||
return list(reversed(self.workflows))[version]
|
||||
|
||||
def copy_tags_from(self, target_user, source_workflow):
|
||||
# Override to only copy owner tags.
|
||||
for src_swta in source_workflow.owner_tags:
|
||||
|
||||
@@ -2224,7 +2224,8 @@ mapper(model.StoredWorkflow, model.StoredWorkflow.table, properties=dict(
|
||||
workflows=relation(model.Workflow,
|
||||
backref='stored_workflow',
|
||||
cascade="all, delete-orphan",
|
||||
primaryjoin=(model.StoredWorkflow.table.c.id == model.Workflow.table.c.stored_workflow_id)),
|
||||
primaryjoin=(model.StoredWorkflow.table.c.id == model.Workflow.table.c.stored_workflow_id),
|
||||
order_by=-model.Workflow.id),
|
||||
latest_workflow=relation(model.Workflow,
|
||||
post_update=True,
|
||||
primaryjoin=(model.StoredWorkflow.table.c.latest_workflow_id == model.Workflow.table.c.id),
|
||||
|
||||
@@ -214,7 +214,18 @@ class WorkflowsAPIController(BaseAPIController, UsesStoredWorkflowMixin, UsesAnn
|
||||
style = "legacy"
|
||||
else:
|
||||
style = "instance"
|
||||
return self.workflow_contents_manager.workflow_to_dict(trans, stored_workflow, style=style)
|
||||
version = kwd.get('version')
|
||||
return self.workflow_contents_manager.workflow_to_dict(trans, stored_workflow, style=style, version=version)
|
||||
|
||||
@expose_api
|
||||
def show_versions(self, trans, workflow_id, **kwds):
|
||||
"""
|
||||
GET /api/workflows/{encoded_workflow_id}/versions
|
||||
|
||||
Lists all versions of this workflow.
|
||||
"""
|
||||
stored_workflow = self.workflow_manager.get_stored_accessible_workflow(trans, workflow_id)
|
||||
return [{'version': i, 'update_time': str(w.update_time), 'steps': len(w.steps)} for i, w in enumerate(reversed(stored_workflow.workflows))]
|
||||
|
||||
@expose_api
|
||||
def create(self, trans, payload, **kwd):
|
||||
@@ -400,7 +411,8 @@ class WorkflowsAPIController(BaseAPIController, UsesStoredWorkflowMixin, UsesAnn
|
||||
|
||||
style = kwd.get("style", "export")
|
||||
download_format = kwd.get('format')
|
||||
ret_dict = self.workflow_contents_manager.workflow_to_dict(trans, stored_workflow, style=style)
|
||||
version = kwd.get('version')
|
||||
ret_dict = self.workflow_contents_manager.workflow_to_dict(trans, stored_workflow, style=style, version=version)
|
||||
if download_format == 'json-download':
|
||||
sname = stored_workflow.name
|
||||
sname = ''.join(c in util.FILENAME_VALID_CHARS and c or '_' for c in sname)[0:150]
|
||||
|
||||
@@ -446,6 +446,11 @@ def populate_api_routes(webapp, app):
|
||||
controller='workflows',
|
||||
action='workflow_dict',
|
||||
conditions=dict(method=['GET']))
|
||||
webapp.mapper.connect('show_versions',
|
||||
'/api/workflows/{workflow_id}/versions',
|
||||
controller='workflows',
|
||||
action='show_versions',
|
||||
conditions=dict(method=['GET']))
|
||||
# Preserve the following download route for now for dependent applications -- deprecate at some point
|
||||
webapp.mapper.connect('workflow_dict',
|
||||
'/api/workflows/download/{workflow_id}',
|
||||
|
||||
@@ -637,7 +637,7 @@ class WorkflowController(BaseUIController, SharableMixin, UsesStoredWorkflowMixi
|
||||
|
||||
@web.expose
|
||||
@web.require_login("edit workflows")
|
||||
def editor(self, trans, id=None):
|
||||
def editor(self, trans, id=None, version=None):
|
||||
"""
|
||||
Render the main workflow editor interface. The canvas is embedded as
|
||||
an iframe (necessary for scrolling to work properly), which is
|
||||
@@ -653,10 +653,18 @@ class WorkflowController(BaseUIController, SharableMixin, UsesStoredWorkflowMixi
|
||||
.order_by(desc(model.StoredWorkflow.table.c.update_time)) \
|
||||
.options(joinedload('latest_workflow').joinedload('steps')) \
|
||||
.all()
|
||||
return trans.fill_template("workflow/editor.mako", workflows=workflows, stored=stored, annotation=self.get_item_annotation_str(trans.sa_session, trans.user, stored))
|
||||
if version is None:
|
||||
version = len(stored.workflows) - 1
|
||||
else:
|
||||
version = int(version)
|
||||
return trans.fill_template("workflow/editor.mako",
|
||||
workflows=workflows,
|
||||
stored=stored,
|
||||
version=version,
|
||||
annotation=self.get_item_annotation_str(trans.sa_session, trans.user, stored))
|
||||
|
||||
@web.json
|
||||
def load_workflow(self, trans, id):
|
||||
def load_workflow(self, trans, id, version=None):
|
||||
"""
|
||||
Get the latest Workflow for the StoredWorkflow identified by `id` and
|
||||
encode it as a json string that can be read by the workflow editor
|
||||
@@ -665,7 +673,7 @@ class WorkflowController(BaseUIController, SharableMixin, UsesStoredWorkflowMixi
|
||||
trans.workflow_building_mode = workflow_building_modes.ENABLED
|
||||
stored = self.get_stored_workflow(trans, id, check_ownership=True, check_accessible=False)
|
||||
workflow_contents_manager = workflows.WorkflowContentsManager(trans.app)
|
||||
return workflow_contents_manager.workflow_to_dict(trans, stored, style="editor")
|
||||
return workflow_contents_manager.workflow_to_dict(trans, stored, style="editor", version=version)
|
||||
|
||||
@web.expose
|
||||
@web.require_login("use workflows")
|
||||
|
||||
@@ -10,25 +10,25 @@
|
||||
self.active_view="workflow"
|
||||
self.overlay_visible=True
|
||||
self.editor_config = {
|
||||
'id' : trans.security.encode_id( stored.id ),
|
||||
'id' : trans.security.encode_id(stored.id),
|
||||
'urls' : {
|
||||
'tool_search' : h.url_for( '/api/tools' ),
|
||||
'get_datatypes' : h.url_for( '/api/datatypes/mapping' ),
|
||||
'load_workflow' : h.url_for( controller='workflow', action='load_workflow' ),
|
||||
'run_workflow' : h.url_for( controller='root', action='index', workflow_id=trans.security.encode_id(stored.id)),
|
||||
'rename_async' : h.url_for( controller='workflow', action='rename_async', id=trans.security.encode_id(stored.id) ),
|
||||
'annotate_async' : h.url_for( controller='workflow', action='annotate_async', id=trans.security.encode_id(stored.id) ),
|
||||
'get_new_module_info' : h.url_for( controller='workflow', action='get_new_module_info' ),
|
||||
'workflow_index' : h.url_for( '/workflows/list' ),
|
||||
'save_workflow' : h.url_for( controller='workflow', action='save_workflow' ),
|
||||
'workflow_save_as' : h.url_for( controller='workflow', action='save_workflow_as')
|
||||
'tool_search' : h.url_for('/api/tools'),
|
||||
'get_datatypes' : h.url_for('/api/datatypes/mapping'),
|
||||
'load_workflow' : h.url_for(controller='workflow', action='load_workflow'),
|
||||
'run_workflow' : h.url_for(controller='root', action='index', workflow_id=trans.security.encode_id(stored.id)),
|
||||
'rename_async' : h.url_for(controller='workflow', action='rename_async', id=trans.security.encode_id(stored.id)),
|
||||
'annotate_async' : h.url_for(controller='workflow', action='annotate_async', id=trans.security.encode_id(stored.id)),
|
||||
'get_new_module_info' : h.url_for(controller='workflow', action='get_new_module_info'),
|
||||
'workflow_index' : h.url_for('/workflows/list'),
|
||||
'save_workflow' : h.url_for(controller='workflow', action='save_workflow'),
|
||||
'workflow_save_as' : h.url_for(controller='workflow', action='save_workflow_as')
|
||||
},
|
||||
'workflows' : [{
|
||||
'id' : trans.security.encode_id( workflow.id ),
|
||||
'latest_id' : trans.security.encode_id( workflow.latest_workflow.id ),
|
||||
'step_count' : len( workflow.latest_workflow.steps ),
|
||||
'name' : h.to_unicode( workflow.name )
|
||||
} for workflow in workflows ]
|
||||
'id' : trans.security.encode_id(workflow.id),
|
||||
'latest_id' : trans.security.encode_id(workflow.latest_workflow.id),
|
||||
'step_count' : len(workflow.latest_workflow.steps),
|
||||
'name' : h.to_unicode(workflow.name)
|
||||
} for workflow in workflows]
|
||||
}
|
||||
%>
|
||||
</%def>
|
||||
@@ -403,6 +403,10 @@
|
||||
<label>Name:</label>
|
||||
<span id="workflow-name" class="editable-text" title="Click to rename workflow">${h.to_unicode( stored.name ) | h}</span>
|
||||
</div>
|
||||
<div id="workflow-version-area" class="form-row">
|
||||
<label>Version:</label>
|
||||
</div>
|
||||
<select id="workflow-version-switch" href="#">Select version</select>
|
||||
## Workflow tags.
|
||||
<%namespace file="/tagging_common.mako" import="render_individual_tagging_element" />
|
||||
<div class="form-row">
|
||||
|
||||
Reference in New Issue
Block a user