Ejemplo n.º 1
0
    BASE_MVC.mixin(
        searchableMixin,
        /** @lends DatasetAssociation.prototype */ {
            _logNamespace: logNamespace,

            /** default attributes for a model */
            defaults: {
                state: STATES.NEW,
                deleted: false,
                purged: false,
                name: "(unnamed dataset)",
                accessible: true,
                // sniffed datatype (sam, tabular, bed, etc.)
                data_type: "",
                file_ext: "",
                file_size: 0,

                // array of associated file types (eg. [ 'bam_index', ... ])
                meta_files: [],

                misc_blurb: "",
                misc_info: "",

                tags: []
                // do NOT default on annotation, as this default is valid and will be passed on 'save'
                //  which is incorrect behavior when the model is only partially fetched (annos are not passed in summary data)
                //annotation          : ''
            },

            /** instance vars and listeners */
            initialize: function(attributes, options) {
                this.debug(`${this}(Dataset).initialize`, attributes, options);

                //!! this state is not in trans.app.model.Dataset.states - set it here -
                if (!this.get("accessible")) {
                    this.set("state", STATES.NOT_VIEWABLE);
                }

                /** Datasets rely/use some web controllers - have the model generate those URLs on startup */
                this.urls = this._generateUrls();

                this._setUpListeners();
            },

            _getDatasetId: function() {
                return this.get("id");
            },

            /** returns misc. web urls for rendering things like re-run, display, etc. */
            _generateUrls: function() {
                const id = this._getDatasetId();
                if (!id) {
                    return {};
                }
                var urls = {
                    purge: `datasets/${id}/purge_async`,
                    display: `datasets/${id}/display/?preview=True`,
                    edit: `datasets/edit?dataset_id=${id}`,
                    download: `datasets/${id}/display${this._downloadQueryParameters()}`,
                    report_error: `dataset/errors?id=${id}`,
                    rerun: `tool_runner/rerun?id=${id}`,
                    show_params: `datasets/${id}/show_params`,
                    visualization: "visualization",
                    meta_download: `dataset/get_metadata_file?hda_id=${id}&metadata_name=`
                };
                _.each(urls, (value, key) => {
                    urls[key] = getAppRoot() + value;
                });
                this.urls = urls;
                return urls;
            },

            _downloadQueryParameters: function() {
                return `?to_ext=${this.get("file_ext")}`;
            },

            /** set up any event listeners
             *  event: state:ready  fired when this DA moves into/is already in a ready state
             */
            _setUpListeners: function() {
                // if the state has changed and the new state is a ready state, fire an event
                this.on("change:state", function(currModel, newState) {
                    this.log(`${this} has changed state:`, currModel, newState);
                    if (this.inReadyState()) {
                        this.trigger("state:ready", currModel, newState, this.previous("state"));
                    }
                });
                // the download url (currently) relies on having a correct file extension
                this.on("change:id change:file_ext", function(currModel) {
                    this._generateUrls();
                });
            },

            // ........................................................................ common queries
            /** override to add urls */
            toJSON: function() {
                var json = Backbone.Model.prototype.toJSON.call(this);
                //console.warn( 'returning json?' );
                //return json;
                return _.extend(json, {
                    urls: this.urls
                });
            },

            /** Is this dataset deleted or purged? */
            isDeletedOrPurged: function() {
                return this.get("deleted") || this.get("purged");
            },

            /** Is this dataset in a 'ready' state; where 'Ready' states are states where no
             *      processing (for the ds) is left to do on the server.
             */
            inReadyState: function() {
                var ready = _.contains(STATES.READY_STATES, this.get("state"));
                return this.isDeletedOrPurged() || ready;
            },

            /** Does this model already contain detailed data (as opposed to just summary level data)? */
            hasDetails: function() {
                // if it's inaccessible assume it has everything it needs
                if (!this.get("accessible")) {
                    return true;
                }
                return this.has("annotation");
            },

            /** Convenience function to match dataset.has_data. */
            hasData: function() {
                return this.get("file_size") > 0;
            },

            // ........................................................................ ajax
            fetch: function(options) {
                var dataset = this;
                return Backbone.Model.prototype.fetch.call(this, options).always(() => {
                    dataset._generateUrls();
                });
            },

            /** override to use actual Dates objects for create/update times */
            parse: function(response, options) {
                var parsed = Backbone.Model.prototype.parse.call(this, response, options);
                if (parsed.create_time) {
                    parsed.create_time = new Date(parsed.create_time);
                }
                if (parsed.update_time) {
                    parsed.update_time = new Date(parsed.update_time);
                }
                return parsed;
            },

            /** override to wait by default */
            save: function(attrs, options) {
                options = options || {};
                options.wait = _.isUndefined(options.wait) ? true : options.wait;
                return Backbone.Model.prototype.save.call(this, attrs, options);
            },

            //NOTE: subclasses of DA's will need to implement url and urlRoot in order to have these work properly
            /** save this dataset, _Mark_ing it as deleted (just a flag) */
            delete: function(options) {
                if (this.get("deleted")) {
                    return jQuery.when();
                }
                return this.save({ deleted: true }, options);
            },
            /** save this dataset, _Mark_ing it as undeleted */
            undelete: function(options) {
                if (!this.get("deleted") || this.get("purged")) {
                    return jQuery.when();
                }
                return this.save({ deleted: false }, options);
            },

            /** remove the file behind this dataset from the filesystem (if permitted) */
            purge: function _purge(options) {
                //TODO: use, override model.destroy, HDA.delete({ purge: true })
                if (this.get("purged")) {
                    return jQuery.when();
                }
                options = options || {};
                options.url = this.urls.purge;

                //TODO: ideally this would be a DELETE call to the api
                //  using purge async for now
                var hda = this;

                var xhr = jQuery.ajax(options);
                xhr.done((message, status, responseObj) => {
                    hda.set({ deleted: true, purged: true });
                });
                xhr.fail((xhr, status, message) => {
                    // Exception messages are hidden within error page including:  '...not allowed in this Galaxy instance.'
                    // unbury and re-add to xhr
                    var error = _l("Unable to purge dataset");
                    var messageBuriedInUnfortunatelyFormattedError =
                        "Removal of datasets by users " + "is not allowed in this Galaxy instance";
                    if (xhr.responseJSON && xhr.responseJSON.error) {
                        error = xhr.responseJSON.error;
                    } else if (xhr.responseText.indexOf(messageBuriedInUnfortunatelyFormattedError) !== -1) {
                        error = messageBuriedInUnfortunatelyFormattedError;
                    }
                    xhr.responseText = error;
                    hda.trigger("error", hda, xhr, options, _l(error), {
                        error: error
                    });
                });
                return xhr;
            },

            // ........................................................................ searching
            /** what attributes of an HDA will be used in a text search */
            searchAttributes: ["name", "file_ext", "genome_build", "misc_blurb", "misc_info", "annotation", "tags"],

            /** our attr keys don't often match the labels we display to the user - so, when using
             *      attribute specifiers ('name="bler"') in a term, allow passing in aliases for the
             *      following attr keys.
             */
            searchAliases: {
                title: "name",
                format: "file_ext",
                database: "genome_build",
                blurb: "misc_blurb",
                description: "misc_blurb",
                info: "misc_info",
                tag: "tags"
            },

            // ........................................................................ misc
            /** String representation */
            toString: function() {
                var nameAndId = this.get("id") || "";
                if (this.get("name")) {
                    nameAndId = `"${this.get("name")}",${nameAndId}`;
                }
                return `Dataset(${nameAndId})`;
            }
        }
    )
