Program Listing for File core.py

Return to documentation for file (upref/core.py)

#!/usr/bin/env python
# -*- coding: utf-8 -*-




import logging
import sys
import os
import os.path
import collections
import tempfile
from copy import deepcopy
import yaml
import appdirs


__DEFAULT_CONF_FILENAME__ = "default.conf"


__EXT_FILENAME__ = ".conf"


__UPREF_FOLDER__ = ".upref"



def load_conf(filename):
    logging.debug('Load the configuration file: %s', filename)
    result = {}

    filename = os.path.abspath(filename)

    if not os.path.isfile(filename):
        logging.info('The configuration file does not exists.')
        logging.info('The configuration filename is %s', filename)
        return result

    with open(filename, 'r') as ymlfile:        result = yaml.load(ymlfile, Loader=yaml.FullLoader)

    return result


def save_conf(conf, filename):
    logging.debug('Save the configuration file: %s', filename)
    filename = os.path.abspath(filename)

    if os.path.isfile(filename):
        logging.info('The configuration file already exists.')

    folder = os.path.split(filename)[0]
    if not os.path.isdir(folder):
        os.makedirs(folder, exist_ok=True)

    with open(filename, 'w') as outfile:
        yaml.dump(conf, outfile, default_flow_style=False)

    return conf



def upref_filename(name):
    upref_folder = appdirs.user_data_dir(__UPREF_FOLDER__, roaming=True)
    filename = os.path.join(upref_folder, name + __EXT_FILENAME__)

    return filename



def default_conf():
    logging.debug('Load the default configuration.')
    filename = os.path.join(__get_this_folder(), __DEFAULT_CONF_FILENAME__)
    return load_conf(filename)



def current_upref(name):
    logging.debug('Load the current preference for %s.', name)
    return load_conf(upref_filename(name))



def load_data(name, default_data=None):
    logging.debug('Load the current data for %s.', name)
    result = load_conf(upref_filename(name))
    if default_data is not None:
        result = dict_merge(default_data, result)
    return result



def save_data(data, name):
    logging.debug('Save the data for %s.', name)
    return save_conf(data, upref_filename(name))



def dict_merge(dct, merge_dct, add_keys=True):
    dct = deepcopy(dct)

    if not add_keys:
        merge_dct = {
            k: merge_dct[k]
            for k in set(dct).intersection(set(merge_dct))
        }

    for k, value in merge_dct.items():
        if isinstance(dct.get(k), dict) \
           and isinstance(value, collections.Mapping):
            dct[k] = dict_merge(dct[k], value, add_keys=add_keys)
        else:
            dct[k] = value
    return dct



def get_pref(data_description, name, interface="gui", force_renew=False,
             mandatory=True):
    # if not mandatory, return the data without asking
    if not mandatory:
        return conv_description_to_raw(current_upref(name))

    # And finally merged with the data from the user preference
    current_data = dict_merge(data_description, current_upref(name))
    default = default_conf()

    if (__package__ in [None, '']) and ('.' not in __name__):
        import gui
        import tty
    else:
        from . import gui
        from . import tty

    interact_ = gui if (interface == "gui") else tty

    not_all_values_are_set = not all_values_are_set(current_data)
    while not_all_values_are_set or force_renew:
        force_renew = False
        completed_data = dict_merge(default, current_data)
        interact_.get_data(completed_data)

        for key in completed_data:
            if not key.endswith("__") and not key.startswith("__"):
                value = completed_data[key].get('value')
                if value is None or len(value) == 0:
                    current_data[key]['value'] = value

        save_conf(current_data, upref_filename(name))
        not_all_values_are_set = not all_values_are_set(current_data)
        if not_all_values_are_set:
            interact_.message(completed_data['__gui__']['not_complete_msg'],
                              completed_data['__gui__']['not_complete_title'])

    return conv_description_to_raw(current_data)


def all_values_are_set(data_description):
    result = True
    for key in data_description:
        if not key.endswith("__") and not key.startswith("__"):
            value = data_description[key].get('value')
            if value is None or len(value) == 0:
                result = False

    return result


def conv_raw_to_description(data):
    result = {}
    for key, value in data.items():
        if isinstance(value, dict):
            result[key] = value
        else:
            result[key] = {'value': value}
    return result


def conv_description_to_raw(data_description):
    result = {}
    for data in data_description:
        if not data.endswith("__") and not data.startswith("__"):
            if 'value' in data_description[data]:
                result[data] = data_description[data]['value']
            else:
                logging.error("No value in the pref %s", data)

    return result


def set_pref(data, name, data_description=None):
    # read the current description if there is none
    if data_description is None:
        data_description = current_upref(name)

    data = dict_merge(data_description, conv_raw_to_description(data))
    save_conf(data, upref_filename(name))



def remove_pref(name):
    filename = upref_filename(name)
    if os.path.isfile(filename):
        logging.debug('Remove the configuration file: %s', filename)
        os.remove(filename)



def is_frozen():
    return getattr(sys, 'frozen', False)


def __get_this_folder():
    return os.path.split(os.path.abspath(os.path.realpath(
        __get_this_filename())))[0]


def __get_this_filename():
    result = ""

    if is_frozen():
        # frozen
        result = sys.executable
    else:
        # unfrozen
        result = __file__

    return result



def __set_logging_system():
    log_filename = os.path.splitext(os.path.abspath(
        os.path.realpath(__get_this_filename())))[0] + '.log'

    if is_frozen():
        log_filename = os.path.abspath(os.path.join(
            tempfile.gettempdir(),
            os.path.basename(__get_this_filename()) + '.log'))

    logging.basicConfig(filename=log_filename, level=logging.DEBUG,
                        format='%(asctime)s: %(message)s',
                        datefmt='%m/%d/%Y %I:%M:%S %p')
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    # set a format which is simpler for console use
    formatter = logging.Formatter('%(asctime)s: %(levelname)-8s %(message)s')
    # tell the handler to use this format
    console.setFormatter(formatter)
    # add the handler to the root logger
    logging.getLogger('').addHandler(console)



def __main():
    # ------------------------------------
    logging.info('Started %s', __get_this_filename())
    logging.info('The Python version is %s.%s.%s',
                 sys.version_info[0], sys.version_info[1], sys.version_info[2])

    ex1_filename = os.path.join(__get_this_folder(), "..", "examples",
                                "example01.conf")
    print("Filename = %s" % ex1_filename)
    ex_conf = load_conf(ex1_filename)
    print("conf loaded = %s" % ex_conf)
    data_ex1 = get_pref(ex_conf, "core_example01")
    print("url=%s" % data_ex1['url'])
    remove_pref("core_example01")
    data_ex1 = get_pref(ex_conf, "core_example01")
    print("Ex1: url=%s" % data_ex1['url'])

    logging.info('Finished')
    # ------------------------------------



if __name__ == '__main__':
    __set_logging_system()
    __main()