Example #1
0
def get_concat_js(valid_experiments):
    '''get_concat_js
    Return javascript concat section for valid experiments, based on psiturk.json
    :param valid_experiments: full paths to valid experiments to include

    ..note::

			case "simple-rt":
				experiments = experiments.concat(simple-rt_experiment)
				break;
			case "choice-rt":
				experiments = experiments.concat(choice-rt_experiment)
				break;

               Format for experiment variables is [exp_id]_experiment

    '''
    concatjs = "\n"
    for valid_experiment in valid_experiments:
        experiment = load_experiment(valid_experiment)[0]
        tag = str(experiment["exp_id"])
        concatjs = '%scase "%s":\n' %(concatjs,tag)
        concatjs = '%s      experiments = experiments.concat(%s_experiment)\n' %(concatjs,tag)
        concatjs = '%s      break;\n' %(concatjs)
    return concatjs
Example #2
0
def get_concat_js(valid_experiments):
    '''get_concat_js
    Return javascript concat section for valid experiments, based on psiturk.json
    :param valid_experiments: full paths to valid experiments to include

    ..note::

			case "simple-rt":
				experiments = experiments.concat(simple-rt_experiment)
				break;
			case "choice-rt":
				experiments = experiments.concat(choice-rt_experiment)
				break;

               Format for experiment variables is [exp_id]_experiment

    '''
    concatjs = "\n"
    for valid_experiment in valid_experiments:
        experiment = load_experiment(valid_experiment)[0]
        tag = str(experiment["exp_id"])
        concatjs = '%scase "%s":\n' % (concatjs, tag)
        concatjs = '%s      experiments = experiments.concat(%s_experiment)\n' % (
            concatjs, tag)
        concatjs = '%s      break;\n' % (concatjs)
    return concatjs
Example #3
0
def validate_surveys(survey_tags,
                     survey_repo,
                     survey_file="survey.tsv",
                     delim="\t"):
    '''validate_surveys validates an experiment factory survey folder
    :param survey_tags: a list of surveys to validate
    :param survey_repo: the survey repo with the survey folders to validate
    :param survey_file: the default file to validate
    '''
    if isinstance(survey_tags, str):
        survey_tags = [survey_tags]

    for survey_tag in survey_tags:
        survey_folder = "%s/%s" % (survey_repo, survey_tag)
        print "Testing load of config.json for %s" % (survey_folder)
        survey = load_experiment("%s" % survey_folder)
        survey_questions = "%s/%s" % (survey_folder, survey_file)
        print "Testing valid columns in %s" % (survey[0]["exp_id"])
        df = read_survey_file(survey_questions, delim=delim)
        assert_equal(isinstance(df, pandas.DataFrame), True)
        print "Testing survey generation of %s" % (survey[0]["exp_id"])
        questions, required_count = parse_questions(survey_questions,
                                                    exp_id=survey[0]["exp_id"],
                                                    validate=True)
        print "Testing validation generation of %s" % (survey[0]["exp_id"])
        validation = parse_validation(required_count)
Example #4
0
def validate_experiment_tag(experiment_folder):
    '''validate_experiment_tag looks for definition of exp_id as the tag somewhere in experiment.js. We are only requiring one definition for now (a more lax approach), but this standard might be changed.
    '''
    experiments = find_directories(experiment_folder)
    print "Testing %s experiment for definition of exp_id in experiment.js..."
    for contender in experiments:
        if validate(contender,warning=False) == True:
            experiment = load_experiment(contender)
            tag = experiment[0]["exp_id"]

            # Experiment MUST contain experiment.js to run main experiment
            print "TESTING %s for exp_id in experiment.js..." %tag
            assert_equal("experiment.js" in experiment[0]["run"],True)

            if "experiment.js" in experiment[0]["run"]:
                experiment_js_file = open("%s/%s/experiment.js" %(experiment_folder,tag),"r") 
                experiment_js_list = [x.strip("\n").replace("'","").replace('"',"").replace(" ","") for x in experiment_js_file.readlines()]
                experiment_js_file.close()
                experiment_js = "".join(experiment_js_list)
                [x]
                has_exp_id = re.search("exp_id:%s" %tag,experiment_js) != None or re.search("exp_id=%s" %tag,experiment_js) != None 
                assert_equal(has_exp_id,True)
                # Ensure all are formatted correctly
                exp_id_instances = [re.findall("exp_id[=|:].+",x) for x in experiment_js_list if len(re.findall("exp_id[=|:].+,",x)) != 0]
                line_numbers = [x+1 for x in range(len(experiment_js_list)) if len(re.findall("exp_id[=|:].+,",experiment_js_list[x])) != 0]
                for e in range(len(exp_id_instances)):
                    exp_id_instance = exp_id_instances[e]
                    line_number = line_numbers[e]
                    print "Checking %s on line %s..." %(exp_id_instance[0],line_number)
                    assert_equal(re.search(tag,exp_id_instance[0])!=None,True) 
Example #5
0
def validate_surveys(survey_tags,survey_repo,survey_file="survey.tsv",delim="\t",raise_error=True):
    '''validate_surveys validates an experiment factory survey folder
    :param survey_tags: a list of surveys to validate
    :param survey_repo: the survey repo with the survey folders to validate
    :param survey_file: the default file to validate
    :param raise_error: throw the error (meaning don't return True or False instead)
    '''
    if isinstance(survey_tags,str):
        survey_tags = [survey_tags]

    for survey_tag in survey_tags:
        try:
            survey_folder = "%s/%s" %(survey_repo,survey_tag)
            print "Testing load of config.json for %s" %(survey_folder)
            survey = load_experiment("%s" %survey_folder)
            survey_questions = "%s/%s" %(survey_folder,survey_file)       
            print "Testing valid columns in %s" %(survey[0]["exp_id"])
            df = read_survey_file(survey_questions,delim=delim)
            assert_equal(isinstance(df,pandas.DataFrame),True)
            print "Testing survey generation of %s" %(survey[0]["exp_id"])
            questions,required_count = parse_questions(survey_questions,
                                                       exp_id=survey[0]["exp_id"],
                                                       validate=True)
            print "Testing validation generation of %s" %(survey[0]["exp_id"])
            validation = parse_validation(required_count)
        except:
            if raise_error == False:
                return False
            raise
    return True
