Пример #1
0
def test_spec_question_label_printer():
    sd = SpecVerifier.demo()
    r = deepcopy(raw)
    r["question"]["1"]["label"] = "Track 1"
    r["question"]["2"]["label"] = ""
    s = SpecVerifier(r)
    assert get_question_label(s, 1) == "Track 1"
    assert get_question_label(s, 2) == "Q2"
    assert get_question_label(s, 3) == get_question_label(sd, 3)
Пример #2
0
def parseAndVerifySpecification(fname):
    os.makedirs(specdir, exist_ok=True)
    os.makedirs("sourceVersions", exist_ok=True)
    print('Parsing and verifying the specification "{}"'.format(fname))
    if not os.path.isfile(fname):
        print(
            'Cannot find "{}" - have you run "plom-build create" yet?'.format(
                fname))
        exit(1)

    sv = SpecVerifier.from_toml_file(fname)
    sv.verifySpec()
    sv.checkCodes()
    sv.saveVerifiedSpec()
    print(
        ">>> Note <<<\n"
        "Before proceeding further, you will need to start the server."
        '\nSee "plom-server --help" for more information on how to get the server up and running.\n'
    )

    sp = SpecParser()
    if sp.spec["numberToName"] > 0:
        print(
            ">>> Note <<<\n"
            'Your spec indicates that you wish to print named papers.\nWhen the server is running, please process your class list using "plom-build class ".\n'
        )
Пример #3
0
def test_ver_map_verions_in_range():
    spec = SpecVerifier.demo()
    vm = make_random_version_map(spec)
    vm[1][1] = -1
    raises(AssertionError, lambda: check_version_map(vm))
    vm[1][1] = spec["numberOfQuestions"] + 1
    raises(AssertionError, lambda: check_version_map(vm, spec))
Пример #4
0
def test_ver_map_json_roundtrip():
    spec = SpecVerifier.demo()
    vm = make_random_version_map(spec)
    s = json.dumps(vm)
    vm2 = json.loads(s)
    assert vm != vm2  # I mean, we'd like, but Json doesn't
    vm3 = undo_json_packing_of_version_map(vm2)
    assert vm == vm3
Пример #5
0
def test_spec_setting_adds_spares():
    r = raw.copy()
    r["numberToName"] = -1
    r["numberToProduce"] = -1
    s = SpecVerifier(r)
    s.set_number_papers_to_name(16)
    s.set_number_papers_add_spares(16)
    assert s.numberToName == 16
    # creates some spares
    assert s.numberToProduce > 16
    s.verifySpec(verbose=False)
Пример #6
0
def checkSpec(spec):
    # This runs after directory check, so we can try to load the local spec file.
    try:
        localSpec = SpecVerifier.load_verified()
    except:
        print("Problem finding local spec file. Aborting.")
        return False

    if (
        localSpec["name"] == spec["name"]
        and localSpec["publicCode"] == spec["publicCode"]
    ):
        return True
    else:
        print(
            "Checking name and public-code in local-spec and server-spec. They disagree. Aborting."
        )
        return False
Пример #7
0
 def __init__(self, db, masterToken):
     log.debug("Initialising server")
     try:
         self.testSpec = SpecVerifier.load_verified()
         log.info("existing spec loaded")
     except FileNotFoundError:
         self.testSpec = None
         log.error("spec file not found -- use 'plom-build' to create one")
         raise
     self.authority = Authority(masterToken)
     self.DB = db
     self.API = serverAPI
     self.Version = __version__
     print(
         "Server launching with masterToken = '{}' {}".format(
             self.authority.get_master_token(),
             type(self.authority.get_master_token()),
         )
     )
     self.tempDirectory = tempfile.TemporaryDirectory()
     # Give directory correct permissions.
     subprocess.check_call(["chmod", "o-r", self.tempDirectory.name])
     self.load_users()
Пример #8
0
def test_spec_too_many_named():
    r = raw.copy()
    r["numberToProduce"] = 50
    r["numberToName"] = 60
    raises(ValueError, lambda: SpecVerifier(r).verifySpec(verbose=False))
Пример #9
0
def test_spec_question_missing_key():
    required_keys = ("pages", "select", "mark")
    for k in required_keys:
        r = deepcopy(raw)
        r["question"]["1"].pop(k)
        raises(ValueError, lambda: SpecVerifier(r).verifySpec(verbose=False))
