Пример #1
0
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
Пример #2
0
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
Пример #3
0
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
Пример #4
0
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
Пример #5
0
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
Пример #6
0
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