예제 #1
0
    def __check_entry_completeness(entry: Namespace) -> Namespace:
        """
        AUTHORS:
        --------

        :author: Alix Leroy

        DESCRIPTION:
        ------------

        Check if the dictionary formatted entry is complete.
        If not complete, fill the dictionary with default value

        PARAMETERS:
        -----------

        :param entry (Namespace): The entry to check the completeness

        RETURN:
        -------

        :return entry (Namespace): The completed entry

        RAISE:
        ------

        :raise DeepError: Raised if the path is not given
        """

        # SOURCE
        if entry.check("source", None) is False:
            Notification(
                DEEP_NOTIF_FATAL,
                "The source was not specified to the following entry : %s" %
                str(entry.get()))

        # JOIN PATH
        if entry.check("join", None) is False:
            entry.add({"join": None}, None)

        # LOADING METHOD
        if entry.check("load_method", None) is False:
            entry.add({"load_method": "online"})

        # DATA TYPE
        if entry.check("type", None) is False:
            entry.add({"type": None})

        return entry
예제 #2
0
class Brain(FrontalLobe):
    """
    AUTHORS:
    --------

    :author: Alix Leroy
    :author: Samuel Westlake

    DESCRIPTION:
    ------------

    A Brain class that manages the commands of the user and allows to start the training

    PUBLIC METHODS:
    ---------------
    :method wake:
    :method sleep:
    :method save_config: Save all configuration files into self.config_dir.
    :method clear_config: Reset self.config to an empty Namespace.
    :method store_config: Store a copy of self.config in self._config.
    :method restore_config: Set self.config to a copy of self._config.
    :method load_config: Load self.config from self.config_dir.
    :method check_config: Check self.config for missing parameters and set data types accordingly.
    :method clear_logs: Deletes logs that are not to be kept, as decided in the config settings.
    :method close_logs: Closes logs that are to be kept and deletes logs that are to be deleted, see config settings.
    :method ui: Start the Visual Cortex of Deeplodocus

    PRIVATE METHODS:
    ----------------
    :method __init__:
    :method __check_config:
    :method __convert_dtype:
    :method __convert:
    :method __on_wake:
    :method __execute_command:
    :method __preprocess_command:
    :method __illegal_command_messages:
    :method __get_command_flags:
    :method __good_bye:
    """
    def __init__(self, config_dir):
        """
        AUTHORS:
        --------

        :author: Alix Leroy

        DESCRIPTION:
        ------------

        Initialize a Deeplodocus Brain

        PARAMETERS:
        -----------

        :param config_path->str: The config path

        RETURN:
        -------

        :return: None
        """
        self.__close_logs(force=True)
        Logo(version=__version__)
        FrontalLobe.__init__(self)  # Model Manager
        self.config_dir = config_dir
        self.visual_cortex = None
        time.sleep(0.5)  # Wait for the UI to respond
        self.config = None
        self._config = None
        self.load_config()
        self.set_device()
        Thalamus()  # Initialize the Signal Manager

    """
    "
    " Public Methods
    "
    """

    def wake(self):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake
        :author: Alix Leroy

        DESCRIPTION:
        ------------
        Deeplodocus terminal commands

        RETURN:
        -------
        :return: None
        """
        self.__on_wake()
        while True:
            try:
                command = Notification(DEEP_NOTIF_INPUT,
                                       DEEP_MSG_INSTRUCTRION).get()
            except KeyboardInterrupt:
                self.sleep()
            self.__execute_command(command)

    def sleep(self):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake
        :author: Alix Leroy

        DESCRIPTION:
        ------------
        Stop the interface, close logs and print good-bye message

        RETURN:
        -------
        :return: None
        """
        # Stop the visual cortex
        if self.visual_cortex is not None:
            self.visual_cortex.stop()
        self.__good_bye()
        self.__close_logs()
        raise SystemExit(0)

    def save_config(self):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Save self.config to the config directory

        RETURN:
        -------
        :return: None
        """
        for key, namespace in self.config.get().items():
            if isinstance(namespace, Namespace):
                namespace.save("%s/%s%s" %
                               (self.config_dir, key, DEEP_EXT_YAML))

    def clear(self, force=False):
        """
        :return:
        """
        if force:
            do_clear = True
        else:
            # Ask user if they really want to clear
            while True:
                do_clear = Notification(
                    DEEP_NOTIF_INPUT,
                    "Are you sure you want to clear this session : %s ? (yes / no)"
                    % self.config.project.session).get()
                if do_clear.lower() in ["yes", "y"]:
                    do_clear = True
                    break
                elif do_clear.lower() in ["no", "n"]:
                    do_clear = False
                    break
        if do_clear:
            Notification(
                DEEP_NOTIF_INFO,
                DEEP_MSG_BRAIN_CLEAR_ALL % self.config.project.session)
            for directory in DEEP_LOG_RESULT_DIRECTORIES:
                path = "/".join((self.config.project.session, directory))
                try:
                    for file in os.listdir(path):
                        os.remove("/".join((path, file)))
                except FileNotFoundError:
                    pass
            Notification(
                DEEP_NOTIF_SUCCESS,
                DEEP_MSG_BRAIN_CLEARED_ALL % self.config.project.session)

    def clear_history(self, force=False):
        if force:
            do_clear = True
        else:
            # Ask user if they really want to clear
            while True:
                do_clear = Notification(
                    DEEP_NOTIF_INPUT,
                    "Are you sure you want to clear history from session : %s ? (yes / no)"
                    % self.config.project.session).get()
                if do_clear.lower() in ["yes", "y"]:
                    do_clear = True
                    break
                elif do_clear.lower() in ["no", "n"]:
                    do_clear = False
                    break

        if do_clear:
            Notification(
                DEEP_NOTIF_INFO,
                DEEP_MSG_BRAIN_CLEAR_HISTORY % self.config.project.session)
            path = "/".join((self.config.project.session, "history"))
            try:
                for file in os.listdir(path):
                    os.remove("/".join((path, file)))
            except FileNotFoundError:
                pass
            Notification(
                DEEP_NOTIF_SUCCESS,
                DEEP_MSG_BRAIN_CLEARED_HISTORY % self.config.project.session)
            self.load_memory()

    def clear_logs(self, force=False):
        if force:
            do_clear = True
        else:
            # Ask user if they really want to clear
            while True:
                do_clear = Notification(
                    DEEP_NOTIF_INPUT,
                    "Are you sure you want to clear logs from session : %s ? (yes / no)"
                    % self.config.project.session).get()
                if do_clear.lower() in ["yes", "y"]:
                    do_clear = True
                    break
                elif do_clear.lower() in ["no", "n"]:
                    do_clear = False
                    break

        if do_clear:
            Notification(
                DEEP_NOTIF_INFO,
                DEEP_MSG_BRAIN_CLEAR_LOGS % self.config.project.session)
            path = "/".join((self.config.project.session, "logs"))
            try:
                for file in os.listdir(path):
                    os.remove("/".join((path, file)))
            except FileNotFoundError:
                pass
            Notification(
                DEEP_NOTIF_SUCCESS,
                DEEP_MSG_BRAIN_CLEARED_LOGS % self.config.project.session)

    def restore_config(self):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Restore the config to the last stored version

        RETURN:
        -------
        :return: None
        """
        self.config = self._config.copy()

    def store_config(self):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Saves a deep copy of the config as _config. In case the user wants to revert to previous settings

        RETURN:
        -------
        :return: None
        """
        self._config = self.config.copy()

    def load_config(self):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Loads each of the config files in the config directory into self.config
        self.check_config() is called penultimately
        self.store_config() is called finally

        RETURN:
        -------
        :return: None
        """
        Notification(DEEP_NOTIF_INFO,
                     DEEP_MSG_CONFIG_LOADING_DIR % self.config_dir)
        # If the config directory exists
        if os.path.isdir(self.config_dir):
            self.config = Namespace()
            # For each expected configuration file
            for key, file_name in DEEP_CONFIG_FILES.items():
                config_path = "%s/%s" % (self.config_dir, file_name)
                if os.path.isfile(config_path):
                    # Notification(DEEP_NOTIF_INFO, DEEP_MSG_CONFIG_LOADING_FILE % config_path)
                    self.config.add({key: Namespace(config_path)})
                    self.check_config(key=key)
                else:
                    self.config = Namespace()
                    Notification(DEEP_NOTIF_FATAL,
                                 DEEP_MSG_FILE_NOT_FOUND % config_path)
            Notification(DEEP_NOTIF_SUCCESS, DEEP_MSG_CONFIG_COMPLETE)
        else:
            Notification(DEEP_NOTIF_ERROR,
                         DEEP_MSG_DIR_NOT_FOUND % self.config_dir)
        self.store_config()

    def check_config(self, key=None):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Checks self.config by auto-completing missing parameters with default values and converting values to the
        required data type.
        Once check_config has been run, it can be assumed that self.config is complete.
        See self.__check_config for more details.

        RETURN:
        -------
        :return: None
        """
        if key is None:
            self.__check_config()
            Notification(DEEP_NOTIF_SUCCESS, DEEP_MSG_CONFIG_COMPLETE)
        else:
            self.__check_config(DEEP_CONFIG[key], sub_space=key)

    def __clear_logs(self, force=False):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Deletes logs that are not to be kept, as decided in the config settings

        PARAMETERS:
        -----------
        :param force: bool Use if you want to force delete all logs

        RETURN:
        -------
        :return: None
        """
        for log_type, (directory, ext) in DEEP_LOGS.items():
            # If forced or log should not be kept, delete the log
            if force or not self.config.project.logs.get(log_type):
                Logs(log_type, directory, ext).delete()

    def __close_logs(self, force=False):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Closes logs that are to be kept and deletes logs that are to be deleted, as decided in the config settings

        PARAMETERS:
        -----------
        :param force: bool: Use if you want to force all logs to close (use if you don't want logs to be deleted)

        RETURN:
        -------
        :return: None
        """
        for log_type, (directory, ext) in DEEP_LOGS.items():
            # If forced to closer or log should be kept, close the log
            # NB: config does not have to exist if force is True
            if log_type == DEEP_LOG_NOTIFICATION:
                try:
                    new_directory = "/".join(
                        (get_main_path(), self.config.project.session, "logs"))
                except AttributeError:
                    new_directory = None
            else:
                new_directory = None
            if force or self.config.project.logs.get(log_type):
                if os.path.isfile("%s/%s%s" % (directory, log_type, ext)):
                    Logs(log_type, directory, ext).close(new_directory)
            else:
                Logs(log_type, directory, ext).delete()

    def ui(self):
        """
        AUTHORS:
        --------

        :author: Alix Leroy

        DESCRIPTION:
        ------------

        Start the User Interface

        PARAMETERS:
        -----------

        None

        RETURN:
        -------

        :return: None
        """

        if self.visual_cortex is None:
            self.visual_cortex = VisualCortex()
        else:
            Notification(DEEP_NOTIF_ERROR,
                         "The Visual Cortex is already running.")

    def stop_ui(self):
        """
        AUTHORS:
        --------

        :author: Alix Leroy

        DESCRIPTION:
        ------------

        Stop the User Interface

        PARAMETERS:
        -----------

        None

        RETURN:
        -------

        :return: None
        """

        if self.visual_cortex is not None:
            self.visual_cortex.stop()
            self.visual_cortex = None
        else:
            Notification(DEEP_NOTIF_ERROR,
                         "The Visual Cortex is already asleep.")

    def plot_history(self, *args, **kwargs):
        """
        :param args:
        :param kwargs:
        :return:
        """
        plot_history("/".join((self.config.project.session, "history")), *args,
                     **kwargs)

    def plot_graph(self, format="svg"):
        Graph(self.model, self.model.named_parameters())

    """
    "
    " Private Methods
    "
    """

    def __check_config(self, dictionary=DEEP_CONFIG, sub_space=None):
        """
        AUTHORS:
        --------

        :author: Samuel Westlake

        DESCRIPTION:
        ------------

        Checks the config for missing or invalid values and corrects where necessary.

        PARAMETERS:
        -----------

        :param dictionary: dictionary to check the config against.
        :param sub_space: list of strings: for internal use

        RETURN:
        -------

        :return: None
        """
        sub_space = [] if sub_space is None else sub_space
        sub_space = sub_space if isinstance(sub_space, list) else [sub_space]
        for name, value in dictionary.items():
            if isinstance(value, dict):
                keys = list(self.config.get(
                    sub_space)) if name is DEEP_CONFIG_WILDCARD else [name]
                for key in keys:
                    if DEEP_CONFIG_DTYPE in value and DEEP_CONFIG_DEFAULT in value:
                        if self.config.check(key, sub_space=sub_space):
                            cond_1 = value[DEEP_CONFIG_DTYPE] is None
                            cond_2 = self.config.get(
                                sub_space)[key] == value[DEEP_CONFIG_DEFAULT]
                            # If dtype is none or the item is already the default value, continue
                            if cond_1 or cond_2:
                                continue
                            else:
                                self.config.get(
                                    sub_space)[key] = self.__convert(
                                        self.config.get(sub_space)[key],
                                        value[DEEP_CONFIG_DTYPE],
                                        value[DEEP_CONFIG_DEFAULT],
                                        sub_space=sub_space + [key])
                        else:
                            self.__add_to_config(key,
                                                 value[DEEP_CONFIG_DEFAULT],
                                                 sub_space)
                    else:
                        self.__check_config(dictionary=value,
                                            sub_space=sub_space + [key])

    def __add_to_config(self, key, default, sub_space):
        """
        Used by self.__config() to add a (default) value to the config
        :param key:
        :param default:
        :param sub_space:
        :return:
        """
        item_path = DEEP_CONFIG_DIVIDER.join(sub_space + [key])
        default_name = {} if isinstance(default, Namespace) else default
        Notification(DEEP_NOTIF_WARNING,
                     DEEP_MSG_CONFIG_NOT_FOUND % (item_path, default_name))
        # self.config.get(sub_space)[key] = default
        self.config.add({key: default}, sub_space=sub_space)

    def __convert(self, value, d_type, default, sub_space=None):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Converts a given value to a given data type, and returns the result.
        If the value can not be converted, the given default value is returned.
        NB: If d_type is given in a list, e.g. [str], it is assume that value should be a list and each item in value
        will be converted to the given data type. If any item in the list cannot be converted, None will be returned.

        PARAMETERS:
        -----------
        :param value: the value to be converted
        :param d_type: the data type to convert the value to
        :param default: the default value to return if the current value
        :param sub_space: the list of strings that defines the current sub-space

        RETURN:
        -------
        :return: new_value
        """
        sub_space = [] if sub_space is None else sub_space
        sub_space = DEEP_CONFIG_DIVIDER.join(sub_space)

        # If the value is not set
        if value is None and default is not None:
            # Notify user that default is being used
            if default == {}:
                new_value = Namespace()
            else:
                new_value = default
                if not default == []:
                    Notification(
                        DEEP_NOTIF_WARNING,
                        DEEP_MSG_CONFIG_NOT_SET % (sub_space, default))
        elif d_type is dict:
            new_value = Namespace(convert_dict(value.get_all()))
        else:
            new_value = convert(value, d_type)
            if new_value is None:
                # Notify user that default is being used
                Notification(
                    DEEP_NOTIF_WARNING, DEEP_MSG_CONFIG_NOT_CONVERTED %
                    (sub_space, value, self.__get_dtype_name(d_type), default))
        return new_value

    def __get_dtype_name(self, d_type):
        """
        :param d_type:
        :return:
        """
        if isinstance(d_type, list):
            return [self.__get_dtype_name(dt) for dt in d_type]
        elif isinstance(d_type, dict):
            return {
                key: self.__get_dtype_name(dt)
                for key, dt in d_type.items()
            }
        else:
            return d_type.__name__

    def __on_wake(self):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Executes the list of commands in self.config.project.on_wake

        RETURN:
        -------
        :return: None
        """
        if self.config.project.on_wake is not None:
            for command in self.config.project.on_wake:
                Notification(DEEP_NOTIF_INFO, command)
                self.__execute_command(command)

    def __execute_command(self, command):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Calls exec for the given command

        PARAMETERS:
        -----------
        :param command: str: the command to execute

        RETURN:
        -------
        :return: None
        """
        commands, flags = self.__preprocess_command(command)
        for command, flag in zip(commands, flags):
            if command in DEEP_EXIT_FLAGS:
                self.sleep()
            else:
                try:
                    if flag is None:
                        exec("self.%s" % command)
                    elif flag == DEEP_CMD_PRINT:
                        exec("Notification(DEEP_NOTIF_RESULT, self.%s)" %
                             command)
                except DeepError as e:
                    time.sleep(0.1)
                except KeyboardInterrupt:
                    self.sleep()

    def __preprocess_command(self, command):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Pre-processes a given command for execuution.

        PARAMETERS:
        -----------
        :param command: str: the command to process.

        RETURN:
        -------
        :return: str: the command, str: any flags associated with the command.
        """
        commands = command.split(" & ")
        for command in commands:
            remove = False
            for prefix in DEEP_FILTER_STARTS_WITH:
                if command.startswith(prefix):
                    remove = True
                    break
            if not remove:
                for suffix in DEEP_FILTER_ENDS_WITH:
                    if command.endswith(suffix):
                        remove = True
            if not remove:
                for item in DEEP_FILTER:
                    if command == item:
                        remove = True
            if not remove:
                for item in DEEP_FILTER_STARTS_ENDS_WITH:
                    if command.startswith(item) and command.endswith(item):
                        remove = True
            if not remove:
                for item in DEEP_FILTER_INCLUDES:
                    if item in command:
                        remove = True
            if remove:
                commands.remove(command)
                self.__illegal_command_messages(command)
        return self.__get_command_flags(commands)

    @staticmethod
    def __illegal_command_messages(command):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Explains why a given command is illegal.

        PARAMETERS:
        -----------
        :param command: str: the illegal command.

        RETURN:
        -------
        :return: None
        """
        message = (DEEP_MSG_ILLEGAL_COMMAND % command)
        if "__" in command or "._" in command or command.startswith("_"):
            message = "%s %s" % (message, DEEP_MSG_PRIVATE)
        if command == "wake()":
            message = "%s %s" % (message, DEEP_MSG_ALREADY_AWAKE)
        if command == "config.save":
            message = "%s %s" % (message, DEEP_MSG_USE_CONFIG_SAVE)
        Notification(DEEP_NOTIF_WARNING, message)

    @staticmethod
    def __get_command_flags(commands):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Extracts any flags from each command in a list of commands.

        PARAMETERS:
        -----------
        :param commands: list of str: the commands to extract flags from.

        RETURN:
        -------
        :return: None
        """
        flags = [None for _ in range(len(commands))]
        for i, command in enumerate(commands):
            for flag in DEEP_CMD_FLAGS:
                if flag in command:
                    flags[i] = flag
                    commands[i] = command.replace(" %s" % flag, "")
                else:
                    flags[i] = None
        return commands, flags

    @staticmethod
    def __good_bye():
        """
        AUTHORS:
        --------

        :author: Alix Leroy

        DESCRIPTION:
        ------------

        Display thanks message

        PARAMETERS:
        -----------

        None

        RETURN:
        -------

        :return: Universal Love <3
        """
        Notification(DEEP_NOTIF_LOVE, "=================================")
        Notification(DEEP_NOTIF_LOVE, "Thank you for using Deeplodocus !")
        Notification(DEEP_NOTIF_LOVE, "== Made by Humans with deep <3 ==")
        Notification(DEEP_NOTIF_LOVE, "=================================")

    """
    "
    " Aliases
    "
    """

    visual_cortex = ui
    vc = ui
    user_interface = ui
예제 #3
0
class Brain(FrontalLobe):
    """
    AUTHORS:
    --------

    :author: Alix Leroy
    :author: Samuel Westlake

    DESCRIPTION:
    ------------

    A Brain class that manages the commands of the user and allows to start the training
    PUBLIC METHODS:
    ---------------
    :method wake:
    :method sleep:
    :method save_config: Save all configuration files into self.config_dir.
    :method clear_config: Reset self.config to an empty Namespace.
    :method store_config: Store a copy of self.config in self._config.
    :method restore_config: Set self.config to a copy of self._config.
    :method load_config: Load self.config from self.config_dir.
    :method check_config: Check self.config for missing parameters and set data types accordingly.
    :method clear_logs: Deletes logs that are not to be kept, as decided in the config settings.
    :method close_logs: Closes logs that are to be kept and deletes logs that are to be deleted, see config settings.
    :method ui:

    PRIVATE METHODS:
    ----------------
    :method __init__:
    :method __check_config:
    :method __convert_dtype:
    :method __convert:
    :method __on_wake:
    :method __execute_command:
    :method __preprocess_command:
    :method __illegal_command_messages:
    :method __get_command_flags:
    """

    def __init__(self, config_dir):
        """
        AUTHORS:
        --------

        :author: Alix Leroy

        DESCRIPTION:
        ------------

        Initialize a Deeplodocus Brain

        PARAMETERS:
        -----------

        :param config_path->str: The config path

        RETURN:
        -------

        :return: None
        """
        FrontalLobe.__init__(self)  # Model Manager
        self.close_logs(force=True)
        self.config_dir = config_dir
        self.config_is_complete = False
        Logo(version=__version__)
        self.visual_cortex = None
        time.sleep(0.5)                     # Wait for the UI to respond
        self.frontal_lobe = None
        self.config = None
        self._config = None
        self.load_config()
        Thalamus()                  # Initialize the Thalamus

    def wake(self):
        """
        Authors : Alix Leroy, SW
        Deeplodocus terminal commands
        :return: None
        """
        self.__on_wake()

        while True:
            command = Notification(DEEP_NOTIF_INPUT, DEEP_MSG_INSTRUCTRION).get()
            try:
                self.__execute_command(command)
            except DeepError:
                time.sleep(0.5)

    def sleep(self):
        """
        Author: SW, Alix Leroy
        Stop the interface, close logs and print good-bye message
        :return: None
        """
        # Stop the visual cortex
        if self.visual_cortex is not None:
            self.visual_cortex.stop()

        self.close_logs()
        End(error=False)

    def save_config(self):
        """
        Author: SW
        Save the config to the config folder
        :return: None
        """
        for key, namespace in self.config.get().items():
            if isinstance(namespace, Namespace):
                namespace.save("%s/%s%s" % (self.config_dir, key, DEEP_EXT_YAML))

    def clear_config(self):
        """
        Author: SW
        Reset the config to an empty Namespace
        :return: None
        """
        self.config = Namespace()

    def restore_config(self):
        """
        Author: SW
        Restore the config to the last stored version
        :return:
        """
        self.config = self._config.copy()

    def store_config(self):
        """
        Author: SW
        Saves a deep copy of the config as _config. In case the user wants to revert to previous settings
        :return: None
        """
        self._config = self.config.copy()

    def load_config(self):
        """
        Author: SW
        Function: Checks current config path is valid, if not, user is prompted to give another
        :return: bool: True if a valid config path is set, otherwise, False
        """
        Notification(DEEP_NOTIF_INFO, DEEP_MSG_LOAD_CONFIG_START % self.config_dir)
        # If the config directory exists
        if os.path.isdir(self.config_dir):
            self.clear_config()
            # For each expected configuration file
            for key, file_name in DEEP_CONFIG_FILES.items():
                config_path = "%s/%s" % (self.config_dir, file_name)
                if os.path.isfile(config_path):
                    self.config.add({key: Namespace(config_path)})
                    Notification(DEEP_NOTIF_SUCCESS, DEEP_MSG_LOAD_CONFIG_FILE % config_path)
                else:
                    Notification(DEEP_NOTIF_ERROR, DEEP_MSG_FILE_NOT_FOUND % config_path)
            self.store_config()
        else:
            Notification(DEEP_NOTIF_ERROR, DEEP_MSG_DIR_NOT_FOUND % self.config_dir)
        #self.check_config()
        self.config.summary()

    def check_config(self):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION SHORT:
        ------------
        Checks self.config by auto-completing missing parameters with default values and converting values to the
        required data type.
        Once check_config has been run, it can be assumed that self.config is complete.
        See self.__check_config for more details.

        RETURN:
        -------
        :return: None
        """
        self.__check_config()
        Notification(DEEP_NOTIF_SUCCESS, DEEP_MSG_CONFIG_COMPLETE)

    def clear_logs(self, force=False):
        """
        Author: SW
        Deletes logs that are not to be kept, as decided in the config settings
        :param force: bool Use if you want to force delete all logs
        :return: None
        """
        for log_type, (directory, ext) in DEEP_LOGS.items():
            # If forced or log should not be kept, delete the log
            if force or not self.config.project.logs.get(log_type):
                Logs(log_type, directory, ext).delete()

    def close_logs(self, force=False):
        """
        Author: SW
        Closes logs that are to be kept and deletes logs that are to be deleted, as decided in the config settings
        :param force: bool: Use if you want to force all logs to close (use if you don't want logs to be deleted)
        :return: None
        """
        for log_type, (directory, ext) in DEEP_LOGS.items():
            # If forced to closer or log should be kept, close the log
            # NB: config does not have to exist if force is True
            if force or self.config.project.logs.get(log_type):
                if os.path.isfile("%s/%s%s" % (directory, log_type, ext)):
                    Logs(log_type, directory, ext).close()
            else:
                Logs(log_type, directory, ext).delete()

    def ui(self):
        """
        AUTHORS:
        --------AttributeError: module 'asyncio' has no attribute 'create_task'


        :author: Alix Leroy

        DESCRIPTION:
        ------------

        Start the User Interface

        PARAMETERS:AttributeError: module 'asyncio' has no attribute 'create_tasAttributeError: module 'asyncio' has no attribute 'create_task'
k'

        -----------

        None

        RETURN:
        -------

        :return: None
        """

        if self.visual_cortex is None:
            self.visual_cortex = VisualCortex()
        else:
            Notification(DEEP_NOTIF_ERROR, "The Visual Cortex is already running.")

    def stop_ui(self):
        """
        AUTHORS:
        --------

        :author: Alix Leroy

        DESCRIPTION:
        ------------

        Stop the User Interface

        PARAMETERS:
        -----------

        None

        RETURN:
        -------

        :return: None
        """

        if self.visual_cortex is not None:
            self.visual_cortex.stop()
            self.visual_cortex = None
        else:
            Notification(DEEP_NOTIF_ERROR, "The Visual Cortex is already asleep.")

    def __check_config(self, dictionary=DEEP_CONFIG, sub_space=None):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        If a parameter is missing, the user is notified by a DEEP_NOTIF_WARNING with DEEP_MSG_CONFIG_NOT_FOUND.
        The missing parameter is added with the default value specified by DEEP_CONFIG and user is notified of the
        addition of the parameter by a DEEP_NOTIF_WARNING with DEE_MSG_CONFIG_ADDED.
        If a parameter is found successfully, it is converted to the data type specified by 'dtype' in DEEP_CONFIG.
        If a parameter cannot be converted to the required data type, it is replaced with the default from DEEP_CONFIG.

        PARAMETERS:
        -----------
        NB: Both parameters are only for use when check_config calls itself.
        :param dictionary: dictionary to compare self.config with.
        :param sub_space: list of strings defining the path to the current sub-space.

        RETURN:
        -------
        :return: None
        """
        sub_space = [] if sub_space is None else sub_space
        sub_space = sub_space if isinstance(sub_space, list) else [sub_space]
        for key, value in dictionary.items():
            if isinstance(value, dict):
                if "dtype" in value and "default" in value:
                    default = value["default"]
                    if self.config.check(key, sub_space=sub_space):
                        d_type = value["dtype"]
                        current = self.config.get(sub_space)[key]
                        self.config.get(sub_space)[key] = self.__convert_dtype(current,
                                                                               d_type,
                                                                               default,
                                                                               sub_space=sub_space + [key])
                    else:
                        item_path = DEEP_CONFIG_DIVIDER.join(sub_space + [key])
                        Notification(DEEP_NOTIF_WARNING, DEEP_MSG_CONFIG_ADDED % (item_path, default))
                        self.config.get(sub_space)[key] = default
                else:
                    self.__check_config(value, sub_space=sub_space + [key])

    def __convert_dtype(self, value, d_type, default, sub_space=None):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Converts a given value to a given data type, and returns the result.
        If the value can not be converted, the given default value is returned.
        NB: If d_type is given in a list, e.g. [str], it is assume that value should be a list and each item in value
        will be converted to the given data type. If any item in the list cannot be converted, None will be returned.

        PARAMETERS:
        -----------
        :param value: the value to be converted
        :param d_type: the data type to convert the value to
        :param default: the default value to return if the current value
        :param sub_space: the list of strings that defines the current sub-space

        RETURN:
        -------
        :return: new_value
        """
        sub_space = [] if sub_space is None else sub_space
        if value is None:
            return None
        else:
            try:
                len(d_type)
                d_type = d_type[0]
                is_list = True
            except TypeError:
                is_list = False
            if is_list:
                new_values = []
                value = value if isinstance(value, list) else [value]
                for item in value:
                    new_item = self.__convert(item, d_type)
                    if new_item is not None:
                        new_values.append(new_item)
                    else:
                        Notification(DEEP_NOTIF_WARNING,
                                     DEEP_MSG_NOT_CONVERTED
                                     % (DEEP_CONFIG_DIVIDER.join(sub_space), new_values, d_type, default))
                        return default
                return new_values
            else:
                if d_type == dict:
                    if isinstance(value, Namespace) or value is None:
                        return value
                    else:
                        Notification(DEEP_NOTIF_WARNING, DEEP_MSG_NOT_CONVERTED % (sub_space, value, d_type, default))
                        return Namespace(default)
                else:
                    new_value = self.__convert(value, d_type)
                    if new_value is None:
                        Notification(DEEP_NOTIF_WARNING, DEEP_MSG_NOT_CONVERTED % (sub_space, value, d_type, default))
                        return default
                    else:
                        return new_value

    @staticmethod
    def __convert(value, d_type):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Converts a given value to a given data type, and returns the result.
        If the value can not be converted, None is returned.

        PARAMETERS:
        -----------
        :param value: the value to be converted.
        :param d_type: the data type to convert the value to.

        RETURN:
        -------
        :return: new_value
        """
        if isinstance(dtype, int) or isinstance(dtype, float):
            try:
                return d_type(eval(value))
            except NameError:
                try:
                    return d_type(value)
                except TypeError:
                    return None
        else:
            try:
                return d_type(value)
            except TypeError:
                return None

    def __on_wake(self):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Executes the list of commands in self.config.project.on_wake

        RETURN:
        -------
        :return: None
        """
        if self.config.project.on_wake is not None:
            if not isinstance(self.config.project.on_wake, list):
                self.config.project.on_wake = [self.config.project.on_wake]
            for command in self.config.project.on_wake:
                self.__execute_command(command)

    def __execute_command(self, command):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Calls exec for the given command

        PARAMETERS:
        -----------
        :param command: str: the command to execute

        RETURN:
        -------
        :return: None
        """
        commands, flags = self.__preprocess_command(command)
        for command, flag in zip(commands, flags):
            if command in DEEP_EXIT_FLAGS:
                self.sleep()
            else:
                try:
                    if flag is None:
                        exec("self.%s" % command)
                    elif flag == DEEP_CMD_PRINT:
                        exec("Notification(DEEP_NOTIF_RESULT, self.%s)" % command)
                except DeepError:
                    time.sleep(0.1)

    def __preprocess_command(self, command):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Pre-processes a given command for execuution.

        PARAMETERS:
        -----------
        :param command: str: the command to process.

        RETURN:
        -------
        :return: str: the command, str: any flags associated with the command.
        """
        commands = command.split(" & ")
        for command in commands:
            remove = False
            for prefix in DEEP_FILTER_STARTS_WITH:
                if command.startswith(prefix):
                    remove = True
                    break
            if not remove:
                for suffix in DEEP_FILTER_ENDS_WITH:
                    if command.endswith(suffix):
                        remove = True
            if not remove:
                for item in DEEP_FILTER:
                    if command == item:
                        remove = True
            if not remove:
                for item in DEEP_FILTER_STARTS_ENDS_WITH:
                    if command.startswith(item) and command.endswith(item):
                        remove = True
            if not remove:
                for item in DEEP_FILTER_INCLUDES:
                    if item in command:
                        remove = True
            if remove:
                commands.remove(command)
                self.__illegal_command_messages(command)
        return self.__get_command_flags(commands)

    @staticmethod
    def __illegal_command_messages(command):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Explains why a given command is illegal.

        PARAMETERS:
        -----------
        :param command: str: the illegal command.

        RETURN:
        -------
        :return: None
        """
        message = (DEEP_MSG_ILLEGAL_COMMAND % command)
        if "__" in command or "._" in command or command.startswith("_"):
            message = "%s %s" % (message, DEEP_MSG_PRIVATE)
        if command == "wake()":
            message = "%s %s" % (message, DEEP_MSG_ALREADY_AWAKE)
        if command == "config.save":
            message = "%s %s" % (message, DEEP_MSG_USE_CONFIG_SAVE)
        Notification(DEEP_NOTIF_WARNING, message)

    @staticmethod
    def __get_command_flags(commands):
        """
        AUTHORS:
        --------
        :author: Samuel Westlake

        DESCRIPTION:
        ------------
        Extracts any flags from each command in a list of commands.

        PARAMETERS:
        -----------
        :param commands: list of str: the commands to extract flags from.

        RETURN:
        -------
        :return: None
        """
        flags = [None for _ in range(len(commands))]
        for i, command in enumerate(commands):
            for flag in DEEP_CMD_FLAGS:
                if flag in command:
                    flags[i] = flag
                    commands[i] = command.replace(" %s" % flag, "")
                else:
                    flags[i] = None
        return commands, flags

    #
    # ALIASES
    #

    visual_cortex = ui
    vc = ui
    user_interface = ui