Пример #10
0
def test_spec_invalid_shortname():
    r = raw.copy()
    r["name"] = "no spaces"
    raises(ValueError, lambda: SpecVerifier(r).verifySpec(verbose=False))
Пример #11
0
def test_ver_map_types3():
    spec = SpecVerifier.demo()
    vm = make_random_version_map(spec)
    vm[1]["2"] = vm[1].pop(2)
    raises(AssertionError, lambda: check_version_map(vm))
Пример #12
0
def test_spec_question_extra_key():
    r = deepcopy(raw)
    r["question"]["1"]["libel"] = "defamation"
    raises(ValueError, lambda: SpecVerifier(r).verifySpec(verbose=False))
Пример #13
0
def test_ver_map_from_dict():
    spec = SpecVerifier.demo()
    spec_dict = spec.get_public_spec_dict()
    vm = make_random_version_map(spec_dict)
    check_version_map(vm, spec_dict)
    check_version_map(vm, spec)
Пример #14
0
def test_spec_negatives_still_pass():
    r = raw.copy()
    r["numberToName"] = -1
    r["numberToProduce"] = -1
    SpecVerifier(r).verifySpec(verbose=False)
Пример #15
0
def test_ver_map_fails_if_too_short():
    spec = SpecVerifier.demo()
    vm = make_random_version_map(spec)
    vm.pop(1)
    check_version_map(vm)  # passes if we don't know spec
    raises(AssertionError, lambda: check_version_map(vm, spec))
Пример #16
0
def test_spec_label_too_long():
    r = deepcopy(raw)
    r["question"]["1"]["label"] = "Distrust That Particular Flavour"
    raises(ValueError, lambda: SpecVerifier(r).verifySpec(verbose=False))
Пример #17
0
def test_spec_question_label_printer_errors():
    s = SpecVerifier.demo()
    N = s["numberOfQuestions"]
    raises(ValueError, lambda: get_question_label(s, N + 1))
    raises(ValueError, lambda: get_question_label(s, -1))
    raises(ValueError, lambda: get_question_label(s, 0))
Пример #18
0
def main():
    args = parser.parse_args()

    if not hasattr(args, "server") or not args.server:
        try:
            args.server = os.environ["PLOM_SERVER"]
        except KeyError:
            pass
    if not hasattr(args, "password") or not args.password:
        try:
            args.password = os.environ["PLOM_MANAGER_PASSWORD"]
        except KeyError:
            pass

    if args.command == "new":
        if args.demo:
            fname = "demoSpec.toml"
        else:
            fname = checkTomlExtension(args.specFile)

        if args.demo_num_papers:
            assert args.demo, "cannot specify number of demo paper outside of demo mode"
        if args.demo:
            print("DEMO MODE: creating demo specification file")
            SpecVerifier.create_demo_template(
                fname, num_to_produce=args.demo_num_papers)
        else:
            print('Creating specification file from template: "{}"'.format(
                fname))
            print('  * Please edit the template spec "{}"'.format(fname))
            SpecVerifier.create_template(fname)

        print('Creating "sourceVersions" directory for your test source PDFs.')
        os.makedirs("sourceVersions", exist_ok=True)
        if not args.demo:
            print(
                "  * Please copy your test in as version1.pdf, version2.pdf, etc."
            )
        if args.demo:
            print(
                "DEMO MODE: building source files: version1.pdf, version2.pdf")
            if not buildDemoSourceFiles():
                exit(1)
            print('DEMO MODE: continuing as if "parse" command was run...')
            parseAndVerifySpecification(fname)
    elif args.command == "parse":
        # check the file extension
        fname = checkTomlExtension(args.specFile)
        # copy the template spec into place
        parseAndVerifySpecification(fname)
    elif args.command == "class":
        cl = process_class_list(args.classlist, args.demo)
        msgr = get_messenger(args.server, args.password)
        upload_classlist(classlist=cl, msgr=msgr)
        print("Imported classlist of length {}.".format(len(cl)))
        print("First student = {}.".format(cl[0]))
        print("Last student = {}.".format(cl[-1]))

    elif args.command == "make":
        status = build_database(args.server, args.password)
        print(status)
        build_papers(args.server, args.password, args.no_pdf, args.without_qr)

    elif args.command == "rubric":
        msgr = get_messenger(args.server, args.password)
        try:
            if args.demo:
                print("Uploading demo rubrics...")
                N = upload_demo_rubrics(msgr)
                print(f"Uploaded {N} demo rubrics")

            elif args.dump:
                filename = Path(args.dump)
                if filename.suffix.casefold() not in (".json", ".toml",
                                                      ".csv"):
                    filename = filename.with_suffix(filename.suffix + ".toml")
                print(f'Saving server\'s current rubrics to "{filename}"')
                rubrics = msgr.MgetRubrics()
                if filename.suffix == ".json":
                    with open(filename, "w") as f:
                        json.dump(rubrics, f, indent="  ")
                elif filename.suffix == ".toml":
                    with open(filename, "w") as f:
                        toml.dump({"rubric": rubrics}, f)
                else:
                    raise NotImplementedError(
                        f'Don\'t know how to export to "{filename}"')
            else:
                filename = Path(args.rubric_file)
                if filename.suffix.casefold() not in (".json", ".toml",
                                                      ".csv"):
                    filename = filename.with_suffix(filename.suffix + ".toml")
                if filename.suffix == ".json":
                    with open(filename, "r") as f:
                        rubrics = json.load(f)
                elif filename.suffix == ".toml":
                    with open(filename, "r") as f:
                        rubrics = toml.load(f)["rubric"]
                else:
                    raise NotImplementedError(
                        f'Don\'t know how to import from "{filename}"')
                print(f'Adding {len(rubrics)} rubrics from file "{filename}"')
                upload_rubrics(msgr, rubrics)

        finally:
            msgr.closeUser()
            msgr.stop()

    elif args.command == "clear":
        clear_manager_login(args.server, args.password)
    else:
        # no command given so print help.
        parser.print_help()