Ejemplo n.º 2
0
    BASE_MVC.mixin(BASE_MVC.SelectableViewMixin, BASE_MVC.DraggableViewMixin, {
        tagName: "div",
        className: "list-item",

        /** Set up the base class and all mixins */
        initialize: function(attributes) {
            ExpandableView.prototype.initialize.call(this, attributes);
            BASE_MVC.SelectableViewMixin.initialize.call(this, attributes);
            BASE_MVC.DraggableViewMixin.initialize.call(this, attributes);
            this._setUpListeners();
        },

        /** event listeners */
        _setUpListeners: function() {
            // hide the primary actions in the title bar when selectable and narrow
            this.on(
                "selectable",
                function(isSelectable) {
                    if (isSelectable) {
                        this.$(".primary-actions").hide();
                    } else {
                        this.$(".primary-actions").show();
                    }
                },
                this
            );
            return this;
        },

        // ........................................................................ rendering
        /** In this override, call methods to build warnings, titlebar and primary actions */
        _buildNewRender: function() {
            var $newRender = ExpandableView.prototype._buildNewRender.call(this);
            $newRender.children(".warnings").replaceWith(this._renderWarnings());
            $newRender.children(".title-bar").replaceWith(this._renderTitleBar());
            $newRender.children(".primary-actions").append(this._renderPrimaryActions());
            $newRender.find("> .title-bar .subtitle").replaceWith(this._renderSubtitle());
            return $newRender;
        },

        /** In this override, render the selector controls and set up dragging before the swap */
        _swapNewRender: function($newRender) {
            ExpandableView.prototype._swapNewRender.call(this, $newRender);
            if (this.selectable) {
                this.showSelector(0);
            }
            if (this.draggable) {
                this.draggableOn();
            }
            return this.$el;
        },

        /** Render any warnings the item may need to show (e.g. "I'm deleted") */
        _renderWarnings: function() {
            var view = this;
            var $warnings = $('<div class="warnings"></div>');
            var json = view.model.toJSON();
            //TODO:! unordered (map)
            _.each(view.templates.warnings, templateFn => {
                $warnings.append($(templateFn(json, view)));
            });
            return $warnings;
        },

        /** Render the title bar (the main/exposed SUMMARY dom element) */
        _renderTitleBar: function() {
            return $(this.templates.titleBar(this.model.toJSON(), this));
        },

        /** Return an array of jQ objects containing common/easily-accessible item controls */
        _renderPrimaryActions: function() {
            // override this
            return [];
        },

        /** Render the title bar (the main/exposed SUMMARY dom element) */
        _renderSubtitle: function() {
            return $(this.templates.subtitle(this.model.toJSON(), this));
        },

        // ......................................................................... events
        /** event map */
        events: {
            // expand the body when the title is clicked or when in focus and space or enter is pressed
            "click .title-bar": "_clickTitleBar",
            "keydown .title-bar": "_keyDownTitleBar",
            "click .selector": "toggleSelect"
        },

        /** expand when the title bar is clicked */
        _clickTitleBar: function(event) {
            event.stopPropagation();
            if (event.altKey) {
                this.toggleSelect(event);
                if (!this.selectable) {
                    this.showSelector();
                }
            } else {
                this.toggleExpanded();
            }
        },

        /** expand when the title bar is in focus and enter or space is pressed */
        _keyDownTitleBar: function(event) {
            // bail (with propagation) if keydown and not space or enter
            var KEYCODE_SPACE = 32;

            var KEYCODE_RETURN = 13;
            if (
                event &&
                event.type === "keydown" &&
                (event.keyCode === KEYCODE_SPACE || event.keyCode === KEYCODE_RETURN)
            ) {
                this.toggleExpanded();
                event.stopPropagation();
                return false;
            }
            return true;
        },

        // ......................................................................... misc
        /** String representation */
        toString: function() {
            var modelString = this.model ? `${this.model}` : "(no model)";
            return `ListItemView(${modelString})`;
        }
    })
