def assemble_experiment_temp_dir(config): """Create a temp directory from which to run an experiment. The new directory will include: - Copies of custom experiment files which don't match the exclusion policy - Templates and static resources from Dallinger - An export of the loaded configuration - Heroku-specific files (Procile, runtime.txt) from Dallinger Assumes the experiment root directory is the current working directory. Returns the absolute path of the new directory. """ app_id = config.get("id") dst = os.path.join(tempfile.mkdtemp(), app_id) # Copy local experiment files, minus some ExperimentFileSource(os.getcwd()).selective_copy_to(dst) # Export the loaded configuration config.write(filter_sensitive=True, directory=dst) # Save the experiment id with open(os.path.join(dst, "experiment_id.txt"), "w") as file: file.write(app_id) # Copy Dallinger files dallinger_root = dallinger_package_path() ensure_directory(os.path.join(dst, "static", "scripts")) ensure_directory(os.path.join(dst, "static", "css")) frontend_files = [ os.path.join("static", "css", "dallinger.css"), os.path.join("static", "scripts", "dallinger2.js"), os.path.join("static", "scripts", "reqwest.min.js"), os.path.join("static", "scripts", "require.js"), os.path.join("static", "scripts", "reconnecting-websocket.js"), os.path.join("static", "scripts", "spin.min.js"), os.path.join("static", "scripts", "tracker.js"), os.path.join("static", "scripts", "store+json2.min.js"), os.path.join("templates", "error.html"), os.path.join("templates", "error-complete.html"), os.path.join("templates", "launch.html"), os.path.join("templates", "complete.html"), os.path.join("templates", "questionnaire.html"), os.path.join("templates", "thanks.html"), os.path.join("templates", "waiting.html"), os.path.join("static", "robots.txt"), ] frontend_dirs = [os.path.join("templates", "base")] for filename in frontend_files: src = os.path.join(dallinger_root, "frontend", filename) dst_filepath = os.path.join(dst, filename) if not os.path.exists(dst_filepath): shutil.copy(src, dst_filepath) for filename in frontend_dirs: src = os.path.join(dallinger_root, "frontend", filename) dst_filepath = os.path.join(dst, filename) if not os.path.exists(dst_filepath): shutil.copytree(src, dst_filepath) # Copy Heroku files heroku_files = ["Procfile"] for filename in heroku_files: src = os.path.join(dallinger_root, "heroku", filename) shutil.copy(src, os.path.join(dst, filename)) # Write out a runtime.txt file based on configuration pyversion = config.get("heroku_python_version") with open(os.path.join(dst, "runtime.txt"), "w") as file: file.write("python-{}".format(pyversion)) if not config.get("clock_on"): # If the clock process has been disabled, overwrite the Procfile: src = os.path.join(dallinger_root, "heroku", "Procfile_no_clock") shutil.copy(src, os.path.join(dst, "Procfile")) return dst
def assemble_experiment_temp_dir(log, config, for_remote=False): """Create a temp directory from which to run an experiment. If for_remote is set to True the preparation includes bundling the local dallinger version if it was installed in editable mode. The new directory will include: - Copies of custom experiment files which don't match the exclusion policy - Templates and static resources from Dallinger - An export of the loaded configuration - Heroku-specific files (Procile, runtime.txt) from Dallinger - A requirements.txt file with the contents from the constraints.txt file in the experiment (Dallinger should have generated one with pip-compile if needed by the time we reach this code) - A dallinger zip (only if dallinger is installed in editable mode) Assumes the experiment root directory is the current working directory. Returns the absolute path of the new directory. """ exp_id = config.get("id") dst = os.path.join(tempfile.mkdtemp(), exp_id) # Copy local experiment files, minus some ExperimentFileSource(os.getcwd()).selective_copy_to(dst) # Export the loaded configuration config.write(filter_sensitive=True, directory=dst) # Save the experiment id with open(os.path.join(dst, "experiment_id.txt"), "w") as file: file.write(exp_id) # Copy Dallinger files dallinger_root = dallinger_package_path() ensure_directory(os.path.join(dst, "static", "scripts")) ensure_directory(os.path.join(dst, "static", "css")) frontend_files = [ os.path.join("static", "css", "bootstrap.min.css"), os.path.join("static", "css", "dallinger.css"), os.path.join("static", "css", "dashboard.css"), os.path.join("static", "scripts", "jquery-3.5.1.min.js"), os.path.join("static", "scripts", "popper.min.js"), os.path.join("static", "scripts", "bootstrap.min.js"), os.path.join("static", "scripts", "clipboard.min.js"), os.path.join("static", "scripts", "dallinger2.js"), os.path.join("static", "scripts", "network-monitor.js"), os.path.join("static", "scripts", "reqwest.min.js"), os.path.join("static", "scripts", "require.js"), os.path.join("static", "scripts", "reconnecting-websocket.js"), os.path.join("static", "scripts", "spin.min.js"), os.path.join("static", "scripts", "tracker.js"), os.path.join("static", "scripts", "store+json2.min.js"), os.path.join("templates", "error.html"), os.path.join("templates", "error-complete.html"), os.path.join("templates", "launch.html"), os.path.join("templates", "questionnaire.html"), os.path.join("templates", "exit_recruiter.html"), os.path.join("templates", "exit_recruiter_mturk.html"), os.path.join("templates", "waiting.html"), os.path.join("templates", "login.html"), os.path.join("templates", "dashboard_lifecycle.html"), os.path.join("templates", "dashboard_database.html"), os.path.join("templates", "dashboard_heroku.html"), os.path.join("templates", "dashboard_home.html"), os.path.join("templates", "dashboard_monitor.html"), os.path.join("templates", "dashboard_mturk.html"), os.path.join("static", "robots.txt"), ] frontend_dirs = [os.path.join("templates", "base")] for filename in frontend_files: src = os.path.join(dallinger_root, "frontend", filename) dst_filepath = os.path.join(dst, filename) if not os.path.exists(dst_filepath): shutil.copy(src, dst_filepath) for filename in frontend_dirs: src = os.path.join(dallinger_root, "frontend", filename) dst_filepath = os.path.join(dst, filename) if not os.path.exists(dst_filepath): shutil.copytree(src, dst_filepath) # Copy Heroku files heroku_files = ["Procfile"] for filename in heroku_files: src = os.path.join(dallinger_root, "heroku", filename) shutil.copy(src, os.path.join(dst, filename)) # Write out a runtime.txt file based on configuration pyversion = config.get("heroku_python_version") with open(os.path.join(dst, "runtime.txt"), "w") as file: file.write("python-{}".format(pyversion)) if not config.get("clock_on"): # If the clock process has been disabled, overwrite the Procfile: src = os.path.join(dallinger_root, "heroku", "Procfile_no_clock") shutil.copy(src, os.path.join(dst, "Procfile")) ExplicitFileSource(os.getcwd()).selective_copy_to(dst) requirements_path = Path(dst) / "requirements.txt" # Overwrite the requirements.txt file with the contents of the constraints.txt file (Path(dst) / "constraints.txt").replace(requirements_path) if for_remote: dallinger_path = get_editable_dallinger_path() if dallinger_path: log("Dallinger is installed as an editable package, " "and so will be copied and deployed in its current state, " "ignoring the dallinger version specified in your experiment's " "requirements.txt file!") egg_name = build_and_place(dallinger_path, dst) # Replace the line about dallinger in requirements.txt so that # it refers to the just generated package constraints_text = requirements_path.read_text() new_constraints_text = re.sub("dallinger==.*", f"file:{egg_name}", constraints_text) requirements_path.write_text(new_constraints_text) return dst