Пример #19
0
def test_ver_map_types4():
    spec = SpecVerifier.demo()
    vm = make_random_version_map(spec)
    vm[1][2] = "str"
    raises(AssertionError, lambda: check_version_map(vm))
Пример #20
0
def test_ver_map_fix_has_ver1_only():
    # assumes version 2 is fixed in demo: test will need adjusting if that changes
    spec = SpecVerifier.demo()
    vm = make_random_version_map(spec)
    vm[1][2] = 2
    raises(AssertionError, lambda: check_version_map(vm, spec))
Пример #21
0
__copyright__ = "Copyright (C) 2019-2020 Colin B. Macdonald and others"
__credits__ = ["The Plom Project Developers"]
__license__ = "AGPL-3.0-or-later"

import os
import sys
import shutil

from plom import SpecVerifier
from plom.finish import CSVFilename

archivename = "{COURSE}_{YEAR}{TERM}_{SHORTNAME}"

if __name__ == "__main__":
    spec = SpecVerifier.load_verified()
    basename = spec["name"]
    archivename = archivename.replace("{SHORTNAME}", basename)

    print("\n\nTODO: THIS SCRIPT NEEDS RETHINKING FOR 0.4!\n\n")

    # TODO: someday we can get this from spec file?
    # https://gitlab.com/plom/plom/issues/94
    if not len(sys.argv) == 4:
        print("ERROR: Incorrect command line...")
        print(__doc__)
        sys.exit(1)
    archivename = archivename.replace("{COURSE}", sys.argv[1])
    archivename = archivename.replace("{YEAR}", sys.argv[2])
    archivename = archivename.replace("{TERM}", sys.argv[3])
Пример #22
0
def test_spec_longname_slash_issue1364():
    r = raw.copy()
    r["longName"] = 'Math123 / Bio321 Midterm ∫∇·Fdv — "have fun!"😀'
    SpecVerifier(r).verifySpec(verbose=False)
Пример #23
0
def test_spec_verify():
    s = SpecVerifier.demo()
    s.verifySpec(verbose=False)
Пример #24
0
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (C) 2021 Colin B. Macdonald

from pytest import raises
from copy import deepcopy
from plom import SpecVerifier, get_question_label

raw = SpecVerifier.demo().spec


def test_spec_demo():
    s = SpecVerifier.demo()
    assert s.number_to_name
    assert s.number_to_produce


def test_spec_verify():
    s = SpecVerifier.demo()
    s.verifySpec(verbose=False)


