Newer
Older
Handbook / calendar / teachingdates / config / config.py
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 LectureStyleError, PaperError, PeriodError

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).
    """
    style = None
    period = None
    paper = None
    year = None
    config = None

    def __init__(self, args):
        self.config = args.config
        self.style = args.style
        self.period = args.period
        self.paper = args.paper
        # priority: year on command line (defaults to current year),
        # year in config
        self.year = (
            args.config["year"] if "year" in args.config.keys()
            else args.year)

        period_config = paper_config = None
        if self.style != "iso":
            if self.period not in self.config.keys():
                raise PeriodError()

            period_config = self.config[self.period]
            # raises KeyError if source key is missing
            period_config["name"] and period_config["weeks"]

            # papers are irrelevant in "calendar" style
            if self.style == "lecture":
                if "papers" in period_config.keys():
                    if self.paper in period_config["papers"].keys():
                        paper_config = period_config["papers"][self.paper]
                        # we expect at least a "lectures" entry
                        paper_config["lectures"]
                    else:
                        raise PaperError()
                else:
                    raise LectureStyleError()

    def get_period_config(self, period=None):
        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, period=None, paper=None):
        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 load_config(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 '{f}'".format(prog=PROG, f=file))
    return file, cfg