Example #6
0
def validate_surveys(survey_tags,
                     survey_repo,
                     survey_file="survey.tsv",
                     delim="\t",
                     raise_error=True):
    '''validate_surveys validates an experiment factory survey folder
    :param survey_tags: a list of surveys to validate
    :param survey_repo: the survey repo with the survey folders to validate
    :param survey_file: the default file to validate
    :param raise_error: throw the error (meaning don't return True or False instead)
    '''
    if isinstance(survey_tags, str):
        survey_tags = [survey_tags]

    for survey_tag in survey_tags:
        try:
            survey_folder = "%s/%s" % (survey_repo, survey_tag)
            print("Testing load of config.json for %s" % (survey_folder))
            survey = load_experiment("%s" % survey_folder)
            survey_questions = "%s/%s" % (survey_folder, survey_file)
            print("Testing valid columns in %s" % (survey[0]["exp_id"]))
            df = read_survey_file(survey_questions, delim=delim)
            assert (isinstance(df, pandas.DataFrame))
            print("Testing survey generation of %s" % (survey[0]["exp_id"]))
            questions, required_count = parse_questions(
                survey_questions, exp_id=survey[0]["exp_id"], validate=True)
            print("Testing validation generation of %s" %
                  (survey[0]["exp_id"]))
            validation = parse_validation(required_count)
        except:
            if raise_error == False:
                return False
            raise
    return True
Example #7
0
def validate_experiment_tag(experiment_folder):
    '''validate_experiment_tag looks for definition of exp_id as the tag somewhere in experiment.js. We are only requiring one definition for now (a more lax approach), but this standard might be changed.
    '''
    experiments = find_directories(experiment_folder)
    print "Testing %s experiment for definition of exp_id in experiment.js..."
    for contender in experiments:
        if validate(contender,warning=False) == True:
            experiment = load_experiment(contender)
            tag = experiment[0]["exp_id"]

            # Experiment MUST contain experiment.js to run main experiment
            print "TESTING %s for exp_id in experiment.js..." %tag
            assert_equal("experiment.js" in experiment[0]["run"],True)

            if "experiment.js" in experiment[0]["run"]:
                experiment_js_file = open("%s/%s/experiment.js" %(experiment_folder,tag),"r") 
                experiment_js_list = [x.strip("\n").replace("'","").replace('"',"").replace(" ","") for x in experiment_js_file.readlines()]
                experiment_js_file.close()
                experiment_js = "".join(experiment_js_list)
                [x]
                has_exp_id = re.search("exp_id:%s" %tag,experiment_js) != None or re.search("exp_id=%s" %tag,experiment_js) != None 
                assert_equal(has_exp_id,True)
                # Ensure all are formatted correctly
                exp_id_instances = [re.findall("exp_id[=|:].+",x) for x in experiment_js_list if len(re.findall("exp_id[=|:].+,",x)) != 0]
                line_numbers = [x+1 for x in range(len(experiment_js_list)) if len(re.findall("exp_id[=|:].+,",experiment_js_list[x])) != 0]
                for e in range(len(exp_id_instances)):
                    exp_id_instance = exp_id_instances[e]
                    line_number = line_numbers[e]
                    print "Checking %s on line %s..." %(exp_id_instance[0],line_number)
                    assert_equal(re.search(tag,exp_id_instance[0])!=None,True) 
Example #8
0
def embed_experiment(folder,url_prefix=""):
    '''embed_experiment
    return an html snippet for embedding into an application. This assumes the same directory structure, that all jspsych files can be found in static/js/jspych, and experiments under static/experiments/[folder]
    :param folder: full path to experiment folder with config.json
    '''
    folder = os.path.abspath(folder)
    experiment = load_experiment("%s" %folder)
    return get_experiment_html(experiment,folder,url_prefix=url_prefix)
Example #9
0
def install_experiment_template(experiment, battery, router_url, to_dir):
    '''install_experiment_template generates a survey or jspsych experiment template,
    and is intended to be called from install_experiment_static, which takes care of other
    dependencies like creating experiment folder, etc. The expfactory repos have validation and
    testing before experiments are allowed to be submit, so we don't do extra checks.
    :param experiment: the expdj.apps.experiments.models.Experiment
    :param battery: the expdj.apps.experiments.models.Battery
    :param router_url: the experiment router URL, to direct to on form submit
    :param to_dir: the directory to install to, typically in a battery folder
    '''
    # If the experiment template is special (survey, experiment, game), we need to render the files
    context = dict()
    if experiment.template == "jspsych":
        template = get_template("experiments/serve_battery.html")
        config = load_experiment(to_dir)[0]
        runcode = get_jspsych_init(config)

    if experiment.template == "survey":
        template = get_template("surveys/serve_battery.html")
        config = load_experiment(to_dir)
        runcode, validation = generate_survey(config,
                                              to_dir,
                                              form_action=router_url,
                                              csrf_token=False)
        context["validation"] = validation

    # static path should be replaced with web path
    url_path = "%s/" % (to_dir.replace(MEDIA_ROOT, MEDIA_URL[:-1]))

    # prepare static files paths
    css, js = prepare_header_scripts(config, url_prefix=url_path)

    # Update the context dictionary, render the template
    context["run"] = runcode
    context["css"] = css
    context["js"] = js
    context["form_submit"] = router_url
    context = Context(context)
    # All templates will have the index written
    return template.render(context)
    def test_find_experiments(self):

        # Test loading experiment folder path
        experiments = get_experiments(self.battery_folder)
        assert_equal(len(experiments), 1)
        self.assertTrue(isinstance(experiments[0], str))

        # Test loading experiment dictionary
        config = get_experiments(self.battery_folder, load=True)
        self.assertTrue(isinstance(config[0][0], dict))

        loaded_experiment = load_experiment(self.experiment)
        self.assertTrue(isinstance(loaded_experiment[0], dict))
    def test_find_experiments(self):

        # Test loading experiment folder path
        experiments = get_experiments(self.battery_folder)
        assert_equal(len(experiments),1)
        self.assertTrue(isinstance(experiments[0],str))

        # Test loading experiment dictionary
        config = get_experiments(self.battery_folder,load=True)
        self.assertTrue(isinstance(config[0][0],dict))

        loaded_experiment = load_experiment(self.experiment)  
        self.assertTrue(isinstance(loaded_experiment[0],dict))
