class TestExperiment(TestCase): def setUp(self): self.ExpValidator = ExperimentValidator() self.base = "/scif/apps" self.experiments = glob("%s/*" % self.base) self.contenders = [os.path.basename(x) for x in self.experiments] def test_experiment(self): """test an experiment, including the markdown file, and repo itself """ print("...Test: Experiment Validation") # First priority - the user gave an experiment folder if "config.json" in self.contenders: for experiment in self.experiments: name = os.path.basename(experiment) if name == "config.json": valid = self.ExpValidator.validate(experiment, validate_folder=False) self.assertTrue(valid) # Otherwise, the user gave a folder with subfolders else: for experiment in self.experiments: name = os.path.basename(experiment) if os.path.isdir(experiment): print("Found experiment %s" % name) valid = self.ExpValidator.validate(experiment) self.assertTrue(valid)
class TestExperiment(TestCase): def setUp(self): self.ExpValidator = ExperimentValidator() self.base = "/scif/apps" self.experiments = glob("%s/*" %self.base) self.contenders = [os.path.basename(x) for x in self.experiments] def test_experiment(self): '''test an experiment, including the markdown file, and repo itself ''' print("...Test: Experiment Validation") # First priority - the user gave an experiment folder if "config.json" in self.contenders: for experiment in self.experiments: name = os.path.basename(experiment) if name == "config.json": valid = self.ExpValidator.validate(experiment, validate_folder=False) self.assertTrue(valid) # Otherwise, the user gave a folder with subfolders else: for experiment in self.experiments: name = os.path.basename(experiment) if os.path.isdir(experiment): print('Found experiment %s' %name) valid = self.ExpValidator.validate(experiment) self.assertTrue(valid)
class TestContribution(TestCase): def setUp(self): self.LibValidator = LibraryValidator() self.ExpValidator = ExperimentValidator() self.RuntimeValidator = RuntimeValidator() self.experiments_base = "/scif/data" self.experiments = glob("%s/*md" % self.experiments_base) def test_contribution(self): '''test an experiment, including the markdown file, and repo itself ''' if len(self.experiments) == 0: print( 'Please bind the directory with your markdown files for the library.' ) sys.exit(1) print("...Test: Global Library validation") for ymlfile in self.experiments: self.assertTrue(self.LibValidator.validate(ymlfile)) url = self.LibValidator.metadata['github'] self.assertTrue(self.ExpValidator.validate(url)) result = self.RuntimeValidator.validate(url) print(result) print(url) self.assertTrue(result)
class TestContribution(TestCase): def setUp(self): self.LibValidator = LibraryValidator() self.ExpValidator = ExperimentValidator() self.RuntimeValidator = RuntimeValidator() self.experiments_base = "/scif/data" self.experiments = glob("%s/*md" %self.experiments_base) def test_contribution(self): '''test an experiment, including the markdown file, and repo itself ''' if len(self.experiments) == 0: print('Please bind the directory with your markdown files for the library.') sys.exit(1) print("...Test: Global Library validation") for ymlfile in self.experiments: self.assertTrue(self.LibValidator.validate(ymlfile)) url = self.LibValidator.metadata['github'] self.assertTrue(self.ExpValidator.validate(url)) result = self.RuntimeValidator.validate(url) print(result) print(url) self.assertTrue(result)
def validate(folder=None, cleanup=False): '''validate :param folder: full path to experiment folder with config.json. If path begins with https, we assume to be starting from a repository. ''' from expfactory.validator import ExperimentValidator cli = ExperimentValidator() return cli.validate(folder, cleanup=cleanup)
class TestLibrary(TestCase): def setUp(self): self.LibValidator = LibraryValidator() self.ExpValidator = ExperimentValidator() self.RuntimeValidator = RuntimeValidator() self.experiments_base = "%s/docs/_library" % (here) self.experiments = self.get_changed_files() self.added = [x for x in self.experiments if '_library' in x] print('Found %s changed or modified files.' % len(self.added)) def get_changed_files(self): '''use the Github compare url (provided by circle) to find changed files between commits''' # Fallback, return all files in experiment base experiments = glob("%s/*" % self.experiments_base) compare_url = os.environ.get("CIRCLE_COMPARE_URL") # If the variable exists, we are running on Circle if compare_url is not None: print('Detected running in Circle CI') # If defined, derive change list from compare url if compare_url != '': compare_url = "%s.patch" % compare_url print("Compare URL: %s" % compare_url) response = requests.get(compare_url) if response.status_code == 200: experiments = set( re.findall(' docs/_library/.+[.]md', response.text)) experiments = [x.strip() for x in experiments] # Otherwise, use all experiments else: print("No compare URL detected, using entire listing.") experiments = glob('%s/*.md' % self.experiments_base) else: print("Not running in Circle Ci") return experiments def test_experiment(self): '''test an experiment, including the markdown file, and repo itself ''' print("...Test: Global Library validation") for ymlfile in self.experiments: if os.path.exists(ymlfile): print("TESTING %s" % ymlfile) self.assertTrue(self.LibValidator.validate(ymlfile)) url = self.LibValidator.metadata['github'] self.assertTrue(self.ExpValidator.validate(url)) result = self.RuntimeValidator.validate(url) print(result) print(url) self.assertTrue(result)
def validate(self, folder): '''validate is the first entrypoint function for running an experiment or survey robot. It ensures that the content is valid, and then calls _validate (should be defined in subclass)''' validator = ExperimentValidator() valid = validator.validate(folder) if valid is True: # IF missing favicon, add self._check_favicon(folder) valid = self._validate(folder) bot.log("[done] stopping web server...") self.httpd.server_close() else: bot.warning('%s is not valid, skipping robot testing.' %folder)
class TestLibrary(TestCase): def setUp(self): self.LibValidator = LibraryValidator() self.ExpValidator = ExperimentValidator() self.RuntimeValidator = RuntimeValidator() self.experiments_base = "%s/experiments" % (here) self.experiments = glob("%s/*" % self.experiments_base) def test_library(self): '''test_validate_library calls all subfunctions ''' print("...Test: Global Library validation") for jsonfile in self.experiments: self.assertTrue(self.LibValidator.validate(jsonfile)) def test_single_experiments(self): '''test_load_json ensures that all files load ''' for jsonfile in self.experiments: print("...%s" % os.path.basename(jsonfile)) config = read_json(jsonfile) self.assertTrue('github' in config) self.assertTrue(isinstance(config, dict)) url = config['github'] self.assertTrue(self.ExpValidator.validate(url)) def test_previews(self): '''assert that each experiment is previewed at the Github page where served ''' bot.test('Testing experiment previews...') for jsonfile in self.experiments: experiment = os.path.basename(jsonfile) print("...%s experiment preview?" % experiment) config = read_json(jsonfile) self.assertTrue('github' in config) self.assertTrue(isinstance(config, dict)) url = config['github'] result = self.RuntimeValidator.validate(url) print(result) print(url) self.assertTrue(result)
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))
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)
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)
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)
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))