Newer
Older
Handbook / calendar / teachingdates / config / cli.py
import argparse
import datetime

from teachingdates import PROG


def oxford_comma_list(words):
    if isinstance(words, list) or isinstance(words, tuple):
        if len(words) == 0:
            return ""
        elif len(words) == 1:
            return words[0]
        elif len(words) == 2:
            return " and ".join(words)
        else:
            return ", ".join(words[:-1]) + ", and " + words[-1]
    else:
        raise TypeError("expected list, got {t}".format(t=str(type(words))))


def switch_message(switches, context, severity="warning", required=False):
    switch_string = oxford_comma_list(switches)
    return ("{prog}: {severity}: {switches} {verb} {required} for "
            "{context}".format(prog=PROG,
                               severity=severity,
                               switches=switch_string,
                               verb="are" if len(switches) > 1 else "is",
                               required="required" if required else "ignored",
                               context=context))


def parse_command_line():
    """Parse command-line arguments.

    Example command lines:
    generate_dates --style calendar --format latex --year 2020 --period S1 --paper INFO201
    generate_dates --style lecture --format xml --period S2 --paper INFO202
    """
    format_map = {
        "t": "text",
        "text": "text",
        "l": "latex",
        "latex": "latex",
        "x": "xml",
        "xml": "xml",
    }
    style_map = {
        "c": "calendar",
        "calendar": "calendar",
        "l": "lecture",
        "lecture": "lecture",
        "i": "iso",
        "iso": "iso",
    }

    parser = argparse.ArgumentParser(prog=PROG, description="")

    parser.add_argument(
        "--config",
        "-c",
        default=None,
        dest="config_file",
        help="file name of the configuration file "
        "[default '~/.config/{prog}/config.yml']".format(prog=PROG))

    parser.add_argument("--debug",
                        "-d",
                        action='store_true',
                        help="enable debugging output")

    parser.add_argument(
        "--end-of-week",
        "-e",
        type=int,
        default=None,
        help="offset to end of week from Monday (= 0) [default 4 (= Friday)]")

    parser.add_argument("--format",
                        "-f",
                        default="latex",
                        choices=["text", "t", "latex", "l", "xml", "x"],
                        help="output format [default 'latex']")

    parser.add_argument("--output",
                        "-o",
                        type=argparse.FileType("w", encoding="UTF-8"),
                        help="file name to write the output to; existing "
                        "files of the same name will be overwritten!")

    parser.add_argument(
        "--paper",
        "-p",
        default=None,
        help="paper code (e.g., INFO201) [required for 'lecture' style, "
        "ignored otherwise]")

    parser.add_argument(
        "--style",
        "-s",
        default="calendar",
        choices=["calendar", "c", "lecture", "l", "iso", "i"],
        help="output style: 'calendar' for teaching calendars in course "
        "outlines, 'lecture' for individual lecture dates, or 'iso'"
        "for dates across the entire ISO-8601 year [default 'calendar']")

    parser.add_argument("--teaching-period",
                        "-t",
                        default=None,
                        choices=["SS", "S1", "S2", "FY"],
                        dest="period",
                        help="teaching period [required]")

    parser.add_argument(
        "--year",
        "-y",
        type=int,
        default=datetime.date.today().year,
        help="the year to generate dates for [default {y}]".format(
            y=datetime.date.today().year))

    args = parser.parse_args()
    # normalise format and style
    args.format = format_map[args.format]
    args.style = style_map[args.style]

    if args.style == "calendar":
        # --paper is irrelevant
        if args.paper:
            args.paper = ""
            print("{prog}: warning: --paper/-p is ignored for 'calendar' "
                  "style".format(prog=PROG))
        # --teaching-period is required
        if not args.period:
            parser.exit(
                2, "{prog}: error: --teaching-period/-t is required for "
                "'calendar' style\n".format(prog=PROG))

    elif args.style == "iso":
        # both --paper and --teaching-period are irrelevant
        if args.paper or args.period:
            args.paper = ""
            args.period = "iso"
            print("{prog}: warning: --paper/-p and --teaching-period/-t are "
                  "ignored for 'iso' style".format(prog=PROG))

    elif args.style == "lecture":
        # --end-of-week is irrelevant
        if args.end_of_week is not None:
            args.end_of_week = 0
            print("{prog}: warning: --end-of-week/-e is ignored for 'lecture' "
                  "style".format(prog=PROG))
        # both --paper and --teaching-period are required
        if not args.paper:
            parser.exit(
                2,
                message="{prog}: error: -paper/-p and --teaching-period/-t "
                "are required for 'lecture' style\n".format(prog=PROG))

    else:
        pass

    # Can't set default week offset until after error processing above,
    # otherwise it generates a spurious warning in 'lecture' style.
    if args.style != "lecture" and args.end_of_week is None:
        args.end_of_week = 4

    return args