Example #12
0
def test_experiment(folder=None,
                    battery_folder=None,
                    port=None,
                    pause_time=2000):
    '''test_experiment
    test an experiment locally with the --test tag and the expfactory robot
    :param folder: full path to experiment folder to preview. If none specified, PWD is used
    :param battery_folder: full path to battery folder to use as a template. If none specified, the expfactory-battery repo will be used.
    :param port: the port number, default will be randomly generated between 8000 and 9999
    '''
    if folder == None:
        folder = os.path.abspath(os.getcwd())

    # Deploy experiment with battery to temporary directory
    tmpdir = tmp_experiment(folder, battery_folder)
    experiment = load_experiment("%s" % folder)

    try:
        if port == None:
            port = choice(range(8000, 9999), 1)[0]
            Handler = ExpfactoryServer
            httpd = SocketServer.TCPServer(("", port), Handler)
            server = Thread(target=httpd.serve_forever)
            server.setDaemon(True)
            server.start()

        # Set up a web browser
        browser = get_browser()
        browser.implicitly_wait(3)  # if error, will wait 3 seconds and retry
        browser.set_page_load_timeout(10)

        print "STARTING TEST OF EXPERIMENT %s" % (experiment[0]["exp_id"])
        get_page(browser, "http://localhost:%s" % (port))

        sleep(3)

        count = 0
        wait_time = 1000
        while True:
            print "Testing block %s of %s" % (count, experiment[0]["exp_id"])
            wait_time, finished = test_block(browser, experiment, pause_time,
                                             wait_time)
            if finished == True:
                break
            count += 1
        print "FINISHING TEST OF EXPERIMENT %s" % (experiment[0]["exp_id"])

    except:
        print "Stopping web server..."
        httpd.server_close()
        shutil.rmtree(tmpdir)
Example #13
0
    def _validate_folder(self, folder=None):
        ''' validate folder takes a cloned github repo, ensures
            the existence of the config.json, and validates it.
        '''
        from expfactory.experiment import load_experiment

        if folder is None:
            folder = os.path.abspath(os.getcwd())

        config = load_experiment(folder, return_path=True)

        if not config:
            return notvalid("%s is not an experiment." % (folder))

        return self._validate_config(folder)
Example #14
0
    def _validate_folder(self, folder=None):
        ''' validate folder takes a cloned github repo, ensures
            the existence of the config.json, and validates it.
        '''
        from expfactory.experiment import load_experiment

        if folder is None:
            folder=os.path.abspath(os.getcwd())

        config = load_experiment(folder, return_path=True)

        if not config:
            return notvalid("%s is not an experiment." %(folder))

        return self._validate_config(folder)
Example #15
0
def get_experiment_run(valid_experiments,deployment="local"):
    '''get_experiment_run
    returns a dictionary of experiment run code (right now just jspsych init objects)
    :param valid_experiments: full path to valid experiments folders, OR a loaded config.json (dict)
    '''
    runs = dict()
    for valid_experiment in valid_experiments:
        if not isinstance(valid_experiment,dict):
            experiment = load_experiment(valid_experiment)[0]
        else:
            experiment = valid_experiment
        tag = str(experiment["exp_id"])
        if experiment["template"] == "jspsych":
            runcode = get_jspsych_init(experiment,deployment=deployment)
        runs[tag] = runcode
    return runs
Example #16
0
def get_experiment_run(valid_experiments, deployment="local"):
    '''get_experiment_run
    returns a dictionary of experiment run code (right now just jspsych init objects)
    :param valid_experiments: full path to valid experiments folders, OR a loaded config.json (dict)
    '''
    runs = dict()
    for valid_experiment in valid_experiments:
        if not isinstance(valid_experiment, dict):
            experiment = load_experiment(valid_experiment)[0]
        else:
            experiment = valid_experiment
        tag = str(experiment["exp_id"])
        if experiment["template"] == "jspsych":
            runcode = get_jspsych_init(experiment, deployment=deployment)
        runs[tag] = runcode
    return runs
Example #17
0
def test_experiment(folder=None,battery_folder=None,port=None,pause_time=2000):
    '''test_experiment
    test an experiment locally with the --test tag and the expfactory robot
    :param folder: full path to experiment folder to preview. If none specified, PWD is used
    :param battery_folder: full path to battery folder to use as a template. If none specified, the expfactory-battery repo will be used.
    :param port: the port number, default will be randomly generated between 8000 and 9999
    '''
    if folder==None:
        folder=os.path.abspath(os.getcwd())

    # Deploy experiment with battery to temporary directory
    tmpdir = tmp_experiment(folder,battery_folder)
    experiment = load_experiment("%s" %folder)

    try:
        if port == None:
            port = choice(range(8000,9999),1)[0]
            Handler = ExpfactoryServer
            httpd = SocketServer.TCPServer(("", port), Handler)
            server = Thread(target=httpd.serve_forever)
            server.setDaemon(True)
            server.start()

        # Set up a web browser
        browser = get_browser()
        browser.implicitly_wait(3) # if error, will wait 3 seconds and retry
        browser.set_page_load_timeout(10)
 
        print "STARTING TEST OF EXPERIMENT %s" %(experiment[0]["exp_id"])
        get_page(browser,"http://localhost:%s" %(port))
        
        sleep(3)

        count=0
        wait_time=1000
        while True:
            print "Testing block %s of %s" %(count,experiment[0]["exp_id"])
            wait_time,finished = test_block(browser,experiment,pause_time,wait_time)
            if finished == True:
                break
            count+=1
        print "FINISHING TEST OF EXPERIMENT %s" %(experiment[0]["exp_id"])

    except:
        print "Stopping web server..."
        httpd.server_close()
        shutil.rmtree(tmpdir)
Example #18
0
def get_load_static(valid_experiments,url_prefix="",unique=True):
    '''get_load_static
    return the scripts and styles as <link> and <script> to embed in a page directly
    :param unique: return only unique scripts [default=True]
    '''
    loadstring = ""
    for valid_experiment in valid_experiments:
        experiment = load_experiment(valid_experiment)
        css,js = get_stylejs(experiment,url_prefix=url_prefix)
        loadstring = "%s%s%s" %(loadstring,js,css)        
    if unique == True:
        scripts = loadstring.split("\n")
        scripts_index = numpy.unique(scripts,return_index=True)[1]
        # This ensures that scripts are loaded in same order as specified in config.json
        unique_scripts = [scripts[idx] for idx in sorted(scripts_index)]
        loadstring = "\n".join(unique_scripts)
    return loadstring
Example #19
0
def get_load_static(valid_experiments, url_prefix="", unique=True):
    '''get_load_static
    return the scripts and styles as <link> and <script> to embed in a page directly
    :param unique: return only unique scripts [default=True]
    '''
    loadstring = ""
    for valid_experiment in valid_experiments:
        experiment = load_experiment(valid_experiment)
        css, js = get_stylejs(experiment, url_prefix=url_prefix)
        loadstring = "%s%s%s" % (loadstring, js, css)
    if unique == True:
        scripts = loadstring.split("\n")
        scripts_index = list(np.unique(scripts, return_index=True))[1]
        # This ensures that scripts are loaded in same order as specified in config.json
        unique_scripts = [scripts[idx] for idx in sorted(scripts_index)]
        loadstring = "\n".join(unique_scripts)
    return loadstring
