- 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).
- """
- style = None
- period = None
- paper = None
- year = None
- config = None
- config_file = None
-
- def __init__(self, args):
- self.load_config(file=args.config_file)
- 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 = (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)
-
- # 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, period=None, paper=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