def test_spec_too_many_named():
    r = raw.copy()
    r["numberToProduce"] = 50
    r["numberToName"] = 60
    s = SpecVerifier(r)
    raises(ValueError, lambda: s.verifySpec(verbose=False))


def test_spec_negatives_still_pass():
    r = raw.copy()
Пример #25
0
def test_ver_map_check_spec_or_dict():
    spec = SpecVerifier.demo()
    vm = make_random_version_map(spec)
    vm.pop(1)
    raises(AssertionError, lambda: check_version_map(vm, spec))
    raises(AssertionError, lambda: check_version_map(vm, spec.get_public_spec_dict()))
Пример #26
0
def test_spec_demo():
    s = SpecVerifier.demo()
    assert s.number_to_name
    assert s.number_to_produce
Пример #27
0
def test_spec_unique_labels():
    r = deepcopy(raw)
    r["question"]["1"]["label"] = "ExA"
    r["question"]["2"]["label"] = "ExA"
    raises(ValueError, lambda: SpecVerifier(r).verifySpec(verbose=False))
Пример #28
0
def main(use_hex, digits, salt=None):
    """Make the secret codes and the return-code webpage.

    args:
        use_hex (bool): use random hex digits, otherwise an integer
            without leading zeros.
        digits (int): length of secret code.
        salt (str): instead of random, hash from student ID salted
            with this string.  Defaults to None, which means do not
            do this, use random secret codes.
    """
    # TODO: another case of filesystem access of spec
    spec = SpecVerifier.load_verified()
    shortname = spec["name"]
    longname = html.escape(spec["longName"])
    codedReturnDir = Path("codedReturn")

    reassembles = ["reassembled", "reassembled_ID_but_not_marked"]
    if os.path.isdir(reassembles[0]) and os.path.isdir(reassembles[1]):
        print('You have more than one "reassembled*" directory:')
        print("  decide what you trying to do and run me again.")
        sys.exit(2)
    elif os.path.isdir(reassembles[0]):
        fromdir = reassembles[0]
    elif os.path.isdir(reassembles[1]):
        fromdir = reassembles[1]
    else:
        print("I cannot find any of the dirs: " + ", ".join(reassembles))
        print("  Have you called the `reassemble` command yet?")
        sys.exit(3)
    print('We will take pdf files from "{0}".'.format(fromdir))

    if codedReturnDir.exists() or os.path.exists("return_codes.csv"):
        print(
            'Directory "{}" and/or "return_codes.csv" already exist:\n'
            "  if you want to re-run this script, delete them first.".format(
                codedReturnDir
            )
        )
        sys.exit(4)
    os.makedirs(codedReturnDir)

    print("Generating return codes spreadsheet...")
    if salt:
        print('Salt string "{}" can reproduce these return codes'.format(salt))
    else:
        print("These return codes will be random and non-reproducible".format(salt))
    sns = csv_add_return_codes(
        CSVFilename, "return_codes.csv", "StudentID", use_hex, digits, salt
    )
    print('The return codes are in "return_codes.csv"')

    numfiles = do_renaming(fromdir, codedReturnDir, sns)
    if numfiles > 0:
        print("Copied (and renamed) {0} files".format(numfiles))
    else:
        print('no pdf files in "{0}"?  Stopping!'.format(fromdir))
        sys.exit(5)

    print("Adding index.html file")
    from .html_view_test_template import htmlsrc

    htmlsrc = htmlsrc.replace("__COURSENAME__", longname)
    htmlsrc = htmlsrc.replace("__TESTNAME__", shortname)
    htmlsrc = htmlsrc.replace("__CODE_LENGTH__", str(digits))
    htmlsrc = htmlsrc.replace("__SID_LENGTH__", str(StudentIDLength))

    with open(codedReturnDir / "index.html", "w") as htmlfile:
        htmlfile.write(htmlsrc)

    print("All done!  Next tasks:")
    print('  * Copy "{}" to your webserver'.format(codedReturnDir))
    print('  * Privately communicate info from "return_codes.csv"')
    print("      - E.g., see `contrib/plom-return_codes_to_canvas_csv.py`")
    print("  * Read docs about the security implications of all this.")
Пример #29
0
def test_make_rand_ver_map():
    spec = SpecVerifier.demo()
    vm = make_random_version_map(spec)
    check_version_map(vm)
    check_version_map(vm, spec)