Example #20
0
def get_timing_js(valid_experiments):
    '''get_timing_js
    Produce string (json / dictionary) of experiment timings
    :param valid_experiments: a list of full paths to valid experiments to include

    ..note::

         Produces the following format for each experiment

         {name:"simple_rt", time: 3.5}, {name:"choice_rt", time: 4}, ...
    
    
    '''
    timingjs = []
    for valid_experiment in valid_experiments:
        experiment = load_experiment(valid_experiment)[0]
        timingjs.append({"name":str(experiment["exp_id"]),"time":experiment["time"]})
    return timingjs
Example #21
0
def main():

    parser = get_parser()

    try:
        args = parser.parse_args()
    except:
        sys.exit(0)

    folder = args.folder
    if folder is None:
        folder = os.getcwd()
    folder = os.path.abspath(folder)

    survey = "%s/survey.tsv" % folder
    if not os.path.exists(survey):
        bot.error("Cannot find %s, required to generate survey." % survey)
        sys.exit(1)

    config = load_experiment(folder)
    html, validation = generate_survey(config=config,
                                       survey_file=survey,
                                       form_action=args.action)

    output = args.output
    if output is None:
        output = folder

    output_index = "%s/index.html" % folder

    if os.path.exists(output_index) and args.force is False:
        bot.error("%s already exists, use --force to overwrite." %
                  output_index)
        sys.exit(1)

    bot.info("Writing output files to %s" % output_index)

    template = get_template('survey/index.html')
    template = sub_template(template, "{{html}}", html)
    template = sub_template(template, "{{exp_id}}", config['exp_id'])
    template = sub_template(template, "{{validation}}", validation)

    write_file(output_index, template)
Example #22
0
def tmp_experiment(folder=None, battery_folder=None):
    '''generate temporary directory with experiment
    :param folder: full path to experiment folder to preview (experiment, survey, or game). If none specified, PWD is used
    :param battery_folder: full path to battery folder to use as a template. If none specified, the expfactory-battery repo will be used.
    '''
    if folder == None:
        folder = os.path.abspath(os.getcwd())

    if battery_folder == None:
        tmpdir = custom_battery_download(repos=["battery"])
    # If user has supplied a local battery folder, copy to tempdir
    else:
        tmpdir = tempfile.mkdtemp()
        copy_directory(battery_folder, "%s/battery" % tmpdir)

    experiment = load_experiment("%s" % folder)
    tag = experiment[0]["exp_id"]

    # Determine experiment template
    experiment_type = "experiments"
    if experiment[0]["template"] == "survey":
        experiment_type = "surveys"
    elif experiment[0]["template"] == "phaser":
        experiment_type = "games"

    # We will copy the entire experiment into the battery folder
    battery_folder = "%s/battery" % (tmpdir)
    experiment_folder = "%s/static/%s/%s" % (battery_folder, experiment_type,
                                             tag)
    if os.path.exists(experiment_folder):
        shutil.rmtree(experiment_folder)
    copy_directory(folder, experiment_folder)
    index_file = "%s/index.html" % (battery_folder)

    # Generate code for js and css
    exp_template = get_experiment_html(experiment, experiment_folder)
    filey = open(index_file, "w")
    filey.writelines(exp_template)
    filey.close()
    os.chdir(battery_folder)
    return tmpdir
Example #23
0
def get_timing_js(valid_experiments):
    '''get_timing_js
    Produce string (json / dictionary) of experiment timings
    :param valid_experiments: a list of full paths to valid experiments to include

    ..note::

         Produces the following format for each experiment

         {name:"simple_rt", time: 3.5}, {name:"choice_rt", time: 4}, ...
    
    
    '''
    timingjs = []
    for valid_experiment in valid_experiments:
        experiment = load_experiment(valid_experiment)[0]
        timingjs.append({
            "name": str(experiment["exp_id"]),
            "time": experiment["time"]
        })
    return timingjs
Example #24
0
def tmp_experiment(folder=None,battery_folder=None):
    '''generate temporary directory with experiment
    :param folder: full path to experiment folder to preview (experiment, survey, or game). If none specified, PWD is used
    :param battery_folder: full path to battery folder to use as a template. If none specified, the expfactory-battery repo will be used.
    '''
    if folder==None:
        folder=os.path.abspath(os.getcwd())

    if battery_folder == None:
        tmpdir = custom_battery_download(repos=["battery"])
    # If user has supplied a local battery folder, copy to tempdir
    else:
        tmpdir = tempfile.mkdtemp()
        copy_directory(battery_folder,"%s/battery" %tmpdir)
        
    experiment = load_experiment("%s" %folder)
    tag = experiment[0]["exp_id"]

    # Determine experiment template
    experiment_type = "experiments"
    if experiment[0]["template"] == "survey":
        experiment_type = "surveys"
    elif experiment[0]["template"] == "phaser":
        experiment_type = "games"

    # We will copy the entire experiment into the battery folder
    battery_folder = "%s/battery" %(tmpdir)
    experiment_folder = "%s/static/%s/%s" %(battery_folder,experiment_type,tag)
    if os.path.exists(experiment_folder):
        shutil.rmtree(experiment_folder)
    copy_directory(folder,experiment_folder)
    index_file = "%s/index.html" %(battery_folder)
        
    # Generate code for js and css
    exp_template = get_experiment_html(experiment,experiment_folder)
    filey = open(index_file,"w")
    filey.writelines(exp_template)
    filey.close()
    os.chdir(battery_folder)
    return tmpdir
Example #25
0
def get_load_js(valid_experiments, url_prefix=""):
    '''get_load_js
    Return javascript to load list of valid experiments, based on psiturk.json
    :param valid_experiments: a list of full paths to valid experiments to include

    ..note::
        Format is:
         {
		case "simple_rt":
			loadjscssfile("static/css/experiments/simple_rt.css","css")
			loadjscssfile("static/js/experiments/simple_rt.js","js")
			break;
		case "choice_rt":
			loadjscssfile("static/css/experiments/choice_rt.css","css")
			loadjscssfile("static/js/experiments/choice_rt.js","js")
			break;

          ...
          }

    '''
    loadstring = "\n"
    for valid_experiment in valid_experiments:
        experiment = load_experiment(valid_experiment)[0]
        tag = str(experiment["exp_id"])
        loadstring = '%scase "%s":\n' % (loadstring, tag)
        for script in experiment["run"]:
            fname, ext = os.path.splitext(script)
            ext = ext.replace(".", "").lower()
            # If the file is included in the experiment
            if len(script.split("/")) == 1:
                loadstring = '%s         loadjscssfile("%sstatic/experiments/%s/%s","%s")\n' % (
                    loadstring, url_prefix, tag, script, ext)
            else:
                loadstring = '%s         loadjscssfile("%s%s","%s")\n' % (
                    loadstring, url_prefix, script, ext)
        loadstring = "%s         break;\n" % (loadstring)
    return loadstring
