import datetime import importlib.resources as pkg_resources from pathlib import Path from shutil import copyfile import yaml from teachingdates import PROG from .errors import (ConfigurationError, PaperError, PaperKeyError, PeriodError, PeriodKeyError) CONFIG_FILENAME = "config.yml" CONFIG_DIR = Path.home().joinpath(".config", PROG) CONFIG_FILE = CONFIG_DIR.joinpath(CONFIG_FILENAME) class Configuration(object): """Validate and structure the configuration for easy access. Includes methods to return the period configuration and the paper configuration, as applicable. (Yes, the period configuration includes the paper configuration, but it may also include other papers, so it's more convenient to return these separately.) The configuration file will normally contain specifications for several teaching periods and papers. We don't need all of this: usually we're only interested in one teaching period and one paper (if relevant). """ def __init__(self, args): self.load_config(file=args.config_file) self.style = args.style self.period = args.period self.paper = args.paper self.end_of_week = args.end_of_week # priority: year on command line (defaults to current year), # year in config self.year = (self.config["year"] if "year" in self.config.keys() else args.year) period_config = paper_config = None if self.style != "iso": if self.period not in self.config.keys(): raise PeriodError(str(self.config_file)) period_config = self.config[self.period] # the "name" and "weeks" keys are required for key in ["name", "weeks"]: if key not in period_config.keys(): raise PeriodKeyError(key, self.period, self.config_file) self.period_name = period_config["name"] # Papers are only relevant in "lecture" style, which by definition # must specify a paper. try: if self.style == "lecture": if ("papers" in period_config.keys() and self.paper in period_config["papers"].keys()): paper_config = period_config["papers"][self.paper] # we expect at least a "lectures" entry if (not paper_config or "lectures" not in paper_config.keys()): raise PaperKeyError("lectures", self.paper, self.config_file) else: raise PaperError(str(self.config_file)) except AttributeError as e: raise PaperError(str(self.config_file)) def load_config(self, file=None): """Load a configuration file. If no file is specified, try the default configuration in ~/.config. If that doesn't exist, copy the default configuration from teachingdates.config. """ cfg = None if not file: file = CONFIG_FILE if not CONFIG_FILE.exists(): CONFIG_DIR.mkdir(exist_ok=True) configmgr = pkg_resources.path("teachingdates.config", CONFIG_FILENAME) with configmgr as f: copyfile(f, CONFIG_FILE) print("{prog}: created new configuration file at " "'{f}'".format(prog=PROG, f=str(CONFIG_FILE))) try: with open(file) as f: cfg = yaml.safe_load(f.read()) except FileNotFoundError as e: print("{prog}: error: no such file '{file}'".format(prog=PROG, file=file)) if not cfg: raise ConfigurationError(str(file)) self.config_file = file self.config = cfg def get_period_config(self, period=None): """Get the configuration details for a teaching period. No need to specify the year as all configurations are for a single year anyway. """ period = self.period if period is None else period if period in self.config.keys(): return self.config[period] else: return None def get_paper_config(self, paper=None, period=None): """Get the configuration details for a paper. Can optionally specify the teaching period. No need to specify the year as all configurations are for a single year anyway. """ paper = self.paper if paper is None else paper period_config = self.get_period_config(period=period) if (period_config and "papers" in period_config.keys() and paper in period_config["papers"].keys()): return period_config["papers"][paper] else: return None def get_config_keys(self): return self.config.keys()