From e683a90b34e84ef545e58144e98017dea35d8d2d Mon Sep 17 00:00:00 2001 From: redxef Date: Thu, 13 Oct 2022 00:16:05 +0200 Subject: [PATCH] Improve logging and config loading. --- adapters/utils.py | 4 ++ adapters/wordpress.py | 3 +- main.py | 127 +++++++++++++++++++++++++++++++++++------- 3 files changed, 114 insertions(+), 20 deletions(-) diff --git a/adapters/utils.py b/adapters/utils.py index f7e5000..05a5669 100644 --- a/adapters/utils.py +++ b/adapters/utils.py @@ -1,6 +1,10 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +import logging + +logger = logging.getLogger(f"wp_cal.{__name__}") + def dict_merge(d0, d1): for k, v in d1.items(): if (k in d0 and isinstance(d0[k], dict) and isinstance(d1[k], dict)): diff --git a/adapters/wordpress.py b/adapters/wordpress.py index dac69e8..d670cc6 100644 --- a/adapters/wordpress.py +++ b/adapters/wordpress.py @@ -5,10 +5,11 @@ import requests import urllib.parse import json import datetime +import logging from . import utils - +logger = logging.getLogger(f"wp_cal.{__name__}") class CalendarMetadata(): diff --git a/main.py b/main.py index fe598b6..ba7d260 100755 --- a/main.py +++ b/main.py @@ -1,17 +1,72 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +import os import sys import yaml +import logging try: - from yaml import CLoader as Loader + from yaml import CLoader as Loader, CDumper as Dumper except ImportError: - from yaml import Loader + from yaml import Loader, Dumper + +import click from adapters import * -class Config(): +logger = logging.getLogger(f'wp_cal.{__name__}') +class BaseConfig(dict): + + def __init__(self, file, defaults, *args, **kwargs): + self._ex = None + self.file = file + self.defaults = defaults + super().__init__(*args, **kwargs) + try: + self._load() + except FileNotFoundError as ex: + self._ex = ex + + def __repr__(self): + return super().__repr__() + + def __str__(self): + return super().__str__() + + def _load(self): + try: + if self.file == '-': + config = yaml.load(sys.stdin, Loader=Loader) + else: + with open(self.file) as fp: + config = yaml.load(fp, Loader=Loader) + if config is None: + config = {} + for k, v in config.items(): + self[k] = v + finally: + for keylist, value in self.defaults: + d = self + for i, key in enumerate(keylist): + repl = value if (i == len(keylist) - 1) else {} + d[key] = d.get(key, repl) + d = d[key] + + def _save(self): + with open(self.file, 'w') as fp: + yaml.dump(self, fp, Dumper=Dumper) + + def exception(self): + return self._ex + + def load(self): + return self._load() + + def save(self): + return self._save() + +class Config(BaseConfig): """ The default configuration. @@ -26,31 +81,60 @@ class Config(): .wordpress.calendar.translations: a dictionary of language <-> translation pairs (example: {"en": "Reservations"}) .wordpress.credentials.user: the user as which to log into wordpress .wordpress.credentials.password: the users password + .logging.level: one or more log levels of the form : seperated by a `,` (comma) """ - def __init__(self, file): - self.file = file - self.config: dict | None = None + def __init__(self, file, defaults, *args, **kwargs): + defaults += [ + ('google.calendar_id', '#TODO insert google calendar id'), + ('google.credentials', {}), + ('google.token_file', os.path.join(os.environ['HOME'], '.wp-cal-google-token')), + ('wordpress.url', '#TODO insert url to wordpress site'), + ('wordpress.calendar.id', '#TODO insert wp-booking-system calendar id'), + ('wordpress.calendar.name', '#TODO insert calendar name'), + ('wordpress.calendar.translations', {'en': '#TODO insert english translation'}), + ('wordpress.credentials.user', '#TODO insert wordpress username'), + ('wordpress.credentials.password', '#TODO insert wordpress password'), + ] + defaults = [(x[0].split('.'), x[1]) for x in defaults] + super().__init__(file, defaults, *args, **kwargs) - def load(self): - if self.file == '-': - config = yaml.load(sys.stdin, Loader=Loader) +def init_logging(level: str): + allowed_values = {'NOTSET', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'} + + log_levels = [x.split(':') for x in level.split(',')] + for module, level in log_levels: + module = module.strip() if module else None + level = level.strip().upper() + if level not in allowed_values: + raise ValueError(f'invalid log level, allowed values: {repr(allowed_values)}') + level = getattr(logging, level) + logging.getLogger(module).setLevel(level) + +@click.command() +@click.option('--level', '-l', envvar='WP_CAL_LEVEL', default=':WARNING,wp_cal:INFO', help='The log level for the application') +@click.option('--config', '-c', envvar='WP_CAL_CONFIG', default='-', help='The configuration file') +def main(level, config): + logging.basicConfig() + init_logging(level) + + config = Config(config, [('logging.level', ':WARNING,wp_cal:INFO')]) + if config.exception(): + logger.info('config not found, trying to generate template') + try: + config.save() + except Exception: + logger.exception('failed to generate template') else: - with open(self.file) as fp: - config = yaml.load(fp, Loader=Loader) - self.config = config + logger.info('generated config at "%s"', config.file) + return - def __getitem__(self, name): - assert self.config is not None - return self.config[name] + init_logging(config['logging']['level']) -if __name__ == '__main__': - config = Config('-') - config.load() g = Google( config['google']['calendar_id'], credentials=config['google']['credentials'], - token_file=config['google'].get('token_file', '~/.wp-cal-integration-google-token.json') + token_file=config['google']['token_file'], ) w = Wordpress( config['wordpress']['url'], @@ -63,5 +147,10 @@ if __name__ == '__main__': ) g.login() events = g.get_events() + logger.info("syncing %d events", len(events)) w.login() w.post_events(events) + logger.info("done") + +if __name__ == '__main__': + main()