Example #26
0
def get_load_js(valid_experiments,url_prefix=""):
    '''get_load_js
    Return javascript to load list of valid experiments, based on psiturk.json
    :param valid_experiments: a list of full paths to valid experiments to include

    ..note::
        Format is:
         {
		case "simple_rt":
			loadjscssfile("static/css/experiments/simple_rt.css","css")
			loadjscssfile("static/js/experiments/simple_rt.js","js")
			break;
		case "choice_rt":
			loadjscssfile("static/css/experiments/choice_rt.css","css")
			loadjscssfile("static/js/experiments/choice_rt.js","js")
			break;

          ...
          }

    '''
    loadstring = "\n"
    for valid_experiment in valid_experiments:
        experiment = load_experiment(valid_experiment)[0]
        tag = str(experiment["exp_id"])
        loadstring = '%scase "%s":\n' %(loadstring,tag)
        for script in experiment["run"]:
            fname,ext = os.path.splitext(script)
            ext = ext.replace(".","").lower()
            # If the file is included in the experiment
            if len(script.split("/")) == 1:                
                loadstring = '%s         loadjscssfile("%sstatic/experiments/%s/%s","%s")\n' %(loadstring,url_prefix,tag,script,ext)
            else:
                loadstring = '%s         loadjscssfile("%s%s","%s")\n' %(loadstring,url_prefix,script,ext)
        loadstring = "%s         break;\n" %(loadstring)
    return loadstring
Example #27
0
def main(args, parser, subparser):

    # List of experiments is required
    template = get_template('build/docker/Dockerfile.template')

    # For now, only one database provided
    database = args.database
    studyid = args.studyid
    experiments = args.experiments

    template = sub_template(template, "{{studyid}}", studyid)
    template = sub_template(template, "{{database}}", database)

    library = get_library(key='name')

    apps = "\n"

    # Add local experiments to library, first preference
    local_installs = 0
    for experiment in experiments:
        if os.path.exists(experiment):

            bot.info('local experiment %s found, validating...' % experiment)

            # Is the experiment valid?
            cli = ExperimentValidator()
            valid = cli.validate(experiment, cleanup=False)

            if valid is True:
                local_installs += 1
                config = load_experiment(experiment)
                exp_id = config['exp_id']

                # If we aren't building in the experiment directory, we need to copy there
                output_dir = "%s/%s" % (os.path.abspath(
                    os.path.dirname(args.output)), exp_id)
                experiment_dir = os.path.abspath(experiment)
                if output_dir != experiment_dir:
                    copy_directory(experiment_dir, output_dir)

                config['local'] = os.path.abspath(experiment)
                library[exp_id] = config

    # Warn the user that local installs are not reproducible (from recipe)
    if local_installs > 0:
        bot.warning(
            "%s local installs detected: build is not reproducible without experiment folders"
            % local_installs)

    # Build Image with Experiments
    for experiment in experiments:
        exp_id = os.path.basename(experiment)
        if exp_id in library:
            config = library[exp_id]

            app = "LABEL EXPERIMENT_%s /scif/apps/%s\n" % (exp_id, exp_id)

            # Here add custom build routine, should be list of lines
            if "install" in config:
                commands = "\n".join(
                    ["RUN %s " % s for x in config['install']]).strip('\n')
                app = "%s%s\n" % (app, commands)

            # The final installation step, either from Github (url) or local folder
            if "local" in config:
                app = "%sADD %s /scif/apps/%s\n" % (app, exp_id, exp_id)
                app = "%sWORKDIR /scif/apps\nRUN expfactory install %s\n" % (
                    app, exp_id)
            else:
                app = "%sWORKDIR /scif/apps\nRUN expfactory install %s\n" % (
                    app, config['github'])
            apps = "%s%s\n" % (apps, app)

        else:
            bot.warning('%s not in library, check spelling & punctuation.' %
                        exp_id)

    if apps == "\n":
        bot.error('No valid experiments found, cancelling recipe build.')
        sys.exit(1)

    template = sub_template(template, "{{experiments}}", apps)
    outfile = write_file(args.output, template)
    bot.log("Recipe written to %s" % outfile)
Example #28
0
 def test_getstylejs(self):
     experiment = load_experiment(self.experiment)
     stylejs = get_stylejs(experiment)
     self.assertTrue(len(stylejs)==2)
     self.assertTrue(re.search("style.css",stylejs[0])!=None)
     self.assertTrue(re.search("experiment.js",stylejs[1])!=None)
