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)
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' )
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))
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
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)
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
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()
def test_spec_too_many_named(): r = raw.copy() r["numberToProduce"] = 50 r["numberToName"] = 60 raises(ValueError, lambda: SpecVerifier(r).verifySpec(verbose=False))
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))
def test_spec_invalid_shortname(): r = raw.copy() r["name"] = "no spaces" raises(ValueError, lambda: SpecVerifier(r).verifySpec(verbose=False))
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))
def test_spec_question_extra_key(): r = deepcopy(raw) r["question"]["1"]["libel"] = "defamation" raises(ValueError, lambda: SpecVerifier(r).verifySpec(verbose=False))
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)
def test_spec_negatives_still_pass(): r = raw.copy() r["numberToName"] = -1 r["numberToProduce"] = -1 SpecVerifier(r).verifySpec(verbose=False)
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))
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))
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))
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()
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))
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))
__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])
def test_spec_longname_slash_issue1364(): r = raw.copy() r["longName"] = 'Math123 / Bio321 Midterm ∫∇·Fdv — "have fun!"😀' SpecVerifier(r).verifySpec(verbose=False)
def test_spec_verify(): s = SpecVerifier.demo() s.verifySpec(verbose=False)
# 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()
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()))
def test_spec_demo(): s = SpecVerifier.demo() assert s.number_to_name assert s.number_to_produce
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))
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.")
def test_make_rand_ver_map(): spec = SpecVerifier.demo() vm = make_random_version_map(spec) check_version_map(vm) check_version_map(vm, spec)