<commit_before>// Create a static 'extends' method on the Object class
// This allows us to extend existing classes
// for classical object-oriented inheritance
Object.extend = function(superClass, definition) {
    var subClass = function() {};
    // Our constructor becomes the 'subclass'
    if (definition.constructor !== Object)
        subClass = definition.constructor;
    subClass.prototype = new superClass();
    for (var prop in definition) {
        if (prop != 'constructor')
            subClass.prototype[prop] = definition[prop];
    }
    return subClass;
};

var delay = (function(){
    var timer = 0;
    return function(callback, ms){
        clearTimeout (timer);
        timer = setTimeout(callback, ms);
    };
})();

var BoltExtender = Object.extend(Object, {

    selector: ".extend-bolt-container",
    messages:  {},

    constructor: function(){
        jQuery(this.selector).on("change", this, this.events.change);
        jQuery(this.selector).on("click", this, this.events.click);

        jQuery(document).ajaxStart(function() {
            // show loader on start
            active_interval = setInterval(function(){
                active_console.append(".");
            },1000);
        }).ajaxSuccess(function() {
            clearInterval(active_interval);
        });

        this.checkInstalled();
        this.liveSearch();

    },

    find: function(selector) {
        return jQuery(this.selector).find(selector);
    },

    setMessage: function(key, value) {
        this.messages[key]=value;
    },

    updateCheck: function() {
        var controller = this;

        controller.find('.update-container').show();
        var target = controller.find(".update-list-items");
        active_console = controller.find('.update-output');
        active_console.html(controller.messages['updating']);
        jQuery.get(baseurl+'check', function(data) {
            if(data.updates.length > 0 || data.installs.length >0) {
                for(var e in data.installs) {
                    var ext = data.installs[e];
                    target.append("
"+ext+"	 Install New Package
");
                }
                for(var e in data.updates) {
                    var ext = data.updates[e];
                    target.append("
"+ext+"	 Install Package Update
");
                }
                active_console.hide();
                controller.find('.update-list').show();
            } else {
                active_console.html(controller.messages['updated']);
            }
        });
    },

    updateRun: function() {
        var controller = this;

        controller.find('.update-container').show();
        var target = controller.find(".update-output" );
        active_console = target;
        active_console.html("Running update....");
        jQuery.get(baseurl+'update', function(data) {
            target.html(data);
            setTimeout(function(){
                controller.find('.update-container').hide();
            },7000);
        });
    },

    updatePackage: function(e) {
        var controller = this;
        var t = this.find('.update-output').html(controller.messages['updating']);
        var packageToUpdate = jQuery(e.target).data("package");
        t.show();
        active_console = t;
        jQuery.get(
            baseurl+'update?package='+packageToUpdate
        )
        .success(function(data) {
            controller.find('.update-output').html(data);
            controller.find('.update-list-items tr[data-package="'+packageToUpdate+'"]').remove();
            delay(function(){
                active_console.hide();
            }, 4000);
            if(controller.find('.update-list-items tbody tr').length <1) {
                controller.find('.update-container').hide();
            }
            controller.checkInstalled();
        });

        e.preventDefault();
    },

    installRun: function(e) {
        var controller = this;

        controller.find('.update-container').show();
        var target = controller.find(".update-output" );
        active_console = target;
        active_console.html(controller.messages['installAll']);
        jQuery.get(baseurl+'installAll', function(data) {
            target.html(data);
            delay(function(){
                controller.find('.update-container').hide();
            },7000);
        });
        e.stopPropagation();
        e.preventDefault();
    },

    checkInstalled: function() {
        var controller = this;

        controller.find('.installed-container').each(function(){
            active_console = controller.find(".installed-container .console");
            var target = jQuery(this).find('.installed-list');
            jQuery.get(baseurl+"installed", function(data) {
                target.show();
                if(data.length > 0) {
                    active_console.html(data.length + " installed extension(s).");
                    delay(function(){
                        controller.find(".installed-container .console").hide();
                    },7000);
                    target.find('.installed-list-items').html('');
                    for(var e in data) {
                        ext = data[e];
                        target.find('.installed-list-items').append("
"+ext["name"]+"	"+ext["version"]+"	 "+ext["type"]+"	 "+ext["descrip"]+"	 Uninstall
");
                    }
                } else {
                    target.find('.installed-list-items').html("
No Bolt Extensions installed.
");
                    active_console.hide();
                }
            });
        });
    },

    checkPackage: function(e) {
        var controller = this;
        var ext = this.find('input[name="check-package"]').val();
        active_console = this.find(".check-package");
        jQuery.ajax({
            url: site+'list.json',
            dataType: 'jsonp',
            data: {'name': ext},
        })
        .success(function(data) {
            if(data.packages.length <1) {
                return alert(controller.messages['extError']);
            }
            var pack = data.packages[0];
            var tpl = '
';
            tpl+='<input type="hidden" name="package-name" value="'+pack.name+'">'
            tpl+=''+pack.title+'
'+pack.name+'	<select name="package-version" class="form-control">';
            for(var v in pack.versions) {
                tpl+='<option value="'+pack.versions[v]+'">'+pack.versions[v]+'</option>'
            }
            tpl +='</select>		 Install Extension
';
            controller.find(".check-package").hide();
            controller.find(".installed-version-item").html(tpl);
            controller.find(".install-version-container").show();
        })
        .fail(function() {
            alert(controller.messages['extError']);
        });

        e.preventDefault();
    },

    install: function(e) {
        var controller = this;

        var package = controller.find('input[name="package-name"]').val();
        var version = controller.find('select[name="package-version"]').val();

        controller.find('.install-response-container').show();
        active_console = controller.find('.install-response-container .console');
        active_console.html(controller.messages['installing']);
        jQuery.get(
            baseurl+'install',
            {'package':package,'version':version}
        )
        .done(function(data) {
            active_console.html(data);

            setTimeout(function(){
                controller.find('.install-version-container').hide();
                controller.find('.install-response-container').hide();
            }, 2000);
            controller.find(".check-package").show()
            controller.find('input[name="check-package"]').val('');
            controller.checkInstalled();
        });
        e.preventDefault();
    },

    uninstall: function(e) {
        var controller = this;
        var t = this.find('.installed-container .console').html(controller.messages['removing']);
        t.show();
        active_console = t;
        jQuery.get(
            jQuery(e.target).attr("href")
        )
        .done(function(data) {
            controller.find('.installed-container .console').html(data);
            controller.checkInstalled();
            delay(function(){
                active_console.hide();
            }, 2000);
        });

        e.preventDefault();
    },

    liveSearch: function() {
        var livesearch = this.find('input[name="check-package"]');

        livesearch.on('keyup', function(e){
            var searchVal = jQuery(this).val();
            if(searchVal.length < 3) return;
            delay(function(){
                jQuery.ajax({
                    url: site+"list.json",
                    dataType: 'jsonp',
                    data: {'name': searchVal}
                })
                .success(function(data){
                    if(data['packages'].length <1 ) return;
                    var cont = livesearch.parent().find(".auto-search");
                    cont.html("").show();
                    for(var p in data['packages']) {
                        var t = data['packages'][p];
                        cont.append(""+t.name+"");
                    }
                    livesearch.on('blur', function(){
                       cont.fadeOut();
                    });
                });
            }, 500 );
        });
    },

    prefill: function(e) {
        var target = jQuery(e.target);
        console.log(target.text());
        this.find('input[name="check-package"]').val( target.text());
        target.parent().hide();
    },

    events: {
        change: function(e, t){
            var controller = e.data;

        },

        click: function(e, t){
            var controller = e.data;
            switch(jQuery(e.target).data('action')) {
                case "update-check"     : controller.updateCheck(); break;
                case "update-run"       : controller.updateRun(); break;
                case "update-package"   : controller.updatePackage(e.originalEvent); break;
                case "check-package"    : controller.checkPackage(e.originalEvent); break;
                case "uninstall-package": controller.uninstall(e.originalEvent); break;
                case "install-package"  : controller.install(e.originalEvent); break;
                case "prefill-package"  : controller.prefill(e.originalEvent); break;
                case "install-run"      : controller.installRun(e.originalEvent); break;
            }
        }

    }

});<commit_msg>increase timeout before removing install response<commit_after>// Create a static 'extends' method on the Object class
// This allows us to extend existing classes
// for classical object-oriented inheritance
Object.extend = function(superClass, definition) {
    var subClass = function() {};
    // Our constructor becomes the 'subclass'
    if (definition.constructor !== Object)
        subClass = definition.constructor;
    subClass.prototype = new superClass();
    for (var prop in definition) {
        if (prop != 'constructor')
            subClass.prototype[prop] = definition[prop];
    }
    return subClass;
};

var delay = (function(){
    var timer = 0;
    return function(callback, ms){
        clearTimeout (timer);
        timer = setTimeout(callback, ms);
    };
})();

var BoltExtender = Object.extend(Object, {

    selector: ".extend-bolt-container",
    messages:  {},

    constructor: function(){
        jQuery(this.selector).on("change", this, this.events.change);
        jQuery(this.selector).on("click", this, this.events.click);

        jQuery(document).ajaxStart(function() {
            // show loader on start
            active_interval = setInterval(function(){
                active_console.append(".");
            },1000);
        }).ajaxSuccess(function() {
            clearInterval(active_interval);
        });

        this.checkInstalled();
        this.liveSearch();

    },

    find: function(selector) {
        return jQuery(this.selector).find(selector);
    },

    setMessage: function(key, value) {
        this.messages[key]=value;
    },

    updateCheck: function() {
        var controller = this;

        controller.find('.update-container').show();
        var target = controller.find(".update-list-items");
        active_console = controller.find('.update-output');
        active_console.html(controller.messages['updating']);
        jQuery.get(baseurl+'check', function(data) {
            if(data.updates.length > 0 || data.installs.length >0) {
                for(var e in data.installs) {
                    var ext = data.installs[e];
                    target.append("
"+ext+"	 Install New Package
");
                }
                for(var e in data.updates) {
                    var ext = data.updates[e];
                    target.append("
"+ext+"	 Install Package Update
");
                }
                active_console.hide();
                controller.find('.update-list').show();
            } else {
                active_console.html(controller.messages['updated']);
            }
        });
    },

    updateRun: function() {
        var controller = this;

        controller.find('.update-container').show();
        var target = controller.find(".update-output" );
        active_console = target;
        active_console.html("Running update....");
        jQuery.get(baseurl+'update', function(data) {
            target.html(data);
            setTimeout(function(){
                controller.find('.update-container').hide();
            },7000);
        });
    },

    updatePackage: function(e) {
        var controller = this;
        var t = this.find('.update-output').html(controller.messages['updating']);
        var packageToUpdate = jQuery(e.target).data("package");
        t.show();
        active_console = t;
        jQuery.get(
            baseurl+'update?package='+packageToUpdate
        )
        .success(function(data) {
            controller.find('.update-output').html(data);
            controller.find('.update-list-items tr[data-package="'+packageToUpdate+'"]').remove();
            delay(function(){
                active_console.hide();
            }, 4000);
            if(controller.find('.update-list-items tbody tr').length <1) {
                controller.find('.update-container').hide();
            }
            controller.checkInstalled();
        });

        e.preventDefault();
    },

    installRun: function(e) {
        var controller = this;

        controller.find('.update-container').show();
        var target = controller.find(".update-output" );
        active_console = target;
        active_console.html(controller.messages['installAll']);
        jQuery.get(baseurl+'installAll', function(data) {
            target.html(data);
            delay(function(){
                controller.find('.update-container').hide();
            },7000);
        });
        e.stopPropagation();
        e.preventDefault();
    },

    checkInstalled: function() {
        var controller = this;

        controller.find('.installed-container').each(function(){
            active_console = controller.find(".installed-container .console");
            var target = jQuery(this).find('.installed-list');
            jQuery.get(baseurl+"installed", function(data) {
                target.show();
                if(data.length > 0) {
                    active_console.html(data.length + " installed extension(s).");
                    delay(function(){
                        controller.find(".installed-container .console").hide();
                    },7000);
                    target.find('.installed-list-items').html('');
                    for(var e in data) {
                        ext = data[e];
                        target.find('.installed-list-items').append("
"+ext["name"]+"	"+ext["version"]+"	 "+ext["type"]+"	 "+ext["descrip"]+"	 Uninstall
");
                    }
                } else {
                    target.find('.installed-list-items').html("
No Bolt Extensions installed.
");
                    active_console.hide();
                }
            });
        });
    },

    checkPackage: function(e) {
        var controller = this;
        var ext = this.find('input[name="check-package"]').val();
        active_console = this.find(".check-package");
        jQuery.ajax({
            url: site+'list.json',
            dataType: 'jsonp',
            data: {'name': ext},
        })
        .success(function(data) {
            if(data.packages.length <1) {
                return alert(controller.messages['extError']);
            }
            var pack = data.packages[0];
            var tpl = '
';
            tpl+='<input type="hidden" name="package-name" value="'+pack.name+'">'
            tpl+=''+pack.title+'
'+pack.name+'	<select name="package-version" class="form-control">';
            for(var v in pack.versions) {
                tpl+='<option value="'+pack.versions[v]+'">'+pack.versions[v]+'</option>'
            }
            tpl +='</select>		 Install Extension
';
            controller.find(".check-package").hide();
            controller.find(".installed-version-item").html(tpl);
            controller.find(".install-version-container").show();
        })
        .fail(function() {
            alert(controller.messages['extError']);
        });

        e.preventDefault();
    },

    install: function(e) {
        var controller = this;

        var package = controller.find('input[name="package-name"]').val();
        var version = controller.find('select[name="package-version"]').val();

        controller.find('.install-response-container').show();
        active_console = controller.find('.install-response-container .console');
        active_console.html(controller.messages['installing']);
        jQuery.get(
            baseurl+'install',
            {'package':package,'version':version}
        )
        .done(function(data) {
            active_console.html(data);

            setTimeout(function(){
                controller.find('.install-version-container').hide();
                controller.find('.install-response-container').hide();
            }, 7000);
            controller.find(".check-package").show()
            controller.find('input[name="check-package"]').val('');
            controller.checkInstalled();
        });
        e.preventDefault();
    },

    uninstall: function(e) {
        var controller = this;
        var t = this.find('.installed-container .console').html(controller.messages['removing']);
        t.show();
        active_console = t;
        jQuery.get(
            jQuery(e.target).attr("href")
        )
        .done(function(data) {
            controller.find('.installed-container .console').html(data);
            controller.checkInstalled();
            delay(function(){
                active_console.hide();
            }, 2000);
        });

        e.preventDefault();
    },

    liveSearch: function() {
        var livesearch = this.find('input[name="check-package"]');

        livesearch.on('keyup', function(e){
            var searchVal = jQuery(this).val();
            if(searchVal.length < 3) return;
            delay(function(){
                jQuery.ajax({
                    url: site+"list.json",
                    dataType: 'jsonp',
                    data: {'name': searchVal}
                })
                .success(function(data){
                    if(data['packages'].length <1 ) return;
                    var cont = livesearch.parent().find(".auto-search");
                    cont.html("").show();
                    for(var p in data['packages']) {
                        var t = data['packages'][p];
                        cont.append(""+t.name+"");
                    }
                    livesearch.on('blur', function(){
                       cont.fadeOut();
                    });
                });
            }, 500 );
        });
    },

    prefill: function(e) {
        var target = jQuery(e.target);
        console.log(target.text());
        this.find('input[name="check-package"]').val( target.text());
        target.parent().hide();
    },

    events: {
        change: function(e, t){
            var controller = e.data;

        },

        click: function(e, t){
            var controller = e.data;
            switch(jQuery(e.target).data('action')) {
                case "update-check"     : controller.updateCheck(); break;
                case "update-run"       : controller.updateRun(); break;
                case "update-package"   : controller.updatePackage(e.originalEvent); break;
                case "check-package"    : controller.checkPackage(e.originalEvent); break;
                case "uninstall-package": controller.uninstall(e.originalEvent); break;
                case "install-package"  : controller.install(e.originalEvent); break;
                case "prefill-package"  : controller.prefill(e.originalEvent); break;
                case "install-run"      : controller.installRun(e.originalEvent); break;
            }
        }

    }

});<|endoftext|>


<commit_before>/*global document,$,alert,window,JSON */

// Global experiment data
var current_experiment = {};

// Support file types
var SUPPORTED_FILES = [
    "csv", "tsv", "bed", "gff", "gtf", "vcf", "bam", "fastq", "fq"
];

var FILE_TYPE_PATTERNS = new RegExp("(:?" + SUPPORTED_FILES.join("|") + ")$");

// Template support
var snp_templates  = {};
var align_templates = {};

function autodetect_file_type(file) {
    var stripped_file = file.replace(/.gz$/, '');

    if (FILE_TYPE_PATTERNS.test(stripped_file)) {
        return stripped_file.match(FILE_TYPE_PATTERNS)[0];
    }
}

function error_help(s) {
    $('#error_help_text')
        .html(s)
        .show()
        .delay(10*1000)
        .fadeOut(1500);
}

function check_login() {
    var logged_in = false;

    $.ajax({
        async: false,
        data: {
            fname: 'check_login',
        },
        success : function(rc) {
            logged_in = rc;
        }
    });

    return logged_in;
}

//
// Requires a done callback and a data object to pass to the callback
//
function Wizard(options) {
    this.done = options.done;
    this.data = options.data;
    this.steps = [];
    this.currentIndex = 0;
    this.initialize();
}

$.extend(Wizard.prototype, {
    initialize: function() {
        this.el = $($("#wizard-template").html());
        this.tabs = this.el.find(".sections");
        this.next = this.el.find(".next");
        this.prev = this.el.find(".prev");
        this.done = this.el.find(".done");
        this.viewer = this.el.find("#step-container");
        this.notifications = this.el.find("#error_help_text");

        // jQuery events
        this.prev.unbind().click(this.movePrevious.bind(this));
        this.next.unbind().click(this.moveNext.bind(this));
        this.done.unbind().click(this.submit.bind(this));
    },

    at_first: function() {
        return this.currentIndex === 0;
    },

    at_last: function() {
        return this.currentIndex >= (this.steps.length - 1);
    },

    render: function() {
        var titles = this.steps.map(function(step) {
            return $("

", { text:  step.title });
        });

        this.tabs.html(titles);
        titles[this.currentIndex].addClass("active");

        var step = this.steps[this.currentIndex];
        if (step.render) {
            step.render();
        }
        this.viewer.html(step.el);

        if (this.at_first()) {
            this.prev.attr("disabled", 1);
        } else {
            this.prev.removeAttr("disabled");
        }

        if (this.at_last()) {
            this.next.attr("disabled", 1);
            this.done.removeAttr("disabled");
        } else {
            this.next.removeAttr("disabled");
            this.done.attr("disabled", 1);
        }
    },

    movePrevious: function() {
        if (!this.at_first()) {
            this.currentIndex--;
            this.render();
            this.notifications.stop(true, true).fadeOut(1500);
        }
    },

    moveNext: function() {
        var step = this.steps[this.currentIndex];

        if (!this.at_last() && step.is_valid()) {
            this.currentIndex++;
            this.render();
            this.notifications.stop(true, true).fadeOut(1500);
        }
    },

    message: function(message) {
        this.notifications.html(message)
            .show()
            .stop(true, true)
            .delay(10*1000)
            .fadeOut(1500);
    },

    submit: function() {
        if (!check_login()) {
            this.message('Your session has expired, please log in again.');
            return;
        }

        if (this.at_last()) {
            this.done(this.data);
        }
    },

    // Expects a view with render, is_valid methods and a element property el
    addStep: function(step, index) {
        if (index !== undefined && index < this.steps.length) {
            this.steps.slice(index, 0, step);
        } else {
            this.steps.push(step);
        }
    }
});

function DataView(experiment) {
    this.experiment = experiment || {};
    this.title = "Data";
    this.files = [];
    this.initialize();
}

$.extend(DataView.prototype, {
    initialize: function() {
        this.el = $($("#data-template").html());
        this.file_selector = $($("#selector-template").html());
        this.selector_container = this.el.find("#selector_container");
    },

    render: function() {
        //FIXME: This selector should be in another view
        var selector = this.file_selector.clone();
        this.selector_container.empty();
        selector.appendTo(this.selector_container);
        selector.tabs();

        //FIXME: selector view should track the current path
        if (pageObj.current_path) {
            irods_get_path(pageObj.current_path);
        } else {
            irods_get_path();
        }

        selector.find('#input_url').bind('keyup focus click', function() {
            var button = selector.find("#ftp_get_button"),
                disabled = !selector.find("#input_url").val();

            button.toggleClass("ui-state-disabled", disabled);
        });

        selector.find('#input_accn').bind('keyup focus click', function() {
            var button = selector.find("#ncbi_get_button"),
                disabled = !selector.find("#input_accn").val();

            button.toggleClass("ui-state-disabled", disabled);
        });

        selector.find('#input_upload_file').fileupload({
            dataType: 'json',
            add: this.add.bind(this),
            done: this.uploaded.bind(this)
        });
    },

    add: function(e, data) {
        var filename = data.files[0].name;

        if ( !add_file_to_list(filename, 'file://'+filename) ) {
            alert('File already exists.');
        } else {
            // mdb 10/29/13 - moved from above to prevent stale load_id value, issue 236
            $('#input_upload_file').fileupload('option', { formData: {
                fname: 'upload_file',
                load_id: load_id
            }});

            data.submit();
        }
    },

    uploaded: function(e, data) {
        this.files.push({
            name: 'file://'+data.result.filename,
            path: data.result.path,
            size: units(data.result.size)
        });

        finish_file_in_list('file', 'file://'+data.result.filename, data.result.path, data.result.size);
    },

    is_valid: function() {
        var items = get_selected_files();

        if (!items.length) {
            return false;
        }

        var file_type = items[0].file_type = this.el.find("#select_file_type option:selected").val();

        if (items === null) {
            error_help('Files are still being transferred, please wait.');
            return false;
        }

        if (items.length === 0) {
            error_help('Please select a data file.');
            return false;
        }

        if (!file_type) {
            error_help("Please select the file type to continue");
            return false;
        }

        $.extend(current_experiment, {
            data: items
        });

        return true;
    }
});

function DescriptionView(experiment) {
    this.experiment = experiment;
    this.sources = undefined;
    this.title = "Describe your experiment";
    this.initialize();
}

$.extend(DescriptionView.prototype, {
    initialize: function() {
        this.el = $($("#description-template").html());
        this.edit_source = this.el.find("#edit_source");
        this.edit_genome = this.el.find("#edit_genome");

        this.get_sources();
    },

    get_sources: function() {
        var self = this;

        return $.ajax({
            dataType: "json",
            data: {
                fname: 'get_sources',
            },
            success : function(sources) {
                self.sources = sources;

                if (sources) {
                    self.edit_source.autocomplete({source: sources});
                }
            },
        });
    },

    render: function() {
        var self = this;

        // jQuery Events
        this.edit_genome.unbind().change(function() {
            // Reset gid when item has changed
            self.experiment.gid = undefined;
        });

        // jQuery UI
        this.edit_genome.autocomplete({
            source:[],
            select: function(event, ui) {
                $(this).val(ui.item.label);
                self.experiment.gid = ui.item.value;
                return false; // Prevent the widget from inserting the value.
            },

            focus: function(event, ui) {
                //$("#edit_genome").val(ui.item.label);
                return false; // Prevent the widget from inserting the value.
            }
        });

        this.edit_source.autocomplete({source: this.sources});
    },

    is_valid: function() {
        var name = this.el.find('#edit_name').val();
        var description = this.el.find('#edit_description').val();
        var version = this.el.find('#edit_version').val();
        var restricted = $('#restricted').is(':checked');
        var genome = this.el.find('#edit_genome').val();
        var aligner = this.el.find("#alignment").find(":checked").val();
        var ignore_cb = this.el.find('#ignore_missing_chrs');
        var ignore_missing_chrs = ignore_cb.is(':checked');

        if (!name) {
            error_help('Please specify an experiment name.');
            return false;
        }
        if (!version) {
            error_help('Please specify an experiment version.');
            return false;
        }

        var source = $('#edit_source').val();
        if (!source || source === 'Search') {
            error_help('Please specify a data source.');
            return false;
        }

        if (!genome || genome === 'Search' || !this.experiment.gid) {
            error_help('Please specify a genome.');
            return false;
        }

       $.extend(this.experiment, {
            description: {
                name: name,
                description: description,
                version: version,
                restricted: restricted,
                genome: genome,
            }
        });

        return true;
    },
});

function render_template(template, container) {
    container.empty()
        .hide()
        .append(template)
        .slideDown();
}

function OptionsView(experiment) {
    this.experiment = experiment;
    this.title = "Options";
    this.initialize();
}

$.extend(OptionsView.prototype, {
    initialize: function() {
        this.el = $($("#options-template").html());
        this.snp_container = this.el.find("#snp-container");
        this.align_container = this.el.find("#align-container");
    },

    is_valid: function() {
        return true;
    },

    render: function() {
        var self = this;

        // jQuery UI
        this.el.find("#edit_user").unbind().autocomplete({
            source:[],
            focus: function() { return false; },
        });

        // jQuery events
        this.el.find("#snps").change(this.update_snp.bind(this));
        this.el.find("[name=aligner]").change(this.update_aligner.bind(this));
        this.el.find("#snp-method").unbind().change(function() {
            var selected = $(this).val();
            render_template(snp_templates[selected], self.snp_container);
        });
    },

    update_snp: function (ev) {
        var enabled = $(ev.target).is(":checked"),
            method = this.el.find("#snp-method");

        var el = $(document.getElementById(method.val()));

        if (enabled) {
            el.show();
            method.removeAttr("disabled");
            this.snp_container.slideDown();
            var initial = $("#snp-method").val();
            render_template(snp_templates[initial], this.snp_container);
        } else {
            method.attr("disabled", 1);
            this.snp_container.slideUp();
        }
    },

    update_aligner: function() {
        var selected = $("#alignment").find(":checked").val();
        render_template(align_templates[selected], this.align_container);
    }
});

function ConfirmationView(experiment) {
    this.experiment = experiment;
    this.initialize();
    this.title = "Review and Load";
}

$.extend(ConfirmationView.prototype, {
    initialize: function() {
        this.el = $($("#confirm-template").html());
        this.description = this.el.find(".confirm-description");
        this.data = this.el.find(".confirm-data");
        this.options = this.el.find(".confirm-options");
        this.pair_template = $($("#summary-pair-template").html());
    },

    render: function() {
        this.renderDescription(this.experiment.description);
        this.renderData(this.experiment.data);
        this.renderOptions(this.experiment.options);
    },

    renderDescription: function(description) {
        var key, newpair;
        this.description.empty();

        // Description Confirmation
        for(key in description) {
            if (description.hasOwnProperty(key)) {
                newpair = this.pair_template.clone();
                newpair.find(".name").html(key);
                newpair.find(".data").html(description[key]);
                this.description.append(newpair);
            }
        }
    },

    renderData: function(data) {
        var index, newpair;

        for(index = 0; index < data.length; index++) {
            newpair = this.pair_template.clone();
            newpair.find(".name").html("File");
            newpair.find(".data").html(data[index].path);
            this.data.append(newpair);
        }
    },

    renderOptions: function(options) {
        this.options.empty();
    },

    is_valid: function() {
        return true;
    }
});

function load(experiment) {
    // Open status dialog right away - issue 101
    reset_log();
    $('#load_dialog').dialog('open');
    $('#load_log').html('Initializing ...');
    newLoad = true;

    $.ajax({
        data: experiment,
        dataType: "json",
        contentType: "application/json",
        success: function(obj) {
            if (obj && obj.error) {
                alert(obj.error);
                return;
            }

            // Set link in status dialog
            $('#loading_msg span a').attr('href', obj.link).html(obj.link);

            // Start status update
            if (obj.job_id) { // JEX status for load FASTQ
                job_id = obj.job_id;
                window.history.pushState({}, "Title", PAGE_NAME + "?job_id=" + obj.job_id); // Add job_id to browser URL
                update_dialog(STATUS_URL + obj.job_id, pageObj.user, "#load_dialog", progress_formatter);
            }
        }
    });
}

function initialize_wizard() {
    var root = $("#wizard-container");
    var wizard = new Wizard({ done: load, data: current_experiment });
    wizard.addStep(new DescriptionView(current_experiment));
    wizard.addStep(new DataView(current_experiment));
    wizard.addStep(new OptionsView(current_experiment));
    wizard.addStep(new ConfirmationView(current_experiment));
    wizard.render();

    // Create psudeo templates
    snp_templates = {
        coge: $($("#coge-snp-template").html()),
        gatk: $($("#samtools-snp-template").html()),
        platypus: $($("#platypus-snp-template").html()),
        samtools: $($("#gatk-snp-template").html())
    };
    align_templates = {
        gsnap: $($("#gsnap-template").html()),
        tophat: $($("#tophat-template").html())
    };

    root.append(wizard.el);
    return wizard;
}
<commit_msg>fix submission function<commit_after>/*global document,$,alert,window,JSON */

// Global experiment data
var current_experiment = {};

// Support file types
var SUPPORTED_FILES = [
    "csv", "tsv", "bed", "gff", "gtf", "vcf", "bam", "fastq", "fq"
];

var FILE_TYPE_PATTERNS = new RegExp("(:?" + SUPPORTED_FILES.join("|") + ")$");

// Template support
var snp_templates  = {};
var align_templates = {};

function autodetect_file_type(file) {
    var stripped_file = file.replace(/.gz$/, '');

    if (FILE_TYPE_PATTERNS.test(stripped_file)) {
        return stripped_file.match(FILE_TYPE_PATTERNS)[0];
    }
}

function error_help(s) {
    $('#error_help_text')
        .html(s)
        .show()
        .delay(10*1000)
        .fadeOut(1500);
}

function check_login() {
    var logged_in = false;

    $.ajax({
        async: false,
        data: {
            fname: 'check_login',
        },
        success : function(rc) {
            logged_in = rc;
        }
    });

    return logged_in;
}

//
// Requires a done callback and a data object to pass to the callback
//
function Wizard(options) {
    this.completed = options.completed;
    this.data = options.data;
    this.steps = [];
    this.currentIndex = 0;
    this.initialize();
}

$.extend(Wizard.prototype, {
    initialize: function() {
        this.el = $($("#wizard-template").html());
        this.tabs = this.el.find(".sections");
        this.next = this.el.find(".next");
        this.prev = this.el.find(".prev");
        this.done = this.el.find(".done");
        this.viewer = this.el.find("#step-container");
        this.notifications = this.el.find("#error_help_text");

        // jQuery events
        this.prev.unbind().click(this.movePrevious.bind(this));
        this.next.unbind().click(this.moveNext.bind(this));
        this.done.unbind().click(this.submit.bind(this));
    },

    at_first: function() {
        return this.currentIndex === 0;
    },

    at_last: function() {
        return this.currentIndex >= (this.steps.length - 1);
    },

    render: function() {
        var titles = this.steps.map(function(step) {
            return $("

", { text:  step.title });
        });

        this.tabs.html(titles);
        titles[this.currentIndex].addClass("active");

        var step = this.steps[this.currentIndex];
        if (step.render) {
            step.render();
        }
        this.viewer.html(step.el);

        if (this.at_first()) {
            this.prev.attr("disabled", 1);
        } else {
            this.prev.removeAttr("disabled");
        }

        if (this.at_last()) {
            this.next.attr("disabled", 1);
            this.done.removeAttr("disabled");
        } else {
            this.next.removeAttr("disabled");
            this.done.attr("disabled", 1);
        }
    },

    movePrevious: function() {
        if (!this.at_first()) {
            this.currentIndex--;
            this.render();
            this.notifications.stop(true, true).fadeOut(1500);
        }
    },

    moveNext: function() {
        var step = this.steps[this.currentIndex];

        if (!this.at_last() && step.is_valid()) {
            this.currentIndex++;
            this.render();
            this.notifications.stop(true, true).fadeOut(1500);
        }
    },

    message: function(message) {
        this.notifications.html(message)
            .show()
            .stop(true, true)
            .delay(10*1000)
            .fadeOut(1500);
    },

    submit: function() {
        if (!check_login()) {
            this.message('Your session has expired, please log in again.');
            return;
        }

        if (this.at_last()) {
            this.completed(this.data);
        }
    },

    // Expects a view with render, is_valid methods and a element property el
    addStep: function(step, index) {
        if (index !== undefined && index < this.steps.length) {
            this.steps.slice(index, 0, step);
        } else {
            this.steps.push(step);
        }
    }
});

function DataView(experiment) {
    this.experiment = experiment || {};
    this.title = "Data";
    this.files = [];
    this.initialize();
}

$.extend(DataView.prototype, {
    initialize: function() {
        this.el = $($("#data-template").html());
        this.file_selector = $($("#selector-template").html());
        this.selector_container = this.el.find("#selector_container");
    },

    render: function() {
        //FIXME: This selector should be in another view
        var selector = this.file_selector.clone();
        this.selector_container.empty();
        selector.appendTo(this.selector_container);
        selector.tabs();

        //FIXME: selector view should track the current path
        if (pageObj.current_path) {
            irods_get_path(pageObj.current_path);
        } else {
            irods_get_path();
        }

        selector.find('#input_url').bind('keyup focus click', function() {
            var button = selector.find("#ftp_get_button"),
                disabled = !selector.find("#input_url").val();

            button.toggleClass("ui-state-disabled", disabled);
        });

        selector.find('#input_accn').bind('keyup focus click', function() {
            var button = selector.find("#ncbi_get_button"),
                disabled = !selector.find("#input_accn").val();

            button.toggleClass("ui-state-disabled", disabled);
        });

        selector.find('#input_upload_file').fileupload({
            dataType: 'json',
            add: this.add.bind(this),
            done: this.uploaded.bind(this)
        });
    },

    add: function(e, data) {
        var filename = data.files[0].name;

        if ( !add_file_to_list(filename, 'file://'+filename) ) {
            alert('File already exists.');
        } else {
            // mdb 10/29/13 - moved from above to prevent stale load_id value, issue 236
            $('#input_upload_file').fileupload('option', { formData: {
                fname: 'upload_file',
                load_id: load_id
            }});

            data.submit();
        }
    },

    uploaded: function(e, data) {
        this.files.push({
            name: 'file://'+data.result.filename,
            path: data.result.path,
            size: units(data.result.size)
        });

        finish_file_in_list('file', 'file://'+data.result.filename, data.result.path, data.result.size);
    },

    is_valid: function() {
        var items = get_selected_files();

        if (!items.length) {
            return false;
        }

        var file_type = items[0].file_type = this.el.find("#select_file_type option:selected").val();

        if (items === null) {
            error_help('Files are still being transferred, please wait.');
            return false;
        }

        if (items.length === 0) {
            error_help('Please select a data file.');
            return false;
        }

        if (!file_type) {
            error_help("Please select the file type to continue");
            return false;
        }

        $.extend(current_experiment, {
            data: items
        });

        return true;
    }
});

function DescriptionView(experiment) {
    this.experiment = experiment;
    this.sources = undefined;
    this.title = "Describe your experiment";
    this.initialize();
}

$.extend(DescriptionView.prototype, {
    initialize: function() {
        this.el = $($("#description-template").html());
        this.edit_source = this.el.find("#edit_source");
        this.edit_genome = this.el.find("#edit_genome");

        this.get_sources();
    },

    get_sources: function() {
        var self = this;

        return $.ajax({
            dataType: "json",
            data: {
                fname: 'get_sources',
            },
            success : function(sources) {
                self.sources = sources;

                if (sources) {
                    self.edit_source.autocomplete({source: sources});
                }
            },
        });
    },

    render: function() {
        var self = this;

        // jQuery Events
        this.edit_genome.unbind().change(function() {
            // Reset gid when item has changed
            self.experiment.gid = undefined;
        });

        // jQuery UI
        this.edit_genome.autocomplete({
            source:[],
            select: function(event, ui) {
                $(this).val(ui.item.label);
                self.experiment.gid = ui.item.value;
                return false; // Prevent the widget from inserting the value.
            },

            focus: function(event, ui) {
                //$("#edit_genome").val(ui.item.label);
                return false; // Prevent the widget from inserting the value.
            }
        });

        this.edit_source.autocomplete({source: this.sources});
    },

    is_valid: function() {
        var name = this.el.find('#edit_name').val();
        var description = this.el.find('#edit_description').val();
        var version = this.el.find('#edit_version').val();
        var restricted = $('#restricted').is(':checked');
        var genome = this.el.find('#edit_genome').val();
        var aligner = this.el.find("#alignment").find(":checked").val();
        var ignore_cb = this.el.find('#ignore_missing_chrs');
        var ignore_missing_chrs = ignore_cb.is(':checked');

        if (!name) {
            error_help('Please specify an experiment name.');
            return false;
        }
        if (!version) {
            error_help('Please specify an experiment version.');
            return false;
        }

        var source = $('#edit_source').val();
        if (!source || source === 'Search') {
            error_help('Please specify a data source.');
            return false;
        }

        if (!genome || genome === 'Search' || !this.experiment.gid) {
            error_help('Please specify a genome.');
            return false;
        }

       $.extend(this.experiment, {
            description: {
                name: name,
                description: description,
                version: version,
                restricted: restricted,
                genome: genome,
            }
        });

        return true;
    },
});

function render_template(template, container) {
    container.empty()
        .hide()
        .append(template)
        .slideDown();
}

function OptionsView(experiment) {
    this.experiment = experiment;
    this.title = "Options";
    this.initialize();
}

$.extend(OptionsView.prototype, {
    initialize: function() {
        this.el = $($("#options-template").html());
        this.snp_container = this.el.find("#snp-container");
        this.align_container = this.el.find("#align-container");
    },

    is_valid: function() {
        return true;
    },

    render: function() {
        var self = this;

        // jQuery UI
        this.el.find("#edit_user").unbind().autocomplete({
            source:[],
            focus: function() { return false; },
        });

        // jQuery events
        this.el.find("#snps").change(this.update_snp.bind(this));
        this.el.find("[name=aligner]").change(this.update_aligner.bind(this));
        this.el.find("#snp-method").unbind().change(function() {
            var selected = $(this).val();
            render_template(snp_templates[selected], self.snp_container);
        });
    },

    update_snp: function (ev) {
        var enabled = $(ev.target).is(":checked"),
            method = this.el.find("#snp-method");

        var el = $(document.getElementById(method.val()));

        if (enabled) {
            el.show();
            method.removeAttr("disabled");
            this.snp_container.slideDown();
            var initial = $("#snp-method").val();
            render_template(snp_templates[initial], this.snp_container);
        } else {
            method.attr("disabled", 1);
            this.snp_container.slideUp();
        }
    },

    update_aligner: function() {
        var selected = $("#alignment").find(":checked").val();
        render_template(align_templates[selected], this.align_container);
    }
});

function ConfirmationView(experiment) {
    this.experiment = experiment;
    this.initialize();
    this.title = "Review and Load";
}

$.extend(ConfirmationView.prototype, {
    initialize: function() {
        this.el = $($("#confirm-template").html());
        this.description = this.el.find(".confirm-description");
        this.data = this.el.find(".confirm-data");
        this.options = this.el.find(".confirm-options");
        this.pair_template = $($("#summary-pair-template").html());
    },

    render: function() {
        this.renderDescription(this.experiment.description);
        this.renderData(this.experiment.data);
        this.renderOptions(this.experiment.options);
    },

    renderDescription: function(description) {
        var key, newpair;
        this.description.empty();

        // Description Confirmation
        for(key in description) {
            if (description.hasOwnProperty(key)) {
                newpair = this.pair_template.clone();
                newpair.find(".name").html(key);
                newpair.find(".data").html(description[key]);
                this.description.append(newpair);
            }
        }
    },

    renderData: function(data) {
        var index, newpair;

        for(index = 0; index < data.length; index++) {
            newpair = this.pair_template.clone();
            newpair.find(".name").html("File");
            newpair.find(".data").html(data[index].path);
            this.data.append(newpair);
        }
    },

    renderOptions: function(options) {
        this.options.empty();
    },

    is_valid: function() {
        return true;
    }
});

function load(experiment) {
    // Open status dialog right away - issue 101
    reset_log();
    $('#load_dialog').dialog('open');
    $('#load_log').html('Initializing ...');
    newLoad = true;

    $.ajax({
        data: experiment,
        dataType: "json",
        contentType: "application/json",
        success: function(obj) {
            if (obj && obj.error) {
                alert(obj.error);
                return;
            }

            // Set link in status dialog
            $('#loading_msg span a').attr('href', obj.link).html(obj.link);

            // Start status update
            if (obj.job_id) { // JEX status for load FASTQ
                job_id = obj.job_id;
                window.history.pushState({}, "Title", PAGE_NAME + "?job_id=" + obj.job_id); // Add job_id to browser URL
                update_dialog(STATUS_URL + obj.job_id, pageObj.user, "#load_dialog", progress_formatter);
            }
        }
    });
}

function initialize_wizard() {
    var root = $("#wizard-container");
    var wizard = new Wizard({ completed: load, data: current_experiment });
    wizard.addStep(new DescriptionView(current_experiment));
    wizard.addStep(new DataView(current_experiment));
    wizard.addStep(new OptionsView(current_experiment));
    wizard.addStep(new ConfirmationView(current_experiment));
    wizard.render();

    // Create psudeo templates
    snp_templates = {
        coge: $($("#coge-snp-template").html()),
        gatk: $($("#samtools-snp-template").html()),
        platypus: $($("#platypus-snp-template").html()),
        samtools: $($("#gatk-snp-template").html())
    };
    align_templates = {
        gsnap: $($("#gsnap-template").html()),
        tophat: $($("#tophat-template").html())
    };

    root.append(wizard.el);
    return wizard;
}
<|endoftext|>


<commit_before>
import {
  filter,
  find
} from 'lodash/collection'
import { isNil } from 'lodash/lang'
import localStorageMgr from 'js/utils/localstorage-mgr'
import {
  STORAGE_EXPERIMENT_PREFIX
} from 'js/constants'
import {
  excludeUsersWhoJoinedWithin,
  includeIfAnyIsTrue,
  onlyIncludeNewUsers
} from 'js/utils/experimentFilters'
import environment from 'js/relay-env'
import UpdateUserExperimentGroupsMutation from 'js/mutations/UpdateUserExperimentGroupsMutation'

const noneGroupKey = 'NONE'

export const createExperiment = ({ name, active = false, disabled = false, groups,
  filters = [], percentageOfExistingUsersInExperiment = 100.0,
  percentageOfNewUsersInExperiment = 100.0 }) => {
  if (!name) {
    throw new Error('An experiment must have a unique "name" value.')
  }
  return {
    // A unique code (without spaces) for this experiment
    name,
    // If true, we will assign users a group in the experiment. If false,
    // we will not assign users to any group.
    active,
    // If true, we will always return the "none" test group when getting
    // the user's experiment group, even if the user is assigned to
    // another group. This should effectively disable the effects of the
    // experiment.
    disabled,
    // The likelihood we'll include an active existing user in the experiment,
    // *after* we filter them through the provided "filters".
    // If this is 100, we will include all users (after filtering) in
    // the experiment.
    percentageOfExistingUsersInExperiment: percentageOfExistingUsersInExperiment,
    // The likelihood we'll include a new user in the experiment,
    // *after* we filter them through the provided "filters".
    // If this is 100, we will include all new users (after filtering)
    // in the experiment.
    percentageOfNewUsersInExperiment: percentageOfNewUsersInExperiment,
    // An array of functions to call to determine whether a user should
    // be excluded from an experiment based on, e.g., the datetime they
    // joined. Each function will receive a user object with keys:
    //  joined {String}: the datetime the user joined
    //  isNewUser {Boolean}: true if this is a brand new user
    // If any filter function returns false, the user will not be
    // included in the experiment.
    filters: filters,
    // The different experiment groups the user could be assigned to.
    groups: Object.assign({}, groups, {
      [noneGroupKey]: NoneExperimentGroup
    }),
    _localStorageKey: `${STORAGE_EXPERIMENT_PREFIX}.${name}`,
    _saveTestGroupToLocalStorage: function (testGroupValue) {
      localStorageMgr.setItem(this._localStorageKey, testGroupValue)
    },
    // This is to track changes/increases in % users included in a test.
    _localStorageKeyForPercentage: `${STORAGE_EXPERIMENT_PREFIX}.${name}.percentageOfUsersLastAssigned`,
    _savePercentageUsersToLocalStorage: function () {
      localStorageMgr.setItem(this._localStorageKeyForPercentage,
        this.percentageOfExistingUsersInExperiment)
    },
    /**
     * Get the value for the percentage of users included in this
     * experiment the last time we assigned this user to a group.
     * @returns {Number} A float equal to a previous value of
     *   `this.percentageOfExistingUsersInExperiment`; or, if we have never
     *   tried to assign this user, return zero.
     */
    _getPercentageUsersFromLocalStorage: function () {
      const percentage = parseFloat(
        localStorageMgr.getItem(this._localStorageKeyForPercentage),
        10)
      return isNaN(percentage) ? 0.0 : percentage
    },
    /**
     * Get whether the user has previously been assigned to a group
     * for this experiment.
     * @returns {Boolean} Whether the user has previously been
     *   assigned to a group for this experiment.
     */
    _hasBeenAssignedToGroup: function () {
      return !isNil(localStorageMgr.getItem(this._localStorageKey))
    },
    /**
     * Save the assigned test group in local storage and to the
     * server.
     * @param {testGroup} The experiment group object.
     * @param {userInfo} The object of user information passed to
     *   assignTestGroup.
     * @returns {undefined}
     */
    _saveTestGroup: function (testGroup, userInfo) {
      this._saveTestGroupToLocalStorage(testGroup.value)

      // Update storage with the current % of users who are included in the
      // experiment.
      this._savePercentageUsersToLocalStorage()

      const experimentName = this.name
      UpdateUserExperimentGroupsMutation(
        environment,
        {
          userId: userInfo.id,
          experimentGroups: {
            [experimentName]: testGroup.schemaValue
          }
        }
      )
    },
    /**
     * Assign the user to an experiment group for this experiment.
     * @param {Object} userInfo
     * @param {String} userInfo.id - The user's ID
     * @param {String} userInfo.joined - The ISO string of when the
     *   user joined.
     * @param {Boolean} userInfo.isNewUser - Whether this user has just
     *   signed up.
     * @returns {undefined}
     */
    assignTestGroup: function (userInfo) {
      if (!this.active) {
        return
      }

      // This is the value of `this.percentageOfExistingUsersInExperiment`
      // the last time we assigned the user to a group in this experiment.
      const previousPercentage = this._getPercentageUsersFromLocalStorage()

      // If the filters exclude this user, don't assign them to an experiment
      // group.
      if (!this.filters.every(function (filterFunc) {
        return filterFunc.call(this, userInfo)
      })) {
        return
      }

      // If the user is already assigned to a group, don't assign a new one,
      // unless: the user is in a "none" group and the percentage of users
      // to assign to this experiment has increased.
      const currentGroup = this.getTestGroup()
      if (
        this._hasBeenAssignedToGroup() &&
        !(
          currentGroup.value === this.groups.NONE.value &&
          this.percentageOfExistingUsersInExperiment > previousPercentage
        )
      ) {
        return
      }

      // Exclude the "none" group.
      const experimentGroups = filter(this.groups, groupObj => groupObj.value !== NoneExperimentGroup.value)

      // If there aren't any test groups, just save the "none" value.
      if (!experimentGroups.length) {
        this._saveTestGroup(this.groups.NONE, userInfo)
        return
      }

      // Calculate the % likelihood of assigning this user to a not-none
      // experiment group. If we previously assigned this user but the
      // % of users we're including in this experiment has increased,
      // we should use the difference in the percentages, which should
      // keep the value of `this.percentageOfExistingUsersInExperiment`
      // as a % of our active user base.
      const likelihoodOfInclusion = (
        userInfo.isNewUser
          ? this.percentageOfNewUsersInExperiment
          : (
            100.0 *
            (this.percentageOfExistingUsersInExperiment - previousPercentage) /
            (100 - previousPercentage)
          )
      )

      // Only assign the experiment to a percentage of random users.
      if ((100 * Math.random()) > likelihoodOfInclusion) {
        this._saveTestGroup(this.groups.NONE, userInfo)
        return
      }

      // There's an equal chance of being assigned to any group,
      // excepting the "none" group.
      const group = experimentGroups[Math.floor(Math.random() * experimentGroups.length)]
      this._saveTestGroup(group, userInfo)
    },
    // Return the the user's assigned experiment group, or the
    // NoneExperimentGroup if the user is not assigned to one.
    getTestGroup: function () {
      // If the test is disabled, return the "none" group value.
      if (this.disabled) {
        return this.groups.NONE
      }

      // Get the user's group from localStorage. If it's not one of
      // the valid group values, return the "none" group value.
      const groupVal = localStorageMgr.getItem(this._localStorageKey)
      const group = find(this.groups, { value: groupVal }) || this.groups.NONE
      return group
    }
  }
}

/**
 * Return an object representing an experiment group the user could be
 * assigned to.
 * @param {Object} experimentGroupConfig
 * @param {string} experimentGroupConfig.value - The string value we'll
 *   use when representing the user's group.
 * @param {string} experimentGroupConfig.schemaValue - The string value
 *   we'll send to the server-side. It should match the GraphQL enum
 *   value of the experiment group.
 *   use when representing the user's group.
 * @return {Object} An object representing the experiment group
 */
export const createExperimentGroup = ({ value, schemaValue }) => {
  if (!(value && schemaValue)) {
    throw new Error('An experiment group must have values for the "value" and "schemaValue" keys.')
  }
  return {
    value,
    schemaValue
  }
}

// The experiment group for when the user is not assigned to any
// experiment group (e.g., not included in the experiment).
const NoneExperimentGroup = createExperimentGroup({
  value: 'none',
  schemaValue: 'NONE'
})

// Add experiment names here as constants.
export const EXPERIMENT_THIRD_AD = 'thirdAd'
export const EXPERIMENT_ONE_AD_FOR_NEW_USERS = 'oneAdForNewUsers'
export const EXPERIMENT_AD_EXPLANATION = 'adExplanation'

// Add ExperimentGroup objects here to enable new experiments.
// The "name" value of the experiment must be the same as the
// field name of the GraphQL ExperimentGroup field name for
// this test.
export const experiments = [
  // @experiment-third-ad
  createExperiment({
    name: EXPERIMENT_THIRD_AD,
    active: true,
    disabled: false,
    percentageOfExistingUsersInExperiment: 0,
    filters: [
      includeIfAnyIsTrue([
        onlyIncludeNewUsers,
        excludeUsersWhoJoinedWithin(30, 'days')
      ])
    ],
    groups: {
      TWO_ADS: createExperimentGroup({
        value: 'twoAds',
        schemaValue: 'TWO_ADS'
      }),
      THREE_ADS: createExperimentGroup({
        value: 'threeAds',
        schemaValue: 'THREE_ADS'
      })
    }
  }),
  // @experiment-one-ad-for-new-users
  createExperiment({
    name: EXPERIMENT_ONE_AD_FOR_NEW_USERS,
    active: true,
    disabled: false,
    filters: [
      onlyIncludeNewUsers
    ],
    groups: {
      DEFAULT: createExperimentGroup({
        value: 'default',
        schemaValue: 'DEFAULT'
      }),
      ONE_AD_AT_FIRST: createExperimentGroup({
        value: 'oneAd',
        schemaValue: 'ONE_AD_AT_FIRST'
      })
    }
  }),
  // @experiment-ad-explanation
  createExperiment({
    name: EXPERIMENT_AD_EXPLANATION,
    active: true,
    disabled: false,
    filters: [
      onlyIncludeNewUsers
    ],
    groups: {
      DEFAULT: createExperimentGroup({
        value: 'default',
        schemaValue: 'DEFAULT'
      }),
      SHOW_EXPLANATION: createExperimentGroup({
        value: 'explanation',
        schemaValue: 'SHOW_EXPLANATION'
      })
    }
  })
]

// We do this to be able to modify the experiments
// variable during testing while keeping it in this file.
// https://github.com/facebook/jest/issues/936#issuecomment-214939935
const getExperiments = () => exports.experiments

/**
 * Get the set of available group values for an experiment.
 * If the experiment does not exist, this returns only the
 * the NoneExperimentGroup.
 * @returns {Object} A map of ExperimentGroup values, keyed
 *   by their name.
 */
export const getExperimentGroups = experimentName => {
  if (!experimentName) {
    throw new Error('Must provide an experiment name.')
  }
  const exp = find(getExperiments(), { name: experimentName })

  // If there's no experiment with this name, return only the
  // NoneExperimentGroup.
  if (!exp) {
    return {
      [noneGroupKey]: NoneExperimentGroup.value
    }
  }

  // Return only the ExperimentGroup string value for ease
  // of comparing user group membership.
  const groups = exp.groups
  const groupNames = Object.keys(groups)
    .reduce((result, key) => {
      result[key] = groups[key].value
      return result
    }, {})
  return groupNames
}

/**
 * Get the user's assigned group value for an experiment.
 * If the experiment does not exist or is disabled, this
 * will return the NoneExperimentGroup value.
 * @returns {String} The value of the user's group for this
 *   experiment.
 */
export const getUserExperimentGroup = experimentName => {
  if (!experimentName) {
    throw new Error('Must provide an experiment name.')
  }
  const exp = find(getExperiments(), { name: experimentName })

  // If there's no experiment with this name, return the
  // NoneExperimentGroup value.
  if (!exp) {
    return NoneExperimentGroup.value
  }
  return exp.getTestGroup().value
}

/**
 * Assigns the user to test groups for all active tests.
 * @param {Object} userInfo
 * @param {String} userInfo.id - The user's ID
 * @param {String} userInfo.joined - The ISO string of when the
 *   user joined.
 * @param {Boolean} userInfo.isNewUser - Whether this user has just
 *   signed up.
 * @returns {undefined}
 */
export const assignUserToTestGroups = userInfo => {
  const exps = getExperiments()
  exps.forEach(experiment => {
    if (experiment.active) {
      experiment.assignTestGroup(userInfo)
    }
  })
}

/**
 * Returns an object with a key for each active experiment. Each
 * key's value is a string representing the test group to which
 * the user is assigned for that test. The object takes the shape
 * of our GraphQL schema's ExperimentGroupsType.
 * @returns {Object} The object of experiment group assignments
 *   for the user, taking the shape of our GraphQL schema's
 *   ExperimentGroupsType.
 */
export const getUserTestGroupsForMutation = () => {
  const exps = getExperiments()
  const userGroupsSchema = exps.reduce((result, experiment) => {
    result[experiment.name] = experiment.getTestGroup().schemaValue
    return result
  }, {})
  return userGroupsSchema
}
<commit_msg>Add note<commit_after>
import {
  filter,
  find
} from 'lodash/collection'
import { isNil } from 'lodash/lang'
import localStorageMgr from 'js/utils/localstorage-mgr'
import {
  STORAGE_EXPERIMENT_PREFIX
} from 'js/constants'
import {
  excludeUsersWhoJoinedWithin,
  includeIfAnyIsTrue,
  onlyIncludeNewUsers
} from 'js/utils/experimentFilters'
import environment from 'js/relay-env'
import UpdateUserExperimentGroupsMutation from 'js/mutations/UpdateUserExperimentGroupsMutation'

const noneGroupKey = 'NONE'

export const createExperiment = ({ name, active = false, disabled = false, groups,
  filters = [], percentageOfExistingUsersInExperiment = 100.0,
  percentageOfNewUsersInExperiment = 100.0 }) => {
  if (!name) {
    throw new Error('An experiment must have a unique "name" value.')
  }
  return {
    // A unique code (without spaces) for this experiment
    name,
    // If true, we will assign users a group in the experiment. If false,
    // we will not assign users to any group.
    active,
    // If true, we will always return the "none" test group when getting
    // the user's experiment group, even if the user is assigned to
    // another group. This should effectively disable the effects of the
    // experiment.
    disabled,
    // The likelihood we'll include an active existing user in the experiment,
    // *after* we filter them through the provided "filters".
    // If this is 100, we will include all users (after filtering) in
    // the experiment.
    // Note: while we technically support increasing the % of active users in
    // an experiment over time, we don't currently log when a user joins an
    // experiment. This means adding new users to an ongoing experiment will
    // likely distort historical analytics. To fix this, we should add a field
    // with the time a user is assigned to each experiment.
    percentageOfExistingUsersInExperiment: percentageOfExistingUsersInExperiment,
    // The likelihood we'll include a new user in the experiment,
    // *after* we filter them through the provided "filters".
    // If this is 100, we will include all new users (after filtering)
    // in the experiment.
    percentageOfNewUsersInExperiment: percentageOfNewUsersInExperiment,
    // An array of functions to call to determine whether a user should
    // be excluded from an experiment based on, e.g., the datetime they
    // joined. Each function will receive a user object with keys:
    //  joined {String}: the datetime the user joined
    //  isNewUser {Boolean}: true if this is a brand new user
    // If any filter function returns false, the user will not be
    // included in the experiment.
    filters: filters,
    // The different experiment groups the user could be assigned to.
    groups: Object.assign({}, groups, {
      [noneGroupKey]: NoneExperimentGroup
    }),
    _localStorageKey: `${STORAGE_EXPERIMENT_PREFIX}.${name}`,
    _saveTestGroupToLocalStorage: function (testGroupValue) {
      localStorageMgr.setItem(this._localStorageKey, testGroupValue)
    },
    // This is to track changes/increases in % users included in a test.
    _localStorageKeyForPercentage: `${STORAGE_EXPERIMENT_PREFIX}.${name}.percentageOfUsersLastAssigned`,
    _savePercentageUsersToLocalStorage: function () {
      localStorageMgr.setItem(this._localStorageKeyForPercentage,
        this.percentageOfExistingUsersInExperiment)
    },
    /**
     * Get the value for the percentage of users included in this
     * experiment the last time we assigned this user to a group.
     * @returns {Number} A float equal to a previous value of
     *   `this.percentageOfExistingUsersInExperiment`; or, if we have never
     *   tried to assign this user, return zero.
     */
    _getPercentageUsersFromLocalStorage: function () {
      const percentage = parseFloat(
        localStorageMgr.getItem(this._localStorageKeyForPercentage),
        10)
      return isNaN(percentage) ? 0.0 : percentage
    },
    /**
     * Get whether the user has previously been assigned to a group
     * for this experiment.
     * @returns {Boolean} Whether the user has previously been
     *   assigned to a group for this experiment.
     */
    _hasBeenAssignedToGroup: function () {
      return !isNil(localStorageMgr.getItem(this._localStorageKey))
    },
    /**
     * Save the assigned test group in local storage and to the
     * server.
     * @param {testGroup} The experiment group object.
     * @param {userInfo} The object of user information passed to
     *   assignTestGroup.
     * @returns {undefined}
     */
    _saveTestGroup: function (testGroup, userInfo) {
      this._saveTestGroupToLocalStorage(testGroup.value)

      // Update storage with the current % of users who are included in the
      // experiment.
      this._savePercentageUsersToLocalStorage()

      const experimentName = this.name
      UpdateUserExperimentGroupsMutation(
        environment,
        {
          userId: userInfo.id,
          experimentGroups: {
            [experimentName]: testGroup.schemaValue
          }
        }
      )
    },
    /**
     * Assign the user to an experiment group for this experiment.
     * @param {Object} userInfo
     * @param {String} userInfo.id - The user's ID
     * @param {String} userInfo.joined - The ISO string of when the
     *   user joined.
     * @param {Boolean} userInfo.isNewUser - Whether this user has just
     *   signed up.
     * @returns {undefined}
     */
    assignTestGroup: function (userInfo) {
      if (!this.active) {
        return
      }

      // This is the value of `this.percentageOfExistingUsersInExperiment`
      // the last time we assigned the user to a group in this experiment.
      const previousPercentage = this._getPercentageUsersFromLocalStorage()

      // If the filters exclude this user, don't assign them to an experiment
      // group.
      if (!this.filters.every(function (filterFunc) {
        return filterFunc.call(this, userInfo)
      })) {
        return
      }

      // If the user is already assigned to a group, don't assign a new one,
      // unless: the user is in a "none" group and the percentage of users
      // to assign to this experiment has increased.
      const currentGroup = this.getTestGroup()
      if (
        this._hasBeenAssignedToGroup() &&
        !(
          currentGroup.value === this.groups.NONE.value &&
          this.percentageOfExistingUsersInExperiment > previousPercentage
        )
      ) {
        return
      }

      // Exclude the "none" group.
      const experimentGroups = filter(this.groups, groupObj => groupObj.value !== NoneExperimentGroup.value)

      // If there aren't any test groups, just save the "none" value.
      if (!experimentGroups.length) {
        this._saveTestGroup(this.groups.NONE, userInfo)
        return
      }

      // Calculate the % likelihood of assigning this user to a not-none
      // experiment group. If we previously assigned this user but the
      // % of users we're including in this experiment has increased,
      // we should use the difference in the percentages, which should
      // keep the value of `this.percentageOfExistingUsersInExperiment`
      // as a % of our active user base.
      const likelihoodOfInclusion = (
        userInfo.isNewUser
          ? this.percentageOfNewUsersInExperiment
          : (
            100.0 *
            (this.percentageOfExistingUsersInExperiment - previousPercentage) /
            (100 - previousPercentage)
          )
      )

      // Only assign the experiment to a percentage of random users.
      if ((100 * Math.random()) > likelihoodOfInclusion) {
        this._saveTestGroup(this.groups.NONE, userInfo)
        return
      }

      // There's an equal chance of being assigned to any group,
      // excepting the "none" group.
      const group = experimentGroups[Math.floor(Math.random() * experimentGroups.length)]
      this._saveTestGroup(group, userInfo)
    },
    // Return the the user's assigned experiment group, or the
    // NoneExperimentGroup if the user is not assigned to one.
    getTestGroup: function () {
      // If the test is disabled, return the "none" group value.
      if (this.disabled) {
        return this.groups.NONE
      }

      // Get the user's group from localStorage. If it's not one of
      // the valid group values, return the "none" group value.
      const groupVal = localStorageMgr.getItem(this._localStorageKey)
      const group = find(this.groups, { value: groupVal }) || this.groups.NONE
      return group
    }
  }
}

/**
 * Return an object representing an experiment group the user could be
 * assigned to.
 * @param {Object} experimentGroupConfig
 * @param {string} experimentGroupConfig.value - The string value we'll
 *   use when representing the user's group.
 * @param {string} experimentGroupConfig.schemaValue - The string value
 *   we'll send to the server-side. It should match the GraphQL enum
 *   value of the experiment group.
 *   use when representing the user's group.
 * @return {Object} An object representing the experiment group
 */
export const createExperimentGroup = ({ value, schemaValue }) => {
  if (!(value && schemaValue)) {
    throw new Error('An experiment group must have values for the "value" and "schemaValue" keys.')
  }
  return {
    value,
    schemaValue
  }
}

// The experiment group for when the user is not assigned to any
// experiment group (e.g., not included in the experiment).
const NoneExperimentGroup = createExperimentGroup({
  value: 'none',
  schemaValue: 'NONE'
})

// Add experiment names here as constants.
export const EXPERIMENT_THIRD_AD = 'thirdAd'
export const EXPERIMENT_ONE_AD_FOR_NEW_USERS = 'oneAdForNewUsers'
export const EXPERIMENT_AD_EXPLANATION = 'adExplanation'

// Add ExperimentGroup objects here to enable new experiments.
// The "name" value of the experiment must be the same as the
// field name of the GraphQL ExperimentGroup field name for
// this test.
export const experiments = [
  // @experiment-third-ad
  createExperiment({
    name: EXPERIMENT_THIRD_AD,
    active: true,
    disabled: false,
    percentageOfExistingUsersInExperiment: 0,
    filters: [
      includeIfAnyIsTrue([
        onlyIncludeNewUsers,
        excludeUsersWhoJoinedWithin(30, 'days')
      ])
    ],
    groups: {
      TWO_ADS: createExperimentGroup({
        value: 'twoAds',
        schemaValue: 'TWO_ADS'
      }),
      THREE_ADS: createExperimentGroup({
        value: 'threeAds',
        schemaValue: 'THREE_ADS'
      })
    }
  }),
  // @experiment-one-ad-for-new-users
  createExperiment({
    name: EXPERIMENT_ONE_AD_FOR_NEW_USERS,
    active: true,
    disabled: false,
    filters: [
      onlyIncludeNewUsers
    ],
    groups: {
      DEFAULT: createExperimentGroup({
        value: 'default',
        schemaValue: 'DEFAULT'
      }),
      ONE_AD_AT_FIRST: createExperimentGroup({
        value: 'oneAd',
        schemaValue: 'ONE_AD_AT_FIRST'
      })
    }
  }),
  // @experiment-ad-explanation
  createExperiment({
    name: EXPERIMENT_AD_EXPLANATION,
    active: true,
    disabled: false,
    filters: [
      onlyIncludeNewUsers
    ],
    groups: {
      DEFAULT: createExperimentGroup({
        value: 'default',
        schemaValue: 'DEFAULT'
      }),
      SHOW_EXPLANATION: createExperimentGroup({
        value: 'explanation',
        schemaValue: 'SHOW_EXPLANATION'
      })
    }
  })
]

// We do this to be able to modify the experiments
// variable during testing while keeping it in this file.
// https://github.com/facebook/jest/issues/936#issuecomment-214939935
const getExperiments = () => exports.experiments

/**
 * Get the set of available group values for an experiment.
 * If the experiment does not exist, this returns only the
 * the NoneExperimentGroup.
 * @returns {Object} A map of ExperimentGroup values, keyed
 *   by their name.
 */
export const getExperimentGroups = experimentName => {
  if (!experimentName) {
    throw new Error('Must provide an experiment name.')
  }
  const exp = find(getExperiments(), { name: experimentName })

  // If there's no experiment with this name, return only the
  // NoneExperimentGroup.
  if (!exp) {
    return {
      [noneGroupKey]: NoneExperimentGroup.value
    }
  }

  // Return only the ExperimentGroup string value for ease
  // of comparing user group membership.
  const groups = exp.groups
  const groupNames = Object.keys(groups)
    .reduce((result, key) => {
      result[key] = groups[key].value
      return result
    }, {})
  return groupNames
}

/**
 * Get the user's assigned group value for an experiment.
 * If the experiment does not exist or is disabled, this
 * will return the NoneExperimentGroup value.
 * @returns {String} The value of the user's group for this
 *   experiment.
 */
export const getUserExperimentGroup = experimentName => {
  if (!experimentName) {
    throw new Error('Must provide an experiment name.')
  }
  const exp = find(getExperiments(), { name: experimentName })

  // If there's no experiment with this name, return the
  // NoneExperimentGroup value.
  if (!exp) {
    return NoneExperimentGroup.value
  }
  return exp.getTestGroup().value
}

/**
 * Assigns the user to test groups for all active tests.
 * @param {Object} userInfo
 * @param {String} userInfo.id - The user's ID
 * @param {String} userInfo.joined - The ISO string of when the
 *   user joined.
 * @param {Boolean} userInfo.isNewUser - Whether this user has just
 *   signed up.
 * @returns {undefined}
 */
export const assignUserToTestGroups = userInfo => {
  const exps = getExperiments()
  exps.forEach(experiment => {
    if (experiment.active) {
      experiment.assignTestGroup(userInfo)
    }
  })
}

/**
 * Returns an object with a key for each active experiment. Each
 * key's value is a string representing the test group to which
 * the user is assigned for that test. The object takes the shape
 * of our GraphQL schema's ExperimentGroupsType.
 * @returns {Object} The object of experiment group assignments
 *   for the user, taking the shape of our GraphQL schema's
 *   ExperimentGroupsType.
 */
export const getUserTestGroupsForMutation = () => {
  const exps = getExperiments()
  const userGroupsSchema = exps.reduce((result, experiment) => {
    result[experiment.name] = experiment.getTestGroup().schemaValue
    return result
  }, {})
  return userGroupsSchema
}
<|endoftext|>


<commit_before>//Create a client instance
var elasticsearch = require('elasticsearch');
var esEndpointIp = process.env.ES_1_PORT_9200_TCP_ADDR;
var esEndpointPort = process.env.ES_1_PORT_9200_TCP_PORT;
var esEndpointLocation = esEndpointIp + ':' + esEndpointPort;

var esClient = new elasticsearch.Client({
    host: esEndpointLocation
    //,log: 'trace'
});

module.exports.search = function (req) {

    var data = [
        {
            "_sourceRecipe1": {
                "recipeYield": "",
                "totalTime": "PT1HPT1H",
                "name": "Homemade Frozen Bean and Veggie BurritosHomemade Frozen Bean and Cheese Burritos",
                "ingredients": ["1 sweet potato", "2 tablespoon olive oil, divided", "1/2 teaspoon cumin", "salt and pepper", "3/4 cup corn", "3/4 cup frozen spinach", "1 can of black beans, rinsed and drained", "1/2 jar of salsa (6 ounce jar)", "4-6 ounces of Pepper Jack cheese", "1 package of whole wheat tortillas (10)", "2 cans refried beans", "1/2 teaspoon cumin (optional)", "salt and pepper", "10 flour tortillas", "1 cup cheese, Cheddar or Pepper Jack", "1/2 jar of salsa (using a 6 ounce jar)"],
                "url": "http://www.thevintagemixer.com/2013/03/homemade-frozen-bean-and-veggie-burritos/",
                "image": "http://d6h7vs5ykbiug.cloudfront.net/wp-content/uploads/2013/03/Frozen-Bean-and-Veggie-Burritos-0.jpg",
                "ts": {
                    "$date": 1365276038444
                },
                "source": "thevintagemixer"
            }
    }, {
            "_sourceRecipe2": {
                "description": "Beautiful buttermilk muffins - berry-streaked with sugar-sparkled tops, big flavor, and buttermilk-tender texture.",
                "prepTime": "PT10M",
                "datePublished": "2013-03-12",
                "recipeYield": "Makes 1 - 1 1/2 dozen, depending on size.",
                "name": "Buttermilk Berry Muffins",
                "ingredients": ["1 1/4 cups / 5 1/2 oz / 160 g whole wheat pastry flour", "2 1/4 cups / 10 1/2 oz / 295 g unbleached all-purpose flour", "1/2 cup / 3 1/2 oz / 100 g firmly packed brown sugar", "3/4 teaspoon fine grain sea salt", "1/2 teaspoon baking soda", "2 teaspoons baking powder", "5 1/2 oz mashed, ripe banana (~2 med.)", "240 ml buttermilk", "3 large eggs", "1 teaspoon vanilla extract", "1/2 cup / 4 oz / 115 g unsalted butter, barely melted", "1 cup / 4.5 oz / 125 g berries, plus more for topping", "rose cinnamon sugar*"],
                "url": "http://www.101cookbooks.com/archives/buttermilk-berry-muffins-recipe.html",
                "image": "http://www.101cookbooks.com/mt-static/images/food/buttermilk_berry_muffin_recipe.jpg",
                "ts": {
                    "$date": 1365276039536
                },
                "cookTime": "PT30M",
                "source": "101cookbooks"
            }
    }, {
            "_sourceRecipe3": {
                "description": "Happy Valentines Day!  I am not a big Valentine's person, i believe we should celebrate love everyday.  However, on Valentine's day i like to give a few cards out, and share a cocktail or two with the loved ones.  White Russian's are some of my favorite dessert drinks!  Have you tried to flavor it with International Delight?  Sooo good!…",
                "prepTime": "PT5M",
                "dateModified": "2013-03-24T04:42:49+00:00",
                "recipeYield": "serves 1",
                "name": "White Russian Cocktail",
                "ingredients": ["2 oz vodka", "1 1/4 oz Kahlua", "1 1/4 oz International Delight French Vanilla Creamer (or you can use milk or half and half)"],
                "url": "http://delishhh.com/2013/02/14/white-russian-cocktail/",
                "image": "http://delishhh.com/wp-content/uploads/2013/02/IMG_5437_small_txt.jpg",
                "ts": {
                    "$date": 1365276040793
                },
                "datePublished": "2013-02-14T21:26:19+00:00",
                "source": "delishhihh"
            }
    }];

    return esClient.search(JSON.parse(req));

}

module.exports.create = function (req) {
    return esClient.create(JSON.parse(req));
}

module.exports.delete = function (req) {
    return esClient.delete(JSON.parse(req));
}

module.exports.ping = function () {
    //Send a HEAD request to /?hello=elasticsearch and allow up to 1 second for it to complete.
    esClient.ping({
        // ping usually has a 3000ms timeout
        requestTimeout: Infinity,

        // undocumented params are appended to the query string
        hello: "elasticsearch!"
    }, function (error) {
        if (error) {
            console.trace('elasticsearch cluster is down!');
        } else {
            return ('All is well');
            console.log('All is well');
        }
    });

}
<commit_msg>log to console : esEndpointLocation<commit_after>//Create a client instance
var elasticsearch = require('elasticsearch');
var esEndpointIp = process.env.ES_1_PORT_9200_TCP_ADDR;
var esEndpointPort = process.env.ES_1_PORT_9200_TCP_PORT;
var esEndpointLocation = esEndpointIp + ':' + esEndpointPort;

if (esEndpointIp == null) {
    console.log('Following docker env variable not available within NodeJS : ES_1_PORT_9200_TCP_ADDR');
    console.log('process.env.ES_1_PORT_9208_TCP_ADDR ', process.env.ES_1_PORT_9200_TCP_ADDR);
    console.log('esEndpointLocation', esEndpointLocation);
} else {
    console.log('esEndpointLocation', esEndpointLocation);
}

var esClient = new elasticsearch.Client({
    host: esEndpointLocation
        //,log: 'trace'
});

module.exports.search = function (req) {

    var data = [
        {
            "_sourceRecipe1": {
                "recipeYield": "",
                "totalTime": "PT1HPT1H",
                "name": "Homemade Frozen Bean and Veggie BurritosHomemade Frozen Bean and Cheese Burritos",
                "ingredients": ["1 sweet potato", "2 tablespoon olive oil, divided", "1/2 teaspoon cumin", "salt and pepper", "3/4 cup corn", "3/4 cup frozen spinach", "1 can of black beans, rinsed and drained", "1/2 jar of salsa (6 ounce jar)", "4-6 ounces of Pepper Jack cheese", "1 package of whole wheat tortillas (10)", "2 cans refried beans", "1/2 teaspoon cumin (optional)", "salt and pepper", "10 flour tortillas", "1 cup cheese, Cheddar or Pepper Jack", "1/2 jar of salsa (using a 6 ounce jar)"],
                "url": "http://www.thevintagemixer.com/2013/03/homemade-frozen-bean-and-veggie-burritos/",
                "image": "http://d6h7vs5ykbiug.cloudfront.net/wp-content/uploads/2013/03/Frozen-Bean-and-Veggie-Burritos-0.jpg",
                "ts": {
                    "$date": 1365276038444
                },
                "source": "thevintagemixer"
            }
    }, {
            "_sourceRecipe2": {
                "description": "Beautiful buttermilk muffins - berry-streaked with sugar-sparkled tops, big flavor, and buttermilk-tender texture.",
                "prepTime": "PT10M",
                "datePublished": "2013-03-12",
                "recipeYield": "Makes 1 - 1 1/2 dozen, depending on size.",
                "name": "Buttermilk Berry Muffins",
                "ingredients": ["1 1/4 cups / 5 1/2 oz / 160 g whole wheat pastry flour", "2 1/4 cups / 10 1/2 oz / 295 g unbleached all-purpose flour", "1/2 cup / 3 1/2 oz / 100 g firmly packed brown sugar", "3/4 teaspoon fine grain sea salt", "1/2 teaspoon baking soda", "2 teaspoons baking powder", "5 1/2 oz mashed, ripe banana (~2 med.)", "240 ml buttermilk", "3 large eggs", "1 teaspoon vanilla extract", "1/2 cup / 4 oz / 115 g unsalted butter, barely melted", "1 cup / 4.5 oz / 125 g berries, plus more for topping", "rose cinnamon sugar*"],
                "url": "http://www.101cookbooks.com/archives/buttermilk-berry-muffins-recipe.html",
                "image": "http://www.101cookbooks.com/mt-static/images/food/buttermilk_berry_muffin_recipe.jpg",
                "ts": {
                    "$date": 1365276039536
                },
                "cookTime": "PT30M",
                "source": "101cookbooks"
            }
    }, {
            "_sourceRecipe3": {
                "description": "Happy Valentines Day!  I am not a big Valentine's person, i believe we should celebrate love everyday.  However, on Valentine's day i like to give a few cards out, and share a cocktail or two with the loved ones.  White Russian's are some of my favorite dessert drinks!  Have you tried to flavor it with International Delight?  Sooo good!…",
                "prepTime": "PT5M",
                "dateModified": "2013-03-24T04:42:49+00:00",
                "recipeYield": "serves 1",
                "name": "White Russian Cocktail",
                "ingredients": ["2 oz vodka", "1 1/4 oz Kahlua", "1 1/4 oz International Delight French Vanilla Creamer (or you can use milk or half and half)"],
                "url": "http://delishhh.com/2013/02/14/white-russian-cocktail/",
                "image": "http://delishhh.com/wp-content/uploads/2013/02/IMG_5437_small_txt.jpg",
                "ts": {
                    "$date": 1365276040793
                },
                "datePublished": "2013-02-14T21:26:19+00:00",
                "source": "delishhihh"
            }
    }];

    return esClient.search(JSON.parse(req));

}

module.exports.create = function (req) {
    return esClient.create(JSON.parse(req));
}

module.exports.delete = function (req) {
    return esClient.delete(JSON.parse(req));
}

module.exports.ping = function () {
    //Send a HEAD request to /?hello=elasticsearch and allow up to 1 second for it to complete.
    esClient.ping({
        // ping usually has a 3000ms timeout
        requestTimeout: Infinity,

        // undocumented params are appended to the query string
        hello: "elasticsearch!"
    }, function (error) {
        if (error) {
            console.trace('elasticsearch cluster is down!');
        } else {
            return ('All is well');
            console.log('All is well');
        }
    });

}<|endoftext|>


<commit_before>Bokeh.$(function() {
  $('.plotdiv').each(function(index, element){
    var modelId = findModelID(element.id);
    var tableEl = $(element).find(".bk-data-table")[0];
    var dataTableView = findViewObject(tableEl, Bokeh.index[modelId]);
    var fields = getFieldNames(dataTableView.model.attributes.columns, $(element).data("sortingFields"));
    var dataSource = dataTableView.mget("source");

    $(element).append("<form class='column-filters' id="+ element.id +"><input type='submit'></form>")

    for (var i = 0; i < fields.length; i++){
      if (fields[i].field != 'name') {
        var optionsString = optionsConstructor(dataSource, fields[i].field)
        $("form.column-filters#" + element.id).append(""+fields[i].title+"<select name=" + fields[i].field + ">"+ optionsString+"</select>")
      };
    }
  })

  $("form.column-filters").submit(function(e){
    e.preventDefault();
    var options = $(e.target).find("select");
    var workingFilters = [];
    for(var i = 0; i < options.length; i++){
      if(options[i].type !== 'submit' && options[i].selectedOptions[0].value !== ""){
        workingFilters.push({name: options[i].name, value: options[i].selectedOptions[0].value})
      }
    }
    var modelId = findModelID(this.id);
    var dataTableView = findViewObject($(document.getElementById(this.id)).find(".bk-data-table")[0], Bokeh.index[modelId])
    var dataSource = dataTableView.mget("source")
    var columns = dataSource.attributes.data
    var rows = []
    var rowsToSelect = applyValueFilter(workingFilters, columns, rows)

    dataTableView.grid.setSelectedRows(rowsToSelect)
    selectionShift(dataTableView)
  })
})

var applyValueFilter = function(workingFilters, columns, rows){
  if(workingFilters.length === 0){
    return rows;
  }
  else{
    var filterToApply = workingFilters.pop()
    var column = columns[filterToApply.name]
    if (rows.length > 0) {
      var newRows = []
      for(var j = 0; j < rows.length; j++){
        if(column[rows[j]] == filterToApply.value){
          newRows.push(rows.slice(j,j+1)[0])
        }
      }
      rows = newRows
    }
    else {
      for(var j = 0; j < column.length; j++){
        if(column[j] == filterToApply.value){
          rows.push(j)
        }
      }
    }
    return applyValueFilter(workingFilters, columns, rows);
  }
}

var rowsIndices = function(array){
  var result = []
  for(var i = 0; i < array.length; i++){
    result.push(i)
  }
  return result
}

var getFieldNames = function(columns, sortingFields){
  var result = []
  for(var j = 0; j < sortingFields.length; j++){
    for(var i = 0; i < columns.length; i++){
      var columnData = Bokeh.Collections("TableColumn").get(columns[i].id)
      if (columnData.attributes.field == sortingFields[j]) {
      result.push({field : columnData.attributes.field, title : columnData.attributes.title})
      };
    }
  }
  return result
}

var optionsConstructor = function(source, fieldName){
  var result = "<option value=''></option>";
  var elements = getUniqueElements(source.attributes.data[fieldName])
  for(var i = 0; i < elements.length; i++){
    result += "<option value='"+ elements[i].toString() +"'>" + elements[i] + "</option>"
  }
  return result
}

var getUniqueElements = function(array){
  var u = {}, a = [];
     for(var i = 0; i < array.length; ++i){
        if(u.hasOwnProperty(array[i])) {
           continue;
        }
        a.push(array[i]);
        u[array[i]] = 1;
     }
     return a;
}<commit_msg>switches class/id order in selector, solves bug<commit_after>Bokeh.$(function() {
  $('.plotdiv').each(function(index, element){
    var modelId = findModelID(element.id);
    var tableEl = $(element).find(".bk-data-table")[0];
    var dataTableView = findViewObject(tableEl, Bokeh.index[modelId]);
    var fields = getFieldNames(dataTableView.model.attributes.columns, $(element).data("sortingFields"));
    var dataSource = dataTableView.mget("source");

    $(element).append("<form class='column-filters' id="+ element.id +"><input type='submit'></form>")

    for (var i = 0; i < fields.length; i++){
      if (fields[i].field != 'name') {
        var optionsString = optionsConstructor(dataSource, fields[i].field)
        $("form#" + element.id + ".column-filters").append(""+fields[i].title+"<select name=" + fields[i].field + ">"+ optionsString+"</select>")
      };
    }
  })

  $("form.column-filters").submit(function(e){
    e.preventDefault();
    var options = $(e.target).find("select");
    var workingFilters = [];
    for(var i = 0; i < options.length; i++){
      if(options[i].type !== 'submit' && options[i].selectedOptions[0].value !== ""){
        workingFilters.push({name: options[i].name, value: options[i].selectedOptions[0].value})
      }
    }
    var modelId = findModelID(this.id);
    var dataTableView = findViewObject($(document.getElementById(this.id)).find(".bk-data-table")[0], Bokeh.index[modelId])
    var dataSource = dataTableView.mget("source")
    var columns = dataSource.attributes.data
    var rows = []
    var rowsToSelect = applyValueFilter(workingFilters, columns, rows)

    dataTableView.grid.setSelectedRows(rowsToSelect)
    selectionShift(dataTableView)
  })
})

var applyValueFilter = function(workingFilters, columns, rows){
  if(workingFilters.length === 0){
    return rows;
  }
  else{
    var filterToApply = workingFilters.pop()
    var column = columns[filterToApply.name]
    if (rows.length > 0) {
      var newRows = []
      for(var j = 0; j < rows.length; j++){
        if(column[rows[j]] == filterToApply.value){
          newRows.push(rows.slice(j,j+1)[0])
        }
      }
      rows = newRows
    }
    else {
      for(var j = 0; j < column.length; j++){
        if(column[j] == filterToApply.value){
          rows.push(j)
        }
      }
    }
    return applyValueFilter(workingFilters, columns, rows);
  }
}

var rowsIndices = function(array){
  var result = []
  for(var i = 0; i < array.length; i++){
    result.push(i)
  }
  return result
}

var getFieldNames = function(columns, sortingFields){
  var result = []
  for(var j = 0; j < sortingFields.length; j++){
    for(var i = 0; i < columns.length; i++){
      var columnData = Bokeh.Collections("TableColumn").get(columns[i].id)
      if (columnData.attributes.field == sortingFields[j]) {
      result.push({field : columnData.attributes.field, title : columnData.attributes.title})
      };
    }
  }
  return result
}

var optionsConstructor = function(source, fieldName){
  var result = "<option value=''></option>";
  var elements = getUniqueElements(source.attributes.data[fieldName])
  for(var i = 0; i < elements.length; i++){
    result += "<option value='"+ elements[i].toString() +"'>" + elements[i] + "</option>"
  }
  return result
}

var getUniqueElements = function(array){
  var u = {}, a = [];
     for(var i = 0; i < array.length; ++i){
        if(u.hasOwnProperty(array[i])) {
           continue;
        }
        a.push(array[i]);
        u[array[i]] = 1;
     }
     return a;
}<|endoftext|>