Example #29
0
def main():
    parser = argparse.ArgumentParser(
    description="generate experiments and infrastructure to serve them.")
    parser.add_argument("--folder", dest='folder', help="full path to single experiment folder (for single experiment run with --run) or folder with many experiments (for battery run with --run)", type=str, default=None)
    parser.add_argument("--subid", dest='subid', help="subject id to embed in experiments data in the case of a battery run with --run", type=str, default=None)
    parser.add_argument("--experiments", dest='experiments', help="comma separated list of experiments for a local battery", type=str, default=None)
    parser.add_argument("--port", dest='port', help="port to preview experiment", type=int, default=None)
    parser.add_argument("--battery", dest='battery_folder', help="full path to local battery folder to use as template", type=str, default=None)
    parser.add_argument("--time", dest='time', help="maximum number of minutes for battery to endure, to select experiments", type=int, default=99999)
    parser.add_argument('--preview', help="preview an experiment locally (development function)", dest='preview', default=False, action='store_true')
    parser.add_argument('--run', help="run a single experiment/survey or battery locally", dest='run', default=False, action='store_true')
    parser.add_argument("--survey", dest='survey', help="survey to run for a local assessment", type=str, default=None)
    parser.add_argument("--game", dest='game', help="game to run for a local assessment", type=str, default=None)
    parser.add_argument('--validate', dest='validate', help="validate an experiment folder", default=False, action='store_true')
    parser.add_argument('--psiturk', dest='psiturk', help="to be used with the --generate command, to generate a psiturk battery instead of local folder deployment", default=False, action='store_true')
    parser.add_argument('--generate', dest='generate', help="generate (and don't run) a battery with --experiments to a --folder", default=False, action='store_true')
    parser.add_argument("--output", dest='output', help="output folder for --generate command, if a temporary directory is not desired. Must not exist.", type=str, default=None)
    parser.add_argument('--test', dest='test', help="test an experiment folder with the experiment robot", default=False, action='store_true')

    try:
        args = parser.parse_args()
    except:
        parser.print_help()
        sys.exit(0)

    # Check if the person wants to preview experiment or battery
    if args.preview == True:
        preview_experiment(folder=args.folder,battery_folder=args.battery_folder,port=args.port)

    # Generate a local battery folder (static)
    elif args.generate == True:

        if args.experiments != None:

            # Deploy a psiturk battery folder
            experiments = args.experiments.split(",")
            if args.psiturk == True:
                outdir = generate(battery_dest=args.output,
                                  battery_repo=args.battery_folder,
                                  experiment_repo=args.folder,
                                  experiments=experiments,
                                  make_config=True,
                                  warning=False)

            # Deploy a regular battery folder
            else:
                outdir = generate_local(battery_dest=args.output,
                                        subject_id="expfactory_battery_result",
                                        battery_repo=args.battery_folder,
                                        experiment_repo=args.folder,
                                        experiments=experiments,
                                        warning=False,
                                        time=args.time)

            print "Battery generation complete: static files are in %s" %(outdir)

        else:
            print "Please specify list of comma separated experiments with --experiments"

    # Run a local battery
    elif args.run == True:

        # Warn the user about using repos for experiments and battery
        if args.battery_folder == None:
            print "No battery folder specified. Will pull latest battery from expfactory-battery repo"

        if args.folder == None:
            print "No experiments, games, or surveys folder specified. Will pull latest from expfactory-experiments repo"

        if args.survey != None:
            survey = args.survey.split(",")
            if len(survey) > 0:
                print "Currently only support running one survey, will run first in list."
                survey = survey[0]
            run_single(exp_id=survey,
                       repo_type="surveys",
                       source_repo=args.folder,
                       battery_repo=args.battery_folder,
                       port=args.port,
                       subject_id=args.subid)

        if args.game != None:
            game = args.game.split(",")
            if len(game) > 0:
                print "Currently only support running one game, will run first in list."
                game = game[0]
            run_single(exp_id=game,
                       repo_type="games",
                       source_repo=args.folder,
                       battery_repo=args.battery_folder,
                       port=args.port,
                       subject_id=args.subid)

        if args.experiments != None:
            experiments = args.experiments.split(",")
            run_battery(experiments=experiments,
                        experiment_folder=args.folder,
                        subject_id=args.subid,
                        battery_folder=args.battery_folder,
                        port=args.port,
                        time=args.time)
        else:
            print "Please specify list of comma separated experiments with --experiments"

    # Validate a config.json
    elif args.validate == True:
        if args.folder == None:
            folder = os.getcwd()
        validate(experiment_folder=folder)
        # If a survey, and if validates, also validate survey.tsv
        experiment = load_experiment(folder)[0]
        if experiment["template"] == "survey":
            print "Validating survey.tsv..."
            survey_repo = os.path.dirname(folder)
            validate_surveys(experiment["exp_id"],survey_repo)

        
    # Run the experiment robot
    elif args.test == True:
        from expfactory.tests import test_experiment
        test_experiment(folder=args.folder,battery_folder=args.battery_folder,port=args.port)

    # Otherwise, just open the expfactory interface
    else:        
        from expfactory.interface import start
        start(port=args.port)    
Example #30
0
def main(args, parser, subparser):

    folder = args.folder
    if folder is None:
        folder = os.getcwd()

    source = args.src[0]
    if source is None:
        bot.error('Please provide a Github https address to install.')
        sys.exit(1)

    # Is the experiment valid?
    cli = ExperimentValidator()
    valid = cli.validate(source, cleanup=False)
    exp_id = os.path.basename(source).replace('.git', '')

    if valid is True:

        # Local Install
        if os.path.exists(source):
            config = load_experiment(source)
            source = os.path.abspath(source)
        else:
            config = load_experiment("%s/%s" % (cli.tmpdir, exp_id))
            source = "%s/%s" % (cli.tmpdir, exp_id)

        exp_id = config['exp_id']
        python_module = exp_id.replace('-', '_').lower()
    else:
        bot.error('%s is not valid.' % exp_id)
        sys.exit(1)

    # Move static files to output folder
    dest = "%s/%s" % (folder, exp_id)

    bot.log("Installing %s to %s" % (exp_id, dest))

    # Building container
    in_container = False
    if os.environ.get('SINGULARITY_IMAGE') is not None:
        in_container = True

    # Running, live container
    elif os.environ.get('EXPFACTORY_CONTAINER') is not None:
        in_container = True

    if in_container is True:

        # if in container, we always force
        args.force = True

        bot.log("Preparing experiment routes...")
        template = get_template('experiments/template.py')
        template = sub_template(template, '{{ exp_id }}', exp_id)
        template = sub_template(template, '{{ exp_id_python }}', python_module)

        # 1. Python blueprint
        views = get_viewsdir(base=args.base)
        view_output = "%s/%s.py" % (views, python_module)
        save_template(view_output, template, base=views)

        # 2. append to __init__
        init = "%s/__init__.py" % views
        with open(init, 'a') as filey:
            filey.writelines('from .%s import *\n' % python_module)

        # 3. Instructions
        if "instructions" in config:
            instruct = "%s/%s.help" % (views, python_module)
        with open(instruct, 'w') as filey:
            filey.writelines(config['instructions'])

    if not os.path.exists(dest):
        os.system('mkdir -p %s' % dest)
    else:
        if args.force is False:
            bot.error('%s is not empty! Use --force to delete and re-create.' %
                      folder)
            sys.exit(1)

    # We don't need to copy if experiment already there
    if source != dest:
        os.system('cp -R %s/* %s' % (source, dest))
