From a2c4948cc5747c12b494a3ce23feb4aedb6eb561 Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Tue, 24 Apr 2012 16:59:37 -0400 Subject: [PATCH 01/13] initial commit for checker script that validates problem files --- common/lib/capa/capa_problem.py | 7 ++- common/lib/capa/checker.py | 91 +++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 4 deletions(-) create mode 100755 common/lib/capa/checker.py diff --git a/common/lib/capa/capa_problem.py b/common/lib/capa/capa_problem.py index a817433b49..fae8042eff 100644 --- a/common/lib/capa/capa_problem.py +++ b/common/lib/capa/capa_problem.py @@ -174,10 +174,9 @@ class LoncapaProblem(object): return self.correct_map def get_question_answers(self): - ''' - Make a dict of (id,correct_answer) entries, for all the problems. - Called by "show answers" button JSON request (see capa_module) - ''' + """Returns a dict of answer_ids to answer values. If we can't generate + an answer (because it's a customresponse type), that answer_id is + not included.""" context=self.extract_context(self.tree) answer_map = dict() problems_simple = self.extract_problems(self.tree) # purified (flat) XML tree of just response queries diff --git a/common/lib/capa/checker.py b/common/lib/capa/checker.py new file mode 100755 index 0000000000..5e0bd34211 --- /dev/null +++ b/common/lib/capa/checker.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python +""" +Commandline tool for doing operations on Problems +""" +import argparse +import logging +import sys + +from cStringIO import StringIO + +from capa_problem import LoncapaProblem + +logging.basicConfig(format="%(levelname)s %(message)s") +log = logging.getLogger('capa.checker') + +def main(): + parser = argparse.ArgumentParser(description='Check Problem Files') + parser.add_argument("command", choices=['test']) # Watch? Render? Open? + parser.add_argument("files", nargs="+", type=argparse.FileType('r')) + parser.add_argument("--seed", required=False, type=int) + + args = parser.parse_args() + log.setLevel(logging.INFO) + + old_stdout = sys.stdout + old_stderr = sys.stderr + for problem_file in args.files: + log.info("Opening {0}".format(problem_file.name)) + sys.stdout = problem_stdout = StringIO() + sys.stderr = problem_stderr = StringIO() + + try: + problem = LoncapaProblem(problem_file.name, "fakeid", seed=args.seed) + except Exception as ex: + log.error("Could not parse file {0}".format(problem_file.name)) + log.exception(ex) + continue + + if args.command == 'test': + test_problem(problem) + + log_captured_output(problem_stdout, + "captured stdout from {0}".format(problem_file.name)) + log_captured_output(problem_stderr, + "captured stderr from {0}".format(problem_file.name)) + + # Print captured problem prints + problem_file.close() + + sys.stdout = old_stdout + sys.stderr = old_stderr + + # In case we want to do anything else here. + +def log_captured_output(output_stream, stream_name): + output_stream.seek(0) + output_text = output_stream.read() + if output_text: + log.info("##### Begin {0} #####\n".format(stream_name) + output_text) + log.info("##### End {0} #####".format(stream_name)) + +def test_problem(problem): + """Split this up so that we're only used for formula/numeric answers. + + Examples of where this fails: + * Displayed answers use units but acceptable ones do not. + - L1e0.xml + - Presents itself as UndefinedVariable (when it tries to pass to calc) + * "a or d" is what's displayed, but only "a" or "d" is accepted, not the + string "a or d". + - L1-e00.xml + """ + answers = problem.get_question_answers() + log.debug(answers) + if answers: + try: + results = problem.grade_answers(answers) + log.debug(results) + assert(all(result == 'correct' for result in results.values())) + except AssertionError: + log.error("The following generated answers were not accepted:") + for question_id, result in sorted(results.items()): + if result != 'correct': + log.error(" {0} = {1}".format(question_id, answers[question_id])) + except Exception as ex: + log.exception(ex) + + + +if __name__ == '__main__': + sys.exit(main()) From 2c6efbfd5d15e00455211205e71504eec62715c5 Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Tue, 24 Apr 2012 17:31:01 -0400 Subject: [PATCH 02/13] only run problem testing when test command is entered in CLI --- common/lib/capa/checker.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/common/lib/capa/checker.py b/common/lib/capa/checker.py index 5e0bd34211..83dfdcc847 100755 --- a/common/lib/capa/checker.py +++ b/common/lib/capa/checker.py @@ -38,11 +38,10 @@ def main(): if args.command == 'test': test_problem(problem) - - log_captured_output(problem_stdout, - "captured stdout from {0}".format(problem_file.name)) - log_captured_output(problem_stderr, - "captured stderr from {0}".format(problem_file.name)) + log_captured_output(problem_stdout, + "captured stdout from {0}".format(problem_file.name)) + log_captured_output(problem_stderr, + "captured stderr from {0}".format(problem_file.name)) # Print captured problem prints problem_file.close() From 8131cb3334a47c81ba2e7b7d3e9c6e32854d5f9e Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Wed, 25 Apr 2012 14:37:33 -0400 Subject: [PATCH 03/13] Allow problem tests to run even if problems can't give us answers to check. customresponses can't tell us what their answer is (there can be a wide range of answers -- it's verified by a small program). So here we simply ignore those fields we can't generate answers for when doing tests against problems. --- common/lib/capa/capa_problem.py | 18 ++++++++++++++++++ common/lib/capa/checker.py | 27 +++++++++++++++++++-------- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/common/lib/capa/capa_problem.py b/common/lib/capa/capa_problem.py index fae8042eff..de3f1a319a 100644 --- a/common/lib/capa/capa_problem.py +++ b/common/lib/capa/capa_problem.py @@ -200,6 +200,24 @@ class LoncapaProblem(object): return answer_map + def get_answer_ids(self): + """Return the IDs of all the responses -- these are the keys used for + the dicts returned by grade_answers and get_question_answers. (Though + get_question_answers may only return a subset of these.""" + answer_ids = [] + context=self.extract_context(self.tree) + problems_simple = self.extract_problems(self.tree) + for response in problems_simple: + responder = response_types[response.tag](response, self.context) + if hasattr(responder, "answer_id"): + answer_ids.append(responder.answer_id) + # customresponse types can have multiple answer_ids + elif hasattr(responder, "answer_ids"): + answer_ids.extend(responder.answer_ids) + + return answer_ids + + # ======= Private ======== def extract_context(self, tree, seed = struct.unpack('i', os.urandom(4))[0]): # private diff --git a/common/lib/capa/checker.py b/common/lib/capa/checker.py index 83dfdcc847..a30012fcb8 100755 --- a/common/lib/capa/checker.py +++ b/common/lib/capa/checker.py @@ -69,18 +69,29 @@ def test_problem(problem): string "a or d". - L1-e00.xml """ - answers = problem.get_question_answers() - log.debug(answers) - if answers: + # These are actual answers we get from the responsetypes + real_answers = problem.get_question_answers() + + # all_answers is real_answers + blanks for other answer_ids for which the + # responsetypes can't provide us pre-canned answers (customresopnse) + all_answer_ids = problem.get_answer_ids() + all_answers = dict((answer_id, real_answers.get(answer_id, "")) + for answer_id in all_answer_ids) + + log.debug(real_answers) + if real_answers: try: - results = problem.grade_answers(answers) - log.debug(results) - assert(all(result == 'correct' for result in results.values())) + real_results = dict((answer_id, result) for answer_id, result + in problem.grade_answers(all_answers).items() + if answer_id in real_answers) + log.debug(real_results) + assert(all(result == 'correct' + for answer_id, result in real_results.items())) except AssertionError: log.error("The following generated answers were not accepted:") - for question_id, result in sorted(results.items()): + for question_id, result in sorted(real_results.items()): if result != 'correct': - log.error(" {0} = {1}".format(question_id, answers[question_id])) + log.error(" {0} = {1}".format(question_id, real_answers[question_id])) except Exception as ex: log.exception(ex) From d9e72b91ba936a8c1bc226f54805a36a8e9877b9 Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Wed, 25 Apr 2012 14:50:27 -0400 Subject: [PATCH 04/13] add commandline flag for logging level --- common/lib/capa/checker.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/lib/capa/checker.py b/common/lib/capa/checker.py index a30012fcb8..44627d7f58 100755 --- a/common/lib/capa/checker.py +++ b/common/lib/capa/checker.py @@ -4,6 +4,7 @@ Commandline tool for doing operations on Problems """ import argparse import logging +import os.path import sys from cStringIO import StringIO @@ -18,9 +19,12 @@ def main(): parser.add_argument("command", choices=['test']) # Watch? Render? Open? parser.add_argument("files", nargs="+", type=argparse.FileType('r')) parser.add_argument("--seed", required=False, type=int) + parser.add_argument("--log-level", required=False, default="INFO", + choices=['info', 'debug', 'warn', 'error', + 'INFO', 'DEBUG', 'WARN', 'ERROR']) args = parser.parse_args() - log.setLevel(logging.INFO) + log.setLevel(args.log_level.upper()) old_stdout = sys.stdout old_stderr = sys.stderr From 0a5e6ce681b1b0f21d5444c10b6e3c3345ad28ee Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Wed, 25 Apr 2012 15:17:03 -0400 Subject: [PATCH 05/13] A little refactoring to make clearer what running test on problem means --- common/lib/capa/capa_problem.py | 4 +++ common/lib/capa/checker.py | 47 +++++++++++++++++++-------------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/common/lib/capa/capa_problem.py b/common/lib/capa/capa_problem.py index de3f1a319a..f107f5f535 100644 --- a/common/lib/capa/capa_problem.py +++ b/common/lib/capa/capa_problem.py @@ -12,6 +12,7 @@ import logging import math import numpy import os +import os.path import random import re import scipy @@ -125,6 +126,9 @@ class LoncapaProblem(object): responder = response_types[response.tag](response, self.context, self.system) responder.preprocess_response() + def __unicode__(self): + return u"LoncapaProblem ({0})".format(os.path.basename(self.filename)) + def get_state(self): ''' Stored per-user session data neeeded to: 1) Recreate the problem diff --git a/common/lib/capa/checker.py b/common/lib/capa/checker.py index 44627d7f58..2d0172c12d 100755 --- a/common/lib/capa/checker.py +++ b/common/lib/capa/checker.py @@ -2,6 +2,8 @@ """ Commandline tool for doing operations on Problems """ +from __future__ import unicode_literals + import argparse import logging import os.path @@ -26,12 +28,8 @@ def main(): args = parser.parse_args() log.setLevel(args.log_level.upper()) - old_stdout = sys.stdout - old_stderr = sys.stderr for problem_file in args.files: log.info("Opening {0}".format(problem_file.name)) - sys.stdout = problem_stdout = StringIO() - sys.stderr = problem_stderr = StringIO() try: problem = LoncapaProblem(problem_file.name, "fakeid", seed=args.seed) @@ -41,28 +39,31 @@ def main(): continue if args.command == 'test': - test_problem(problem) - log_captured_output(problem_stdout, - "captured stdout from {0}".format(problem_file.name)) - log_captured_output(problem_stderr, - "captured stderr from {0}".format(problem_file.name)) + command_test(problem) - # Print captured problem prints problem_file.close() - sys.stdout = old_stdout - sys.stderr = old_stderr - # In case we want to do anything else here. -def log_captured_output(output_stream, stream_name): - output_stream.seek(0) - output_text = output_stream.read() - if output_text: - log.info("##### Begin {0} #####\n".format(stream_name) + output_text) - log.info("##### End {0} #####".format(stream_name)) +def command_test(problem): + # We're going to trap stdout/stderr from the problems (yes, some print) + old_stdout, old_stderr = sys.stdout, sys.stderr + try: + sys.stdout = StringIO() + sys.stderr = StringIO() -def test_problem(problem): + check_that_suggested_answers_work(problem) + + log_captured_output(sys.stdout, + "captured stdout from {0}".format(problem)) + log_captured_output(sys.stderr, + "captured stderr from {0}".format(problem)) + except Exception as e: + log.exception(e) + finally: + sys.stdout, sys.stderr = old_stdout, old_stderr + +def check_that_suggested_answers_work(problem): """Split this up so that we're only used for formula/numeric answers. Examples of where this fails: @@ -99,6 +100,12 @@ def test_problem(problem): except Exception as ex: log.exception(ex) +def log_captured_output(output_stream, stream_name): + output_stream.seek(0) + output_text = output_stream.read() + if output_text: + log.info("##### Begin {0} #####\n".format(stream_name) + output_text) + log.info("##### End {0} #####".format(stream_name)) if __name__ == '__main__': From accdbdd6601cec1ce2dc207651cf0e562247a274 Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Thu, 26 Apr 2012 14:06:16 -0400 Subject: [PATCH 06/13] Check that blank answers are never marked correct (because of NaN issues) --- common/lib/capa/checker.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/common/lib/capa/checker.py b/common/lib/capa/checker.py index 2d0172c12d..109ba636cd 100755 --- a/common/lib/capa/checker.py +++ b/common/lib/capa/checker.py @@ -53,6 +53,7 @@ def command_test(problem): sys.stderr = StringIO() check_that_suggested_answers_work(problem) + check_that_blanks_fail(problem) log_captured_output(sys.stdout, "captured stdout from {0}".format(problem)) @@ -63,6 +64,21 @@ def command_test(problem): finally: sys.stdout, sys.stderr = old_stdout, old_stderr +def check_that_blanks_fail(problem): + """Leaving it blank should never work. Neither should a space.""" + blank_answers = dict((answer_id, u"") + for answer_id in problem.get_question_answers()) + grading_results = problem.grade_answers(blank_answers) + try: + assert(all(result == 'incorrect' for result in grading_results.values())) + except AssertionError: + log.error("Blank accepted as correct answer in {0} for {1}" + .format(problem, + [answer_id for answer_id, result + in sorted(grading_results.items()) + if result != 'incorrect'])) + + def check_that_suggested_answers_work(problem): """Split this up so that we're only used for formula/numeric answers. @@ -78,12 +94,12 @@ def check_that_suggested_answers_work(problem): real_answers = problem.get_question_answers() # all_answers is real_answers + blanks for other answer_ids for which the - # responsetypes can't provide us pre-canned answers (customresopnse) + # responsetypes can't provide us pre-canned answers (customresponse) all_answer_ids = problem.get_answer_ids() all_answers = dict((answer_id, real_answers.get(answer_id, "")) for answer_id in all_answer_ids) - log.debug(real_answers) + log.debug("Real answers: {0}".format(real_answers)) if real_answers: try: real_results = dict((answer_id, result) for answer_id, result @@ -93,11 +109,13 @@ def check_that_suggested_answers_work(problem): assert(all(result == 'correct' for answer_id, result in real_results.items())) except AssertionError: - log.error("The following generated answers were not accepted:") + log.error("The following generated answers were not accepted for {0}:" + .format(problem)) for question_id, result in sorted(real_results.items()): if result != 'correct': log.error(" {0} = {1}".format(question_id, real_answers[question_id])) except Exception as ex: + log.error("Uncaught error in {0}".format(problem)) log.exception(ex) def log_captured_output(output_stream, stream_name): From 3cbb74aee7c8a581ea4e6af2f8412d2b2f6abff3 Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Mon, 14 May 2012 16:21:03 -0400 Subject: [PATCH 07/13] fix breakage in problem checker caused by LoncapaProblem taking a file object in its constructor instead of a filename --- common/lib/capa/capa_problem.py | 2 +- common/lib/capa/checker.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/lib/capa/capa_problem.py b/common/lib/capa/capa_problem.py index f107f5f535..db41ef5441 100644 --- a/common/lib/capa/capa_problem.py +++ b/common/lib/capa/capa_problem.py @@ -127,7 +127,7 @@ class LoncapaProblem(object): responder.preprocess_response() def __unicode__(self): - return u"LoncapaProblem ({0})".format(os.path.basename(self.filename)) + return u"LoncapaProblem ({0})".format(os.path.basename(self.fileobject.name)) def get_state(self): ''' Stored per-user session data neeeded to: diff --git a/common/lib/capa/checker.py b/common/lib/capa/checker.py index 109ba636cd..a4f4b8683f 100755 --- a/common/lib/capa/checker.py +++ b/common/lib/capa/checker.py @@ -32,7 +32,7 @@ def main(): log.info("Opening {0}".format(problem_file.name)) try: - problem = LoncapaProblem(problem_file.name, "fakeid", seed=args.seed) + problem = LoncapaProblem(problem_file, "fakeid", seed=args.seed) except Exception as ex: log.error("Could not parse file {0}".format(problem_file.name)) log.exception(ex) From 9f2bce000158dbb7dfd3c9fa2c9a0e2837ee6e01 Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Mon, 14 May 2012 19:22:09 -0400 Subject: [PATCH 08/13] Catch common case where problem answers are specified with units but don't expect them in the input --- common/lib/capa/checker.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/lib/capa/checker.py b/common/lib/capa/checker.py index a4f4b8683f..81c46677b3 100755 --- a/common/lib/capa/checker.py +++ b/common/lib/capa/checker.py @@ -11,6 +11,7 @@ import sys from cStringIO import StringIO +from calc import UndefinedVariable from capa_problem import LoncapaProblem logging.basicConfig(format="%(levelname)s %(message)s") @@ -108,6 +109,9 @@ def check_that_suggested_answers_work(problem): log.debug(real_results) assert(all(result == 'correct' for answer_id, result in real_results.items())) + except UndefinedVariable as uv_exc: + log.error("The variable \"{0}\" specified in the ".format(uv_exc) + + "solution isn't recognized (is it a units measure?).") except AssertionError: log.error("The following generated answers were not accepted for {0}:" .format(problem)) From b096981482012b418191f5137b295dea40b4c676 Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Tue, 15 May 2012 17:22:50 -0400 Subject: [PATCH 09/13] add a 'show' command to our little problem utility --- common/lib/capa/checker.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/common/lib/capa/checker.py b/common/lib/capa/checker.py index 81c46677b3..85c57521e3 100755 --- a/common/lib/capa/checker.py +++ b/common/lib/capa/checker.py @@ -19,7 +19,7 @@ log = logging.getLogger('capa.checker') def main(): parser = argparse.ArgumentParser(description='Check Problem Files') - parser.add_argument("command", choices=['test']) # Watch? Render? Open? + parser.add_argument("command", choices=['test', 'show']) # Watch? Render? Open? parser.add_argument("files", nargs="+", type=argparse.FileType('r')) parser.add_argument("--seed", required=False, type=int) parser.add_argument("--log-level", required=False, default="INFO", @@ -41,11 +41,18 @@ def main(): if args.command == 'test': command_test(problem) + elif args.command == 'show': + command_show(problem) problem_file.close() # In case we want to do anything else here. +def command_show(problem): + """Display the text for this problem""" + print problem.get_html() + + def command_test(problem): # We're going to trap stdout/stderr from the problems (yes, some print) old_stdout, old_stderr = sys.stdout, sys.stderr From 46e108e15b3f037bb2b0ac00042c09d79ad27ef8 Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Thu, 17 May 2012 17:58:07 -0400 Subject: [PATCH 10/13] remove assumption that LoncapaProblem.fileobject is actually a file on the filesystem --- common/lib/capa/capa_problem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/lib/capa/capa_problem.py b/common/lib/capa/capa_problem.py index db41ef5441..f051979be4 100644 --- a/common/lib/capa/capa_problem.py +++ b/common/lib/capa/capa_problem.py @@ -127,7 +127,7 @@ class LoncapaProblem(object): responder.preprocess_response() def __unicode__(self): - return u"LoncapaProblem ({0})".format(os.path.basename(self.fileobject.name)) + return u"LoncapaProblem ({0})".format(self.fileobject) def get_state(self): ''' Stored per-user session data neeeded to: From 8643b725501248ffcd9f1540adb97df45efc82ea Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Thu, 17 May 2012 18:00:58 -0400 Subject: [PATCH 11/13] Change inaccurate comment. --- common/lib/capa/capa_problem.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/common/lib/capa/capa_problem.py b/common/lib/capa/capa_problem.py index f051979be4..4b40df6653 100644 --- a/common/lib/capa/capa_problem.py +++ b/common/lib/capa/capa_problem.py @@ -179,8 +179,10 @@ class LoncapaProblem(object): def get_question_answers(self): """Returns a dict of answer_ids to answer values. If we can't generate - an answer (because it's a customresponse type), that answer_id is - not included.""" + an answer (this sometimes happens in customresponses), that answer_id is + not included. Called by "show answers" button JSON request + (see capa_module) + """ context=self.extract_context(self.tree) answer_map = dict() problems_simple = self.extract_problems(self.tree) # purified (flat) XML tree of just response queries From 684be8d25711398c271207512091dc007a3bc197 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Thu, 7 Jun 2012 15:02:03 -0400 Subject: [PATCH 12/13] Move capa templates into the capa library --- {lms => common/lib/capa}/templates/choicegroup.html | 0 {lms => common/lib/capa}/templates/imageinput.html | 0 {lms => common/lib/capa}/templates/jstextline.html | 0 {lms => common/lib/capa}/templates/mathstring.html | 0 {lms => common/lib/capa}/templates/schematicinput.html | 0 {lms => common/lib/capa}/templates/solutionspan.html | 0 {lms => common/lib/capa}/templates/textinput.html | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename {lms => common/lib/capa}/templates/choicegroup.html (100%) rename {lms => common/lib/capa}/templates/imageinput.html (100%) rename {lms => common/lib/capa}/templates/jstextline.html (100%) rename {lms => common/lib/capa}/templates/mathstring.html (100%) rename {lms => common/lib/capa}/templates/schematicinput.html (100%) rename {lms => common/lib/capa}/templates/solutionspan.html (100%) rename {lms => common/lib/capa}/templates/textinput.html (100%) diff --git a/lms/templates/choicegroup.html b/common/lib/capa/templates/choicegroup.html similarity index 100% rename from lms/templates/choicegroup.html rename to common/lib/capa/templates/choicegroup.html diff --git a/lms/templates/imageinput.html b/common/lib/capa/templates/imageinput.html similarity index 100% rename from lms/templates/imageinput.html rename to common/lib/capa/templates/imageinput.html diff --git a/lms/templates/jstextline.html b/common/lib/capa/templates/jstextline.html similarity index 100% rename from lms/templates/jstextline.html rename to common/lib/capa/templates/jstextline.html diff --git a/lms/templates/mathstring.html b/common/lib/capa/templates/mathstring.html similarity index 100% rename from lms/templates/mathstring.html rename to common/lib/capa/templates/mathstring.html diff --git a/lms/templates/schematicinput.html b/common/lib/capa/templates/schematicinput.html similarity index 100% rename from lms/templates/schematicinput.html rename to common/lib/capa/templates/schematicinput.html diff --git a/lms/templates/solutionspan.html b/common/lib/capa/templates/solutionspan.html similarity index 100% rename from lms/templates/solutionspan.html rename to common/lib/capa/templates/solutionspan.html diff --git a/lms/templates/textinput.html b/common/lib/capa/templates/textinput.html similarity index 100% rename from lms/templates/textinput.html rename to common/lib/capa/templates/textinput.html From f2423ca15e529f3cc227f473ade320162453064d Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Thu, 7 Jun 2012 14:56:27 -0400 Subject: [PATCH 13/13] Add a local system with a mako render function for testing problems --- common/lib/capa/checker.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/common/lib/capa/checker.py b/common/lib/capa/checker.py index 85c57521e3..742d28766b 100755 --- a/common/lib/capa/checker.py +++ b/common/lib/capa/checker.py @@ -6,17 +6,33 @@ from __future__ import unicode_literals import argparse import logging -import os.path import sys +from path import path from cStringIO import StringIO +from collections import defaultdict from calc import UndefinedVariable from capa_problem import LoncapaProblem +from mako.lookup import TemplateLookup logging.basicConfig(format="%(levelname)s %(message)s") log = logging.getLogger('capa.checker') + +class DemoSystem(object): + def __init__(self): + self.lookup = TemplateLookup(directories=[path(__file__).dirname() / 'templates']) + + def render_template(self, template_filename, dictionary, context=None): + if context is None: + context = {} + + context_dict = {} + context_dict.update(dictionary) + context_dict.update(context) + return self.lookup.get_template(template_filename).render(**context_dict) + def main(): parser = argparse.ArgumentParser(description='Check Problem Files') parser.add_argument("command", choices=['test', 'show']) # Watch? Render? Open? @@ -29,11 +45,13 @@ def main(): args = parser.parse_args() log.setLevel(args.log_level.upper()) + system = DemoSystem() + for problem_file in args.files: log.info("Opening {0}".format(problem_file.name)) try: - problem = LoncapaProblem(problem_file, "fakeid", seed=args.seed) + problem = LoncapaProblem(problem_file, "fakeid", seed=args.seed, system=system) except Exception as ex: log.error("Could not parse file {0}".format(problem_file.name)) log.exception(ex)