Ejemplo n.º 3
0
import DATASET from "mvc/dataset/dataset-model";
import HISTORY_CONTENT from "mvc/history/history-content-model";
import BASE_MVC from "mvc/base-mvc";
import _l from "utils/localization";

//==============================================================================
var _super = DATASET.DatasetAssociation;

var hcontentMixin = HISTORY_CONTENT.HistoryContentMixin;
/** @class (HDA) model for a Galaxy dataset contained in and related to a history.
 */
var HistoryDatasetAssociation = _super.extend(
    BASE_MVC.mixin(
        hcontentMixin,
        /** @lends HistoryDatasetAssociation.prototype */ {
            /** default attributes for a model */
            defaults: _.extend({}, _super.prototype.defaults, hcontentMixin.defaults, {
                history_content_type: "dataset",
                model_class: "HistoryDatasetAssociation"
            })
        }
    )
);

//==============================================================================
export default {
    HistoryDatasetAssociation: HistoryDatasetAssociation
};
Ejemplo n.º 4
0
    BASE_MVC.mixin(
        searchableMixin,
        /** @lends Job.prototype */ {
            _logNamespace: logNamespace,

            /** default attributes for a model */
            defaults: {
                model_class: "Job",

                tool_id: null,
                exit_code: null,

                inputs: {},
                outputs: {},
                params: {},

                create_time: null,
                update_time: null,
                state: STATES.NEW
            },

            /** override to parse params on incomming */
            parse: function(response, options) {
                response.params = this.parseParams(response.params);
                return response;
            },

            /** override to treat param values as json */
            parseParams: function(params) {
                var newParams = {};
                _.each(params, (value, key) => {
                    newParams[key] = JSON.parse(value);
                });
                return newParams;
            },

            /** instance vars and listeners */
            initialize: function(attributes, options) {
                this.debug(`${this}(Job).initialize`, attributes, options);

                this.set("params", this.parseParams(this.get("params")), {
                    silent: true
                });

                this.outputCollection = attributes.outputCollection || new HISTORY_CONTENTS.HistoryContents([]);
                this._setUpListeners();
            },

            /** set up any event listeners
             *  event: state:ready  fired when this DA moves into/is already in a ready state
             */
            _setUpListeners: function() {
                // if the state has changed and the new state is a ready state, fire an event
                this.on("change:state", function(currModel, newState) {
                    this.log(`${this} has changed state:`, currModel, newState);
                    if (this.inReadyState()) {
                        this.trigger("state:ready", currModel, newState, this.previous("state"));
                    }
                });
            },

            // ........................................................................ common queries
            /** Is this job in a 'ready' state; where 'Ready' states are states where no
             *      processing is left to do on the server.
             */
            inReadyState: function() {
                return _.contains(STATES.READY_STATES, this.get("state"));
            },

            /** Does this model already contain detailed data (as opposed to just summary level data)? */
            hasDetails: function() {
                //?? this may not be reliable
                return !_.isEmpty(this.get("outputs"));
            },

            // ........................................................................ ajax
            /** root api url */
            urlRoot: `${Galaxy.root}api/jobs`,
            //url : function(){ return this.urlRoot; },

            // ........................................................................ searching
            // see base-mvc, SearchableModelMixin
            /** what attributes of an Job will be used in a text search */
            //searchAttributes : [
            //    'tool'
            //],

            // ........................................................................ misc
            /** String representation */
            toString: function() {
                return ["Job(", this.get("id"), ":", this.get("tool_id"), ")"].join("");
            }
        }
    )