Example #31
0
def main(args,parser,subparser):

    template = 'build/docker/Dockerfile.template'

    # Full path to template is required if provided via input
    if args.input is not None:
        template = args.input

    template = get_template(template)
    
    # For now, only one database provided
    database = args.database
    studyid = args.studyid
    experiments = args.experiments
    branch = "-b %s" %os.environ.get('EXPFACTORY_BRANCH','master')

    headless = "false"
    if args.headless is True:
        headless = "true"

    template = sub_template(template,"{{studyid}}",studyid)
    template = sub_template(template,"{{database}}",database)
    template = sub_template(template,"{{headless}}",headless)
    template = sub_template(template,"{{branch}}",branch)

    if args.headless is True:
        bot.info("Headless build detected, you will need to generate tokens for application entry with expfactory users --new")

    library = get_library(key='name')

    apps = "\n"

    # Add local experiments to library, first preference
    local_installs = 0
    for experiment in experiments:
        if os.path.exists(experiment):

            bot.info('local experiment %s found, validating...' %experiment)

            # Is the experiment valid?
            cli = ExperimentValidator()
            valid = cli.validate(experiment, cleanup=False)

            if valid is True:
                local_installs +=1
                config = load_experiment(experiment)
                exp_id = config['exp_id']

                # If we aren't building in the experiment directory, we need to copy there
                output_dir = "%s/%s" %(os.path.abspath(os.path.dirname(args.output)), exp_id)
                experiment_dir = os.path.abspath(experiment)
                if output_dir != experiment_dir:
                    copy_directory(experiment_dir, output_dir)

                config['local'] = os.path.abspath(experiment)
                library[exp_id] = config


    # Warn the user that local installs are not reproducible (from recipe)
    if local_installs > 0:
        bot.warning("%s local installs detected: build is not reproducible without experiment folders" %local_installs)

    # Build Image with Experiments
    for experiment in experiments:
        exp_id = os.path.basename(experiment)
        if exp_id in library:
            config = library[exp_id]

            app = "LABEL EXPERIMENT_%s /scif/apps/%s\n" %(exp_id, exp_id)

            # Here add custom build routine, should be list of lines
            if "install" in config:
                commands = "\n".join(["RUN %s "%s for x in config['install']]).strip('\n')
                app = "%s%s\n" %(app, commands)

            # The final installation step, either from Github (url) or local folder
            if "local" in config:
                app = "%sADD %s /scif/apps/%s\n" %(app, exp_id, exp_id)
                app = "%sWORKDIR /scif/apps\nRUN expfactory install %s\n" %(app, exp_id)
            else:
                app = "%sWORKDIR /scif/apps\nRUN expfactory install %s\n" %(app, config['github'])  
            apps = "%s%s\n" %(apps,app)


        else:
            bot.warning('%s not in library, check spelling & punctuation.' %exp_id)

    if apps == "\n":
        bot.error('No valid experiments found, cancelling recipe build.')
        sys.exit(1)

    template = sub_template(template,"{{experiments}}",apps)
    outfile = write_file(args.output,template)
    bot.log("Recipe written to %s" %outfile)
Example #32
0
def main(args, parser, subparser):

    template = "build/docker/Dockerfile.template"

    # Full path to template is required if provided via input
    if args.input is not None:
        template = args.input

    template = get_template(template)

    # For now, only one database provided
    database = args.database
    studyid = args.studyid
    experiments = args.experiments
    branch = "-b %s" % os.environ.get("EXPFACTORY_BRANCH", "master")

    headless = "false"
    if args.headless is True:
        headless = "true"

    template = sub_template(template, "{{studyid}}", studyid)
    template = sub_template(template, "{{database}}", database)
    template = sub_template(template, "{{headless}}", headless)
    template = sub_template(template, "{{branch}}", branch)

    if args.headless is True:
        bot.info(
            "Headless build detected, you will need to generate tokens for application entry with expfactory users --new"
        )

    library = get_library(key="name")

    apps = "\n"

    # Add local experiments to library, first preference
    local_installs = 0
    for experiment in experiments:
        if os.path.exists(experiment):

            bot.info("local experiment %s found, validating..." % experiment)

            # Is the experiment valid?
            cli = ExperimentValidator()
            valid = cli.validate(experiment, cleanup=False)

            if valid is True:
                local_installs += 1
                config = load_experiment(experiment)
                exp_id = config["exp_id"]

                # If we aren't building in the experiment directory, we need to copy there
                output_dir = "%s/%s" % (
                    os.path.abspath(os.path.dirname(args.output)),
                    exp_id,
                )
                experiment_dir = os.path.abspath(experiment)
                if output_dir != experiment_dir:
                    copy_directory(experiment_dir, output_dir)

                config["local"] = os.path.abspath(experiment)
                library[exp_id] = config

    # Warn the user that local installs are not reproducible (from recipe)
    if local_installs > 0:
        bot.warning(
            "%s local installs detected: build is not reproducible without experiment folders"
            % local_installs
        )

    # Build Image with Experiments
    for experiment in experiments:
        exp_id = os.path.basename(experiment)
        if exp_id in library:
            config = library[exp_id]

            app = "LABEL EXPERIMENT_%s /scif/apps/%s\n" % (exp_id, exp_id)

            # Here add custom build routine, should be list of lines
            if "install" in config:
                commands = "\n".join(["RUN %s " % s for x in config["install"]]).strip(
                    "\n"
                )
                app = "%s%s\n" % (app, commands)

            # The final installation step, either from Github (url) or local folder
            if "local" in config:
                app = "%sADD %s /scif/apps/%s\n" % (app, exp_id, exp_id)
                app = "%sWORKDIR /scif/apps\nRUN expfactory install %s\n" % (
                    app,
                    exp_id,
                )
            else:
                app = "%sWORKDIR /scif/apps\nRUN expfactory install %s\n" % (
                    app,
                    config["github"],
                )
            apps = "%s%s\n" % (apps, app)

        else:
            bot.warning("%s not in library, check spelling & punctuation." % exp_id)

    if apps == "\n":
        bot.error("No valid experiments found, cancelling recipe build.")
        sys.exit(1)

    template = sub_template(template, "{{experiments}}", apps)
    outfile = write_file(args.output, template)
    bot.log("Recipe written to %s" % outfile)
Example #33
0
def deploy_battery(deployment,
                   battery,
                   experiment_type,
                   context,
                   task_list,
                   template,
                   result,
                   next_page=None,
                   last_experiment=False):
    '''deploy_battery is a general function for returning the final view to deploy a battery, either local or MTurk
    :param deployment: either "docker-mturk" or "docker-local"
    :param battery: models.Battery object
    :param experiment_type: experiments,games,or surveys
    :param context: context, which should already include next_page,
    :param next_page: the next page to navigate to [optional] default is to reload the page to go to the next experiment
    :param task_list: list of models.Experiment instances
    :param template: html template to render
    :param result: the result object, turk.models.Result
    :param last_experiment: boolean if true will redirect the user to a page to submit the result (for surveys)
    '''
    if next_page == None:
        next_page = "javascript:window.location.reload();"
    context["next_page"] = next_page

    # Check the user blacklist status
    try:
        blacklist = Blacklist.objects.get(worker=result.worker,
                                          battery=battery)
        if blacklist.active == True:
            return render_to_response("experiments/blacklist.html")
    except:
        pass

    # Get experiment folders
    experiment_folders = [
        os.path.join(media_dir, experiment_type, x.template.exp_id)
        for x in task_list
    ]
    context["experiment_load"] = get_load_static(experiment_folders,
                                                 url_prefix="/")

    # Get code to run the experiment (not in external file)
    runcode = ""

    # Experiments templates
    if experiment_type in ["experiments"]:
        runcode = get_experiment_run(
            experiment_folders,
            deployment=deployment)[task_list[0].template.exp_id]
        if result != None:
            runcode = runcode.replace("{{result.id}}", str(result.id))
        runcode = runcode.replace("{{next_page}}", next_page)
    elif experiment_type in ["games"]:
        experiment = load_experiment(experiment_folders[0])
        runcode = experiment[0]["deployment_variables"]["run"]
    elif experiment_type in ["surveys"]:
        experiment = load_experiment(experiment_folders[0])
        resultid = ""
        if result != None:
            resultid = result.id
        runcode, validation = generate_survey(experiment,
                                              experiment_folders[0],
                                              form_action="/local/%s/" %
                                              resultid,
                                              csrf_token=True)

        # Field will be filled in by browser cookie, and hidden fields are added for data
        csrf_field = '<input type="hidden" name="csrfmiddlewaretoken" value="hello">'
        csrf_field = '%s\n<input type="hidden" name="djstatus" value="FINISHED">' % (
            csrf_field)
        csrf_field = '%s\n<input type="hidden" name="url" value="chickenfingers">' % (
            csrf_field)

        runcode = runcode.replace("{% csrf_token %}", csrf_field)
        context["validation"] = validation

        if last_experiment == True:
            context["last_experiment"] = last_experiment

    context["run"] = runcode
    response = render_to_response(template, context)

    # without this header, the iFrame will not render in Amazon
    response['x-frame-options'] = 'this_can_be_anything'
    return response
