diff --git a/mitxgraders/listgrader.py b/mitxgraders/listgrader.py index 56f09fa2..65559aa5 100644 --- a/mitxgraders/listgrader.py +++ b/mitxgraders/listgrader.py @@ -59,27 +59,23 @@ def calculate_cost(result): input_list = [result_matrix[i][j] for i, j in indexes] return input_list -def padded_find_optimal_order(check, answers, student_list): +def get_padded_lists(list1, list2): """ - Same as find_optimal_order, but keeps track of missing and extra answers. - - Idea is: - use _AutomaticFailure to pad expect and answers to equal length - modify check to reject _AutomaticFailure + Pads the shorter of list1 and list2 and returns copies of both """ - if len(answers) == len(student_list): - return find_optimal_order(check, answers, student_list) + maxlen = max(len(list1), len(list2)) + padded1 = list1 + [_AutomaticFailure()]*(maxlen-len(list1)) + padded2 = list2 + [_AutomaticFailure()]*(maxlen-len(list2)) - maxlen = max(len(answers), len(student_list)) - padded_answers = answers + [_AutomaticFailure()]*(maxlen-len(answers)) - padded_student_list = student_list + [_AutomaticFailure()]*(maxlen-len(student_list)) + return padded1, padded2 +def padded_check(check): + """Wraps a check function to reject _AutomaticFailure""" def _check(ans, inp): if isinstance(ans, _AutomaticFailure) or isinstance(inp, _AutomaticFailure): return {'ok': False, 'msg': '', 'grade_decimal': 0} return check(ans, inp) - - return find_optimal_order(_check, padded_answers, padded_student_list) + return _check def consolidate_grades(grade_decimals, n_expect=None): """ @@ -677,15 +673,19 @@ def check_response(self, answer, student_input): len(student_list), self.config['delimiter'])) + # We need to keep track of missing and extra answers. + # Idea is: + # use _AutomaticFailure to pad expect and answers to equal length + # modify check to reject _AutomaticFailure + pad_ans, pad_stud = get_padded_lists(answers, student_list) + # Modify the check function to deal with the padding + checker = padded_check(self.config['subgrader'].check) + + # Compute the results if self.config['ordered']: - input_list = [ - self.config['subgrader'].check(*pair) - for pair in zip(answers, student_list) - ] + input_list = [checker(*pair) for pair in zip(pad_ans, pad_stud)] else: - input_list = padded_find_optimal_order(self.config['subgrader'].check, - answers, - student_list) + input_list = find_optimal_order(checker, pad_ans, pad_stud) result = consolidate_cfn_return(input_list, n_expect=len(answers), diff --git a/python_lib.zip b/python_lib.zip index 8b0cbff5..ea06d9b7 100644 Binary files a/python_lib.zip and b/python_lib.zip differ diff --git a/tests/test_singlelistgrader.py b/tests/test_singlelistgrader.py index e50c8904..52b09965 100644 --- a/tests/test_singlelistgrader.py +++ b/tests/test_singlelistgrader.py @@ -98,6 +98,15 @@ def test_too_many_items(): } assert grader(None, submission) == expected_result +def test_ordered_wrong_number(): + grader = SingleListGrader( + answers=["tiger", "skunk"], + subgrader=StringGrader(), + length_error=False + ) + assert grader(None, "tiger, skunk, horse")["grade_decimal"] == 0.5 + assert grader(None, "tiger")["grade_decimal"] == 0.5 + def test_way_too_many_items_reduces_score_to_zero(): grader = SingleListGrader( answers=[