class TestProjectController(): def setup_method(self): self.temp_dir = tempfile.mkdtemp(dir=test_datmo_dir) Config().set_home(self.temp_dir) self.project_controller = ProjectController() self.environment_ids = [] def teardown_method(self): if not check_docker_inactive(test_datmo_dir): self.project_controller = ProjectController() if self.project_controller.is_initialized: self.environment_controller = EnvironmentController() for env_id in list(set(self.environment_ids)): if not self.environment_controller.delete(env_id): raise Exception def test_init_failure_none(self): # Test failed case failed = False try: self.project_controller.init(None, None) except ValidationFailed: failed = True assert failed def test_init_failure_empty_str(self): # Test failed case failed = False try: self.project_controller.init("", "") except ValidationFailed: failed = True assert failed assert not self.project_controller.code_driver.is_initialized assert not self.project_controller.file_driver.is_initialized def test_init_failure_git_code_driver(self): # Create a HEAD.lock file in .git to make GitCodeDriver.init() fail if self.project_controller.code_driver.type == "git": git_dir = os.path.join( self.project_controller.code_driver.filepath, ".git") os.makedirs(git_dir) with open(os.path.join(git_dir, "HEAD.lock"), "a+") as f: f.write(to_bytes("test")) failed = False try: self.project_controller.init("test1", "test description") except Exception: failed = True assert failed assert not self.project_controller.code_driver.is_initialized assert not self.project_controller.file_driver.is_initialized def test_init_success(self): result = self.project_controller.init("test1", "test description") # Tested with is_initialized assert self.project_controller.model.name == "test1" assert self.project_controller.model.description == "test description" assert result and self.project_controller.is_initialized # Changeable by user, not tested in is_initialized assert self.project_controller.current_session.name == "default" # TODO: Test lower level functions (DAL, JSONStore, etc for interruptions) # def test_init_with_interruption(self): # # Reinitializing after timed interruption during init # @timeout_decorator.timeout(0.001, use_signals=False) # def timed_init_with_interruption(): # result = self.project_controller.init("test1", "test description") # return result # # failed = False # try: # timed_init_with_interruption() # except timeout_decorator.timeout_decorator.TimeoutError: # failed = True # # Tested with is_initialized # assert failed # # # Reperforming init after a wait of 2 seconds # time.sleep(2) # result = self.project_controller.init("test2", "test description") # # Tested with is_initialized # assert self.project_controller.model.name == "test2" # assert self.project_controller.model.description == "test description" # assert result and self.project_controller.is_initialized # # # Changeable by user, not tested in is_initialized # assert self.project_controller.current_session.name == "default" def test_init_reinit_failure_empty_str(self): _ = self.project_controller.init("test1", "test description") failed = True try: self.project_controller.init("", "") except Exception: failed = True assert failed assert self.project_controller.model.name == "test1" assert self.project_controller.model.description == "test description" assert self.project_controller.code_driver.is_initialized assert self.project_controller.file_driver.is_initialized def test_init_reinit_success(self): _ = self.project_controller.init("test1", "test description") # Test out functionality for re-initialize project result = self.project_controller.init("anything", "else") assert self.project_controller.model.name == "anything" assert self.project_controller.model.description == "else" assert result == True def test_cleanup_no_environment(self): self.project_controller.init("test2", "test description") result = self.project_controller.cleanup() assert not self.project_controller.code_driver.is_initialized assert not self.project_controller.file_driver.is_initialized # Ensure that containers built with this image do not exist # assert not self.project_controller.environment_driver.list_containers(filters={ # "ancestor": image_id # }) assert result == True @pytest_docker_environment_failed_instantiation(test_datmo_dir) def test_cleanup_with_environment(self): self.project_controller.init("test2", "test description") result = self.project_controller.cleanup() assert not self.project_controller.code_driver.is_initialized assert not self.project_controller.file_driver.is_initialized assert not self.project_controller.environment_driver.list_images( "datmo-test2") # Ensure that containers built with this image do not exist # assert not self.project_controller.environment_driver.list_containers(filters={ # "ancestor": image_id # }) assert result == True def test_status_basic(self): self.project_controller.init("test3", "test description") status_dict, current_snapshot, latest_snapshot_user_generated, latest_snapshot_auto_generated, unstaged_code, unstaged_environment, unstaged_files = \ self.project_controller.status() assert status_dict assert isinstance(status_dict, dict) assert status_dict['name'] == "test3" assert status_dict['description'] == "test description" assert isinstance(status_dict['config'], dict) assert not current_snapshot assert not latest_snapshot_user_generated assert not latest_snapshot_auto_generated assert unstaged_code # no files, but unstaged because blank commit id has not yet been created (no initial snapshot) assert not unstaged_environment assert not unstaged_files @pytest_docker_environment_failed_instantiation(test_datmo_dir) def test_status_snapshot_task(self): self.project_controller.init("test4", "test description") self.snapshot_controller = SnapshotController() self.task_controller = TaskController() # Create files to add self.snapshot_controller.file_driver.create("dirpath1", directory=True) self.snapshot_controller.file_driver.create("dirpath2", directory=True) self.snapshot_controller.file_driver.create("filepath1") # Create environment definition env_def_path = os.path.join(self.snapshot_controller.home, "Dockerfile") with open(env_def_path, "wb") as f: f.write(to_bytes("FROM python:3.5-alpine")) environment_paths = [env_def_path] # Create config config_filepath = os.path.join(self.snapshot_controller.home, "config.json") with open(config_filepath, "wb") as f: f.write(to_bytes(str("{}"))) # Create stats stats_filepath = os.path.join(self.snapshot_controller.home, "stats.json") with open(stats_filepath, "wb") as f: f.write(to_bytes(str("{}"))) input_dict = { "message": "my test snapshot", "paths": [ os.path.join(self.snapshot_controller.home, "dirpath1"), os.path.join(self.snapshot_controller.home, "dirpath2"), os.path.join(self.snapshot_controller.home, "filepath1") ], "environment_paths": environment_paths, "config_filename": config_filepath, "stats_filename": stats_filepath, } # Create snapshot in the project, then wait, and try status first_snapshot = self.snapshot_controller.create(input_dict) status_dict, current_snapshot, latest_snapshot_user_generated, latest_snapshot_auto_generated, unstaged_code, unstaged_environment, unstaged_files = \ self.project_controller.status() assert status_dict assert isinstance(status_dict, dict) assert status_dict['name'] == "test4" assert status_dict['description'] == "test description" assert isinstance(status_dict['config'], dict) assert not current_snapshot # snapshot was created from other environments and files (so user is not on any current snapshot) assert isinstance(latest_snapshot_user_generated, Snapshot) assert latest_snapshot_user_generated == first_snapshot assert not latest_snapshot_auto_generated assert not unstaged_code assert not unstaged_environment assert not unstaged_files # Create and run a task and test if task is shown first_task = self.task_controller.create() # Create task_dict task_command = ["sh", "-c", "echo accuracy:0.45"] task_dict = {"command_list": task_command} updated_first_task = self.task_controller.run( first_task.id, task_dict=task_dict) before_snapshot_obj = self.task_controller.dal.snapshot.get_by_id( updated_first_task.before_snapshot_id) after_snapshot_obj = self.task_controller.dal.snapshot.get_by_id( updated_first_task.after_snapshot_id) before_environment_obj = self.task_controller.dal.environment.get_by_id( before_snapshot_obj.environment_id) after_environment_obj = self.task_controller.dal.environment.get_by_id( after_snapshot_obj.environment_id) assert before_environment_obj == after_environment_obj self.environment_ids.append(after_environment_obj.id) status_dict, current_snapshot, latest_snapshot_user_generated, latest_snapshot_auto_generated, unstaged_code, unstaged_environment, unstaged_files = \ self.project_controller.status() assert status_dict assert isinstance(status_dict, dict) assert status_dict['name'] == "test4" assert status_dict['description'] == "test description" assert isinstance(status_dict['config'], dict) assert isinstance(current_snapshot, Snapshot) assert isinstance(latest_snapshot_user_generated, Snapshot) assert latest_snapshot_user_generated == first_snapshot assert isinstance(latest_snapshot_auto_generated, Snapshot) # current snapshot is the before snapshot for the run assert current_snapshot == before_snapshot_obj assert current_snapshot != latest_snapshot_auto_generated assert current_snapshot != latest_snapshot_user_generated # latest autogenerated snapshot is the after snapshot id assert latest_snapshot_auto_generated == after_snapshot_obj assert latest_snapshot_auto_generated != latest_snapshot_user_generated # user generated snapshot is not associated with any before or after snapshot assert latest_snapshot_user_generated != before_snapshot_obj assert latest_snapshot_user_generated != after_snapshot_obj assert not unstaged_code assert not unstaged_environment assert not unstaged_files
class ProjectCommand(BaseCommand): def __init__(self, cli_helper): super(ProjectCommand, self).__init__(cli_helper) self.project_controller = ProjectController() def init(self, name, description): """Initialize command Parameters ---------- name : str name for the project description : str description of the project Returns ------- datmo.core.entity.model.Model """ # Check if project already exists is_new_model = False if not self.project_controller.model: is_new_model = True if is_new_model: # Initialize a new project self.cli_helper.echo( __("info", "cli.project.init.create", {"path": self.project_controller.home})) if not name: _, default_name = os.path.split(self.project_controller.home) name = self.cli_helper.prompt(__("prompt", "cli.project.init.name"), default=default_name) if not description: description = self.cli_helper.prompt( __("prompt", "cli.project.init.description")) try: success = self.project_controller.init(name, description) if success: self.cli_helper.echo( __("info", "cli.project.init.create.success", { "name": name, "path": self.project_controller.home })) except Exception: self.cli_helper.echo( __("info", "cli.project.init.create.failure", { "name": name, "path": self.project_controller.home })) return None else: # Update the current project self.cli_helper.echo( __( "info", "cli.project.init.update", { "name": self.project_controller.model.name, "path": self.project_controller.home })) # Prompt for the name and description and add default if not given if not name: name = self.cli_helper.prompt( __("prompt", "cli.project.init.name"), default=self.project_controller.model.name) if not description: description = self.cli_helper.prompt( __("prompt", "cli.project.init.description"), default=self.project_controller.model.description) # Update the project with the values given try: success = self.project_controller.init(name, description) if success: self.cli_helper.echo( __("info", "cli.project.init.update.success", { "name": name, "path": self.project_controller.home })) except Exception: self.cli_helper.echo( __("info", "cli.project.init.update.failure", { "name": name, "path": self.project_controller.home })) return None self.cli_helper.echo("") # Print out simple project meta data for k, v in self.project_controller.model.to_dictionary().items(): if k != "config": self.cli_helper.echo(str(k) + ": " + str(v)) # Ask question if the user would like to setup environment environment_setup = self.cli_helper.prompt_bool( __("prompt", "cli.project.environment.setup")) if environment_setup: # TODO: ramove business logic from here and create common helper # Setting up the environment definition file self.environment_controller = EnvironmentController() environment_types = self.environment_controller.get_environment_types( ) environment_type = self.cli_helper.prompt_available_options( environment_types, option_type="type") available_environment_frameworks = self.environment_controller.get_supported_frameworks( environment_type) environment_framework = self.cli_helper.prompt_available_options( available_environment_frameworks, option_type="framework") available_environment_languages = self.environment_controller.get_supported_languages( environment_type, environment_framework) environment_language = self.cli_helper.prompt_available_options( available_environment_languages, option_type="language") options = { "environment_type": environment_type, "environment_framework": environment_framework, "environment_language": environment_language } environment_obj = self.environment_controller.setup( options=options) self.cli_helper.echo( __("info", "cli.environment.setup.success", (environment_obj.name, environment_obj.id))) else: self.cli_helper.echo( "there was no environment setup. you can get information" " here: https://datmo.readthedocs.io/en/latest/env-setup.html") return self.project_controller.model def version(self): return self.cli_helper.echo("datmo version: %s" % __version__) @Helper.notify_no_project_found def status(self): status_dict, current_snapshot, latest_snapshot_user_generated, latest_snapshot_auto_generated, unstaged_code, unstaged_environment, unstaged_files = \ self.project_controller.status() # Print out simple project meta data for k, v in status_dict.items(): if k != "config": self.cli_helper.echo(str(k) + ": " + str(v)) self.cli_helper.echo("") # Print out any unstaged changes else print out the latest snapshot state of the repository if not unstaged_code and not unstaged_environment and not unstaged_files: self.cli_helper.echo( "all changes have been saved, no unstaged changes") self.cli_helper.echo("") self.cli_helper.echo("current snapshot state of the repository: ") if current_snapshot: self.cli_helper.echo(current_snapshot) else: # Print out the unstaged components if unstaged self.cli_helper.echo("unstaged changes since latest snapshot:") if unstaged_code: self.cli_helper.echo("code has been changed") if unstaged_environment: self.cli_helper.echo("environment has been changed") if unstaged_files: self.cli_helper.echo("files have been changed") # Print out info for the latest snapshot (the most recent first, and state if autogenerated or by user) if latest_snapshot_user_generated and not latest_snapshot_auto_generated: self.cli_helper.echo("latest snapshot generated by the user: "******"no snapshot autogenerated by datmo") elif latest_snapshot_auto_generated and not latest_snapshot_user_generated: self.cli_helper.echo("latest snapshot autogenerated by datmo: ") self.cli_helper.echo(latest_snapshot_auto_generated) self.cli_helper.echo("no snapshot generated by the user") elif not latest_snapshot_user_generated and not latest_snapshot_auto_generated: self.cli_helper.echo("no snapshots created yet") elif latest_snapshot_user_generated.created_at > latest_snapshot_auto_generated.created_at: self.cli_helper.echo("latest snapshot generated by the user: "******"latest snapshot autogenerated by datmo: ") self.cli_helper.echo(latest_snapshot_auto_generated) elif latest_snapshot_user_generated.created_at < latest_snapshot_auto_generated.created_at: self.cli_helper.echo("latest snapshot autogenerated by datmo: ") self.cli_helper.echo(latest_snapshot_auto_generated) self.cli_helper.echo("latest snapshot generated by the user: "******"prompt", "cli.project.cleanup.confirm")) # Cleanup datmo project if user specifies if response: self.cli_helper.echo( __( "info", "cli.project.cleanup", { "name": self.project_controller.model.name, "path": self.project_controller.home })) try: success = self.project_controller.cleanup() if success: self.cli_helper.echo( __( "info", "cli.project.cleanup.success", { "name": self.project_controller.model.name, "path": self.project_controller.home })) return success except Exception: self.cli_helper.echo( __( "info", "cli.project.cleanup.failure", { "name": self.project_controller.model.name, "path": self.project_controller.home })) return False
class ProjectCommand(BaseCommand): def __init__(self, cli_helper): super(ProjectCommand, self).__init__(cli_helper) self.project_controller = ProjectController() def init(self, name, description, force): """Initialize command Parameters ---------- name : str name for the project description : str description of the project force : bool Boolean to force initialization without prompts Returns ------- datmo.core.entity.model.Model """ # Check if project already exists is_new_model = False if not self.project_controller.model: is_new_model = True if is_new_model: # Initialize a new project self.cli_helper.echo( __("info", "cli.project.init.create", {"path": self.project_controller.home})) if not name: _, default_name = os.path.split(self.project_controller.home) if not force: name = self.cli_helper.prompt(__("prompt", "cli.project.init.name"), default=default_name) else: name = default_name if not description: if not force: description = self.cli_helper.prompt( __("prompt", "cli.project.init.description")) else: description = "" try: success = self.project_controller.init(name, description) if success: self.cli_helper.echo( __("info", "cli.project.init.create.success", { "name": name, "path": self.project_controller.home })) except Exception: self.cli_helper.echo( __("info", "cli.project.init.create.failure", { "name": name, "path": self.project_controller.home })) return None else: # Update the current project self.cli_helper.echo( __( "info", "cli.project.init.update", { "name": self.project_controller.model.name, "path": self.project_controller.home })) # Prompt for the name and description and add default if not given if not name and not force: name = self.cli_helper.prompt( __("prompt", "cli.project.init.name"), default=self.project_controller.model.name) elif force: name = self.project_controller.model.name if not description and not force: description = self.cli_helper.prompt( __("prompt", "cli.project.init.description"), default=self.project_controller.model.description) elif force: description = self.project_controller.model.description # Update the project with the values given try: success = self.project_controller.init(name, description) if success: self.cli_helper.echo( __("info", "cli.project.init.update.success", { "name": name, "path": self.project_controller.home })) except Exception: self.cli_helper.echo( __("info", "cli.project.init.update.failure", { "name": name, "path": self.project_controller.home })) return None self.cli_helper.echo("") # Print out simple project meta data for k, v in self.project_controller.model.to_dictionary().items(): if k != "config": self.cli_helper.echo(str(k) + ": " + str(v)) # Ask question if the user would like to setup environment environment_setup = self.cli_helper.prompt_bool( __("prompt", "cli.project.environment.setup")) if not force else False if environment_setup: # TODO: remove business logic from here and create common helper # Setting up the environment definition file self.environment_controller = EnvironmentController() environment_types = self.environment_controller.get_environment_types( ) environment_type = self.cli_helper.prompt_available_options( environment_types, option_type="type") available_environment_frameworks = self.environment_controller.get_supported_frameworks( environment_type) environment_framework = self.cli_helper.prompt_available_options( available_environment_frameworks, option_type="framework") available_environment_languages = self.environment_controller.get_supported_languages( environment_type, environment_framework) environment_language = self.cli_helper.prompt_available_options( available_environment_languages, option_type="language") options = { "environment_type": environment_type, "environment_framework": environment_framework, "environment_language": environment_language } environment_obj = self.environment_controller.setup( options=options) self.cli_helper.echo( __("info", "cli.environment.setup.success", (environment_obj.name, environment_obj.id))) else: self.cli_helper.echo( "there was no environment setup. you can get information" " here: https://datmo.readthedocs.io/en/latest/env-setup.html") return self.project_controller.model def version(self): return self.cli_helper.echo("datmo version: %s" % __version__) def configure(self): """ Configure datmo installation """ # General setup setup_remote_bool = self.cli_helper.prompt_bool( "Would you like to setup your remote credentials? [yN]") if setup_remote_bool: datmo_api_key = None while not datmo_api_key: datmo_api_key = self.cli_helper.prompt( "Enter API key for datmo") # Initialize remote API to get master ip address remote_api = RemoteAPI(datmo_api_key) response = remote_api.get_deployment_info() master_system_info = response['body']['master_system_info'] master_server_ip = str(master_system_info.get('datmo_master_ip')) if isinstance(master_system_info, dict)\ else None # Create a config file self.datmo_config = JSONStore( os.path.join(os.path.expanduser("~"), ".datmo", "config")) config = dict() if master_server_ip and datmo_api_key: config["MASTER_SERVER_IP"] = master_server_ip config["DATMO_API_KEY"] = datmo_api_key self.datmo_config.to_file(config) else: self.cli_helper.echo( "Datmo API key could not be saved. Please try again") # Setup project specific things if self.project_controller.model: pass else: self.cli_helper.echo( "No datmo project found. Skipping configuration for project.") @Helper.notify_no_project_found def status(self): status_dict, current_snapshot, latest_snapshot_user_generated, latest_snapshot_auto_generated, unstaged_code, unstaged_environment, unstaged_files = \ self.project_controller.status() # Print out simple project meta data for k, v in status_dict.items(): if k != "config": self.cli_helper.echo(str(k) + ": " + str(v)) self.cli_helper.echo("") # Print out any unstaged changes else print out the latest snapshot state of the repository if not unstaged_code and not unstaged_environment and not unstaged_files: self.cli_helper.echo( "all changes have been saved, no unstaged changes") self.cli_helper.echo("") self.cli_helper.echo("current snapshot state of the repository: ") if current_snapshot: self.cli_helper.echo(current_snapshot) else: # Print out the unstaged components if unstaged self.cli_helper.echo("unstaged changes since latest snapshot:") if unstaged_code: self.cli_helper.echo("code has been changed") if unstaged_environment: self.cli_helper.echo("environment has been changed") if unstaged_files: self.cli_helper.echo("files have been changed") # Print out info for the latest snapshot (the most recent first, and state if autogenerated or by user) if latest_snapshot_user_generated and not latest_snapshot_auto_generated: self.cli_helper.echo("latest snapshot generated by the user: "******"no snapshot autogenerated by datmo") elif latest_snapshot_auto_generated and not latest_snapshot_user_generated: self.cli_helper.echo("latest snapshot autogenerated by datmo: ") self.cli_helper.echo(latest_snapshot_auto_generated) self.cli_helper.echo("no snapshot generated by the user") elif not latest_snapshot_user_generated and not latest_snapshot_auto_generated: self.cli_helper.echo("no snapshots created yet") elif latest_snapshot_user_generated.created_at > latest_snapshot_auto_generated.created_at: self.cli_helper.echo("latest snapshot generated by the user: "******"latest snapshot autogenerated by datmo: ") self.cli_helper.echo(latest_snapshot_auto_generated) elif latest_snapshot_user_generated.created_at < latest_snapshot_auto_generated.created_at: self.cli_helper.echo("latest snapshot autogenerated by datmo: ") self.cli_helper.echo(latest_snapshot_auto_generated) self.cli_helper.echo("latest snapshot generated by the user: "******"prompt", "cli.project.cleanup.confirm")) # Cleanup datmo project if user specifies if response: name = self.project_controller.model.name if self.project_controller.model.name else "" path = self.project_controller.home if self.project_controller.home else "" self.cli_helper.echo( __("info", "cli.project.cleanup", { "name": name, "path": path })) try: success = self.project_controller.cleanup() if success: self.cli_helper.echo( __("info", "cli.project.cleanup.success", { "name": name, "path": path })) return success except Exception: self.cli_helper.echo( __("info", "cli.project.cleanup.failure", { "name": name, "path": path })) return False def dashboard(self): if not self.project_controller.is_initialized: self.cli_helper.echo( "Please initialize datmo before using this command") return False dir_path = os.path.dirname(os.path.abspath(__file__)) os.chdir(os.path.join(dir_path, "../../dashboard")) app.run(host='0.0.0.0') return True
class ProjectCommand(BaseCommand): # NOTE: dal_driver is not passed into the project because it is created # first by ProjectController and then passed down to all other Controllers def __init__(self, home, cli_helper): super(ProjectCommand, self).__init__(home, cli_helper) self.project_controller = ProjectController(home=home) def init(self, name, description): """Initialize command Parameters ---------- name : str name for the project description : str description of the project Returns ------- datmo.core.entity.model.Model """ # Check if project already exists is_new_model = False if not self.project_controller.model: is_new_model = True if is_new_model: # Initialize a new project self.cli_helper.echo( __("info", "cli.project.init.create", { "name": name, "path": self.home })) if not name: name = self.cli_helper.prompt( __("prompt", "cli.project.init.name")) if not description: description = self.cli_helper.prompt( __("prompt", "cli.project.init.description")) try: success = self.project_controller.init(name, description) if success: self.cli_helper.echo( __("info", "cli.project.init.create.success", { "name": name, "path": self.home })) except: self.cli_helper.echo( __("info", "cli.project.init.create.failure", { "name": name, "path": self.home })) return None else: # Update the current project self.cli_helper.echo( __("info", "cli.project.init.update", { "name": self.project_controller.model.name, "path": self.home })) if not name: name = self.cli_helper.prompt( __("prompt", "cli.project.init.name")) if not description: description = self.cli_helper.prompt( __("prompt", "cli.project.init.description")) # Update project parameter if given parameter is not falsy and different if not name or name == self.project_controller.model.name: name = self.project_controller.model.name if not description or description == self.project_controller.model.description: description = self.project_controller.model.description # Update the project with the values given try: success = self.project_controller.init(name, description) if success: self.cli_helper.echo( __("info", "cli.project.init.update.success", { "name": name, "path": self.home })) except: self.cli_helper.echo( __("info", "cli.project.init.update.failure", { "name": name, "path": self.home })) return None self.cli_helper.echo("") # Print out simple project meta data for k, v in self.project_controller.model.to_dictionary().items(): if k != "config": self.cli_helper.echo(str(k) + ": " + str(v)) return self.project_controller.model def version(self): return self.cli_helper.echo("datmo version: %s" % __version__) def status(self): status_dict, latest_snapshot, ascending_unstaged_tasks = self.project_controller.status( ) # Print out simple project meta data for k, v in status_dict.items(): if k != "config": self.cli_helper.echo(str(k) + ": " + str(v)) self.cli_helper.echo("") # Print out project config meta data self.cli_helper.echo("config: ") self.cli_helper.echo(status_dict['config']) self.cli_helper.echo("") # Print out latest snapshot info self.cli_helper.echo("latest snapshot id: ") if latest_snapshot: self.cli_helper.echo(latest_snapshot.id) else: self.cli_helper.echo("no snapshots created yet") self.cli_helper.echo("") # Print out unstaged tasks self.cli_helper.echo("unstaged task ids:") if ascending_unstaged_tasks: for task in ascending_unstaged_tasks: self.cli_helper.echo(task.id) else: self.cli_helper.echo("no unstaged tasks") return status_dict, latest_snapshot, ascending_unstaged_tasks def cleanup(self): # Prompt user to ensure they would like to remove all datmo information response = self.cli_helper.prompt_bool( __("prompt", "cli.project.cleanup.confirm")) # Cleanup datmo project if user specifies if response: self.cli_helper.echo( __( "info", "cli.project.cleanup", { "name": self.project_controller.model.name, "path": self.project_controller.home })) try: success = self.project_controller.cleanup() if success: self.cli_helper.echo( __( "info", "cli.project.cleanup.success", { "name": self.project_controller.model.name, "path": self.project_controller.home })) return success except Exception: self.cli_helper.echo( __( "info", "cli.project.cleanup.failure", { "name": self.project_controller.model.name, "path": self.project_controller.home })) return False
class TestProjectController(): def setup_method(self): # provide mountable tmp directory for docker tempfile.tempdir = "/tmp" if not platform.system( ) == "Windows" else None test_datmo_dir = os.environ.get('TEST_DATMO_DIR', tempfile.gettempdir()) self.temp_dir = tempfile.mkdtemp(dir=test_datmo_dir) self.project = ProjectController(self.temp_dir) def teardown_method(self): pass def test_init_none(self): # Test failed case failed = False try: self.project.init(None, None) except ValidationFailed: failed = True assert failed def test_init_empty_str(self): # Test failed case failed = False try: self.project.init("", "") except ValidationFailed: failed = True assert failed def test_init(self): result = self.project.init("test1", "test description") # Tested with is_initialized assert self.project.model.name == "test1" assert self.project.model.description == "test description" assert self.project.code_driver.is_initialized assert self.project.file_driver.is_initialized assert self.project.environment_driver.is_initialized assert result and self.project.is_initialized # Changeable by user, not tested in is_initialized assert self.project.current_session.name == "default" # Check Project template if user specified template # TODO: Add in Project template if user specifies # Test out functionality for re-initialize project result = self.project.init("anything", "else") assert self.project.model.name == "anything" assert self.project.model.description == "else" assert result == True def test_cleanup(self): self.project.init("test2", "test description") result = self.project.cleanup() assert not self.project.code_driver.exists_code_refs_dir() assert not self.project.file_driver.exists_datmo_file_structure() assert not self.project.environment_driver.list_images("datmo-test2") # Ensure that containers built with this image do not exist # assert not self.project.environment_driver.list_containers(filters={ # "ancestor": image_id # }) assert result == True def test_status_basic(self): self.project.init("test3", "test description") status_dict, latest_snapshot, ascending_unstaged_task_list = \ self.project.status() assert status_dict assert isinstance(status_dict, dict) assert status_dict['name'] == "test3" assert status_dict['description'] == "test description" assert isinstance(status_dict['config'], dict) assert not latest_snapshot assert not ascending_unstaged_task_list def test_status_snapshot_task(self): self.project.init("test4", "test description") self.snapshot = SnapshotController(self.temp_dir) self.task = TaskController(self.temp_dir) # Create files to add self.snapshot.file_driver.create("dirpath1", directory=True) self.snapshot.file_driver.create("dirpath2", directory=True) self.snapshot.file_driver.create("filepath1") # Create environment_driver definition env_def_path = os.path.join(self.snapshot.home, "Dockerfile") with open(env_def_path, "w") as f: f.write(to_unicode(str("FROM datmo/xgboost:cpu"))) # Create config config_filepath = os.path.join(self.snapshot.home, "config.json") with open(config_filepath, "w") as f: f.write(to_unicode(str("{}"))) # Create stats stats_filepath = os.path.join(self.snapshot.home, "stats.json") with open(stats_filepath, "w") as f: f.write(to_unicode(str("{}"))) input_dict = { "message": "my test snapshot", "filepaths": [ os.path.join(self.snapshot.home, "dirpath1"), os.path.join(self.snapshot.home, "dirpath2"), os.path.join(self.snapshot.home, "filepath1") ], "environment_definition_filepath": env_def_path, "config_filename": config_filepath, "stats_filename": stats_filepath, } # Create snapshot in the project first_snapshot = self.snapshot.create(input_dict) status_dict, latest_snapshot, ascending_unstaged_task_list = \ self.project.status() assert status_dict assert isinstance(status_dict, dict) assert status_dict['name'] == "test4" assert status_dict['description'] == "test description" assert isinstance(status_dict['config'], dict) assert isinstance(latest_snapshot, Snapshot) assert latest_snapshot.id == first_snapshot.id assert not ascending_unstaged_task_list # Create and run a task and test if unstaged task is shown first_task = self.task.create() # Create task_dict task_command = ["sh", "-c", "echo accuracy:0.45"] task_dict = {"command": task_command} updated_first_task = self.task.run(first_task.id, task_dict=task_dict) status_dict, latest_snapshot, ascending_unstaged_task_list = \ self.project.status() assert status_dict assert isinstance(status_dict, dict) assert status_dict['name'] == "test4" assert status_dict['description'] == "test description" assert isinstance(status_dict['config'], dict) assert isinstance(latest_snapshot, Snapshot) assert latest_snapshot.id == first_snapshot.id assert isinstance(ascending_unstaged_task_list[0], Task) assert ascending_unstaged_task_list[0].id == updated_first_task.id
class ProjectCommand(BaseCommand): def __init__(self, cli_helper): super(ProjectCommand, self).__init__(cli_helper) self.project_controller = ProjectController() def init(self, name, description): """Initialize command Parameters ---------- name : str name for the project description : str description of the project Returns ------- datmo.core.entity.model.Model """ # Check if project already exists is_new_model = False if not self.project_controller.model: is_new_model = True if is_new_model: # Initialize a new project self.cli_helper.echo( __("info", "cli.project.init.create", {"path": self.project_controller.home})) if not name: _, default_name = os.path.split(self.project_controller.home) name = self.cli_helper.prompt(__("prompt", "cli.project.init.name"), default=default_name) if not description: description = self.cli_helper.prompt( __("prompt", "cli.project.init.description")) try: success = self.project_controller.init(name, description) if success: self.cli_helper.echo( __("info", "cli.project.init.create.success", { "name": name, "path": self.project_controller.home })) except Exception: self.cli_helper.echo( __("info", "cli.project.init.create.failure", { "name": name, "path": self.project_controller.home })) return None else: # Update the current project self.cli_helper.echo( __( "info", "cli.project.init.update", { "name": self.project_controller.model.name, "path": self.project_controller.home })) # Prompt for the name and description and add default if not given if not name: name = self.cli_helper.prompt( __("prompt", "cli.project.init.name"), default=self.project_controller.model.name) if not description: description = self.cli_helper.prompt( __("prompt", "cli.project.init.description"), default=self.project_controller.model.description) # Update the project with the values given try: success = self.project_controller.init(name, description) if success: self.cli_helper.echo( __("info", "cli.project.init.update.success", { "name": name, "path": self.project_controller.home })) except Exception: self.cli_helper.echo( __("info", "cli.project.init.update.failure", { "name": name, "path": self.project_controller.home })) return None self.cli_helper.echo("") # Print out simple project meta data for k, v in self.project_controller.model.to_dictionary().items(): if k != "config": self.cli_helper.echo(str(k) + ": " + str(v)) # Ask question if the user would like to setup environment environment_setup = self.cli_helper.prompt_bool( __("prompt", "cli.project.environment.setup")) if environment_setup: # Setting up the environment definition file self.environment_controller = EnvironmentController() available_environments = self.environment_controller.get_supported_environments( ) input_environment_name = self.cli_helper.prompt_available_environments( available_environments) try: options = {"name": input_environment_name} environment_obj = self.environment_controller.setup( options=options) self.cli_helper.echo( __("info", "cli.environment.setup.success", (environment_obj.name, environment_obj.id))) except EnvironmentDoesNotExist: self.cli_helper.echo( __("error", "cli.environment.setup.argument", input_environment_name)) return self.project_controller.model def version(self): return self.cli_helper.echo("datmo version: %s" % __version__) @Helper.notify_no_project_found def status(self): status_dict, latest_snapshot, ascending_unstaged_tasks = self.project_controller.status( ) # Print out simple project meta data for k, v in status_dict.items(): if k != "config": self.cli_helper.echo(str(k) + ": " + str(v)) self.cli_helper.echo("") # Print out project config meta data self.cli_helper.echo("config: ") self.cli_helper.echo(status_dict['config']) self.cli_helper.echo("") # Print out latest snapshot info self.cli_helper.echo("latest snapshot id: ") if latest_snapshot: self.cli_helper.echo(latest_snapshot.id) else: self.cli_helper.echo("no snapshots created yet") self.cli_helper.echo("") # Print out unstaged tasks self.cli_helper.echo("unstaged task ids:") if ascending_unstaged_tasks: for task in ascending_unstaged_tasks: self.cli_helper.echo(task.id) else: self.cli_helper.echo("no unstaged tasks") return status_dict, latest_snapshot, ascending_unstaged_tasks def cleanup(self): # Prompt user to ensure they would like to remove datmo information along with environment and files folder response = self.cli_helper.prompt_bool( __("prompt", "cli.project.cleanup.confirm")) # Cleanup datmo project if user specifies if response: self.cli_helper.echo( __( "info", "cli.project.cleanup", { "name": self.project_controller.model.name, "path": self.project_controller.home })) try: success = self.project_controller.cleanup() if success: self.cli_helper.echo( __( "info", "cli.project.cleanup.success", { "name": self.project_controller.model.name, "path": self.project_controller.home })) return success except Exception: self.cli_helper.echo( __( "info", "cli.project.cleanup.failure", { "name": self.project_controller.model.name, "path": self.project_controller.home })) return False