Example #34
0
def deploy_battery(deployment,battery,experiment_type,context,task_list,template,result,next_page=None,last_experiment=False):
    '''deploy_battery is a general function for returning the final view to deploy a battery, either local or MTurk
    :param deployment: either "docker-mturk" or "docker-local"
    :param battery: models.Battery object
    :param experiment_type: experiments,games,or surveys
    :param context: context, which should already include next_page,
    :param next_page: the next page to navigate to [optional] default is to reload the page to go to the next experiment
    :param task_list: list of models.Experiment instances
    :param template: html template to render
    :param result: the result object, turk.models.Result
    :param last_experiment: boolean if true will redirect the user to a page to submit the result (for surveys)
    '''
    if next_page == None:
        next_page = "javascript:window.location.reload();"
    context["next_page"] = next_page

    # Check the user blacklist status
    try:
        blacklist = Blacklist.objects.get(worker=result.worker,battery=battery)
        if blacklist.active == True:
            return render_to_response("experiments/blacklist.html")
    except:
        pass

    # Get experiment folders
    experiment_folders = [os.path.join(media_dir,experiment_type,x.template.exp_id) for x in task_list]
    context["experiment_load"] = get_load_static(experiment_folders,url_prefix="/")

    # Get code to run the experiment (not in external file)
    runcode = ""

    # Experiments templates
    if experiment_type in ["experiments"]:
        runcode = get_experiment_run(experiment_folders,deployment=deployment)[task_list[0].template.exp_id]
        if result != None:
            runcode = runcode.replace("{{result.id}}",str(result.id))
        runcode = runcode.replace("{{next_page}}",next_page)
    elif experiment_type in ["games"]:
        experiment = load_experiment(experiment_folders[0])
        runcode = experiment[0]["deployment_variables"]["run"]
    elif experiment_type in ["surveys"]:
        experiment = load_experiment(experiment_folders[0])
        resultid = ""
        if result != None:
            resultid = result.id
        runcode,validation = generate_survey(experiment,experiment_folders[0],
                                             form_action="/local/%s/" %resultid,
                                             csrf_token=True)

        # Field will be filled in by browser cookie, and hidden fields are added for data
        csrf_field = '<input type="hidden" name="csrfmiddlewaretoken" value="hello">'
        csrf_field = '%s\n<input type="hidden" name="djstatus" value="FINISHED">' %(csrf_field)
        csrf_field = '%s\n<input type="hidden" name="url" value="chickenfingers">' %(csrf_field)

        runcode = runcode.replace("{% csrf_token %}",csrf_field)
        context["validation"] = validation

        if last_experiment == True:
            context["last_experiment"] = last_experiment

    context["run"] = runcode
    response = render_to_response(template, context)

    # without this header, the iFrame will not render in Amazon
    response['x-frame-options'] = 'this_can_be_anything'
    return response
Example #35
0
def main(args,parser,subparser):

    folder = args.folder
    if folder is None:
        folder = os.getcwd()

    source = args.src[0]
    if source is None:
        bot.error('Please provide a Github https address to install.')
        sys.exit(1)

    # Is the experiment valid?
    cli = ExperimentValidator()
    valid = cli.validate(source, cleanup=False)
    exp_id = os.path.basename(source).replace('.git','')

    if valid is True:

        # Local Install
        if os.path.exists(source):
            config = load_experiment(source)
            source = os.path.abspath(source)
        else:
            config = load_experiment("%s/%s" %(cli.tmpdir,exp_id))
            source = "%s/%s" %(cli.tmpdir,exp_id)

        exp_id = config['exp_id']
        python_module = exp_id.replace('-','_').lower()
    else:
        bot.error('%s is not valid.' % exp_id)
        sys.exit(1)

    # Move static files to output folder
    dest = "%s/%s" %(folder,exp_id)

    bot.log("Installing %s to %s" %(exp_id, dest))
    
    # Building container
    in_container = False
    if os.environ.get('SINGULARITY_IMAGE') is not None:
        in_container = True

    # Running, live container
    elif os.environ.get('EXPFACTORY_CONTAINER') is not None:
        in_container = True

    if in_container is True:

        # if in container, we always force
        args.force = True

        bot.log("Preparing experiment routes...")
        template = get_template('experiments/template.py')
        template = sub_template(template, '{{ exp_id }}', exp_id)
        template = sub_template(template, '{{ exp_id_python }}', python_module)

        # 1. Python blueprint
        views = get_viewsdir(base=args.base)
        view_output = "%s/%s.py" %(views, python_module)
        save_template(view_output, template, base=views)
    
        # 2. append to __init__
        init = "%s/__init__.py" % views
        with open(init,'a') as filey:
            filey.writelines('from .%s import *\n' %python_module)

        # 3. Instructions
        if "instructions" in config:
            instruct = "%s/%s.help" %(views, python_module)
        with open(instruct,'w') as filey:
            filey.writelines(config['instructions'])

    if not os.path.exists(dest):
        os.system('mkdir -p %s' %dest)
    else:
        if args.force is False:
            bot.error('%s is not empty! Use --force to delete and re-create.' %folder)
            sys.exit(1) 

    # We don't need to copy if experiment already there
    if source != dest:
        os.system('cp -R %s/* %s' %(source, dest))