import logging import os import sys import colorama import yaml import xdg.BaseDirectory from . import __version__ from .exception import JrnlError from .color import ERROR_COLOR from .color import RESET_COLOR from .output import list_journals # Constants DEFAULT_CONFIG_NAME = "jrnl.yaml" XDG_RESOURCE = "jrnl" DEFAULT_JOURNAL_NAME = "journal.txt" DEFAULT_JOURNAL_KEY = "default" def save_config(config): config["version"] = __version__ with open(get_config_path(), "w") as f: yaml.safe_dump( config, f, encoding="utf-8", allow_unicode=True, default_flow_style=False ) def get_config_path(): try: config_directory_path = xdg.BaseDirectory.save_config_path(XDG_RESOURCE) except FileExistsError: raise JrnlError( "ConfigDirectoryIsFile", config_directory_path=os.path.join( xdg.BaseDirectory.xdg_config_home, XDG_RESOURCE ), ) return os.path.join( config_directory_path or os.path.expanduser("~"), DEFAULT_CONFIG_NAME ) def get_default_config(): return { "version": __version__, "journals": {"default": get_default_journal_path()}, "editor": os.getenv("VISUAL") or os.getenv("EDITOR") or "", "encrypt": False, "template": False, "default_hour": 9, "default_minute": 0, "timeformat": "%Y-%m-%d %H:%M", "tagsymbols": "@", "highlight": True, "linewrap": 79, "indent_character": "|", "colors": { "date": "none", "title": "none", "body": "none", "tags": "none", }, } def get_default_journal_path(): journal_data_path = xdg.BaseDirectory.save_data_path( XDG_RESOURCE ) or os.path.expanduser("~") return os.path.join(journal_data_path, DEFAULT_JOURNAL_NAME) def scope_config(config, journal_name): if journal_name not in config["journals"]: return config config = config.copy() journal_conf = config["journals"].get(journal_name) if type(journal_conf) is dict: # We can override the default config on a by-journal basis logging.debug( "Updating configuration with specific journal overrides %s", journal_conf ) config.update(journal_conf) else: # But also just give them a string to point to the journal file config["journal"] = journal_conf return config def verify_config_colors(config): """ Ensures the keys set for colors are valid colorama.Fore attributes, or "None" :return: True if all keys are set correctly, False otherwise """ all_valid_colors = True for key, color in config["colors"].items(): upper_color = color.upper() if upper_color == "NONE": continue if not getattr(colorama.Fore, upper_color, None): print( "[{2}ERROR{3}: {0} set to invalid color: {1}]".format( key, color, ERROR_COLOR, RESET_COLOR ), file=sys.stderr, ) all_valid_colors = False return all_valid_colors def load_config(config_path): """Tries to load a config file from YAML.""" with open(config_path) as f: return yaml.load(f, Loader=yaml.FullLoader) def is_config_json(config_path): with open(config_path, "r", encoding="utf-8") as f: config_file = f.read() return config_file.strip().startswith("{") def update_config(config, new_config, scope, force_local=False): """Updates a config dict with new values - either global if scope is None or config['journals'][scope] is just a string pointing to a journal file, or within the scope""" if scope and type(config["journals"][scope]) is dict: # Update to journal specific config["journals"][scope].update(new_config) elif scope and force_local: # Convert to dict config["journals"][scope] = {"journal": config["journals"][scope]} config["journals"][scope].update(new_config) else: config.update(new_config) def get_journal_name(args, config): args.journal_name = DEFAULT_JOURNAL_KEY if args.text and args.text[0] in config["journals"]: args.journal_name = args.text[0] args.text = args.text[1:] elif DEFAULT_JOURNAL_KEY not in config["journals"]: print("No default journal configured.", file=sys.stderr) print(list_journals(config), file=sys.stderr) sys.exit(1) logging.debug("Using journal name: %s", args.journal_name) return args