diff --git a/common/lib/xmodule/xmodule/graders.py b/common/lib/xmodule/xmodule/graders.py index 35318f4f1e..862da791c0 100644 --- a/common/lib/xmodule/xmodule/graders.py +++ b/common/lib/xmodule/xmodule/graders.py @@ -45,8 +45,9 @@ def invalid_args(func, argdict): Given a function and a dictionary of arguments, returns a set of arguments from argdict that aren't accepted by func """ - args, varargs, keywords, defaults = inspect.getargspec(func) - if keywords: return set() # All accepted + args, _, keywords, _ = inspect.getargspec(func) + if keywords: + return set() # All accepted return set(argdict) - set(args) @@ -119,7 +120,7 @@ class CourseGrader(object): that has the matching section format. The grader outputs a dictionary with the following keys: - - percent: Contaisn a float value, which is the final percentage score for the student. + - percent: Contains a float value, which is the final percentage score for the student. - section_breakdown: This is a list of dictionaries which provide details on sections that were graded. These are used for display in a graph or chart. The format for a section_breakdown dictionary is explained below. @@ -150,6 +151,7 @@ class CourseGrader(object): @abc.abstractmethod def grade(self, grade_sheet, generate_random_scores=False): + '''Given a grade sheet, return a dict containing grading information''' raise NotImplementedError @@ -158,7 +160,10 @@ class WeightedSubsectionsGrader(CourseGrader): This grader takes a list of tuples containing (grader, category_name, weight) and computes a final grade by totalling the contribution of each sub grader and multiplying it by the given weight. For example, the sections may be - [ (homeworkGrader, "Homework", 0.15), (labGrader, "Labs", 0.15), (midtermGrader, "Midterm", 0.30), (finalGrader, "Final", 0.40) ] + + [ (homeworkGrader, "Homework", 0.15), (labGrader, "Labs", 0.15), (midtermGrader, "Midterm", 0.30), + (finalGrader, "Final", 0.40) ] + All items in section_breakdown for each subgrader will be combined. A grade_breakdown will be composed using the score from each grader. @@ -177,12 +182,12 @@ class WeightedSubsectionsGrader(CourseGrader): for subgrader, category, weight in self.sections: subgrade_result = subgrader.grade(grade_sheet, generate_random_scores) - weightedPercent = subgrade_result['percent'] * weight - section_detail = "{0} = {1:.1%} of a possible {2:.0%}".format(category, weightedPercent, weight) + weighted_percent = subgrade_result['percent'] * weight + section_detail = "{0} = {1:.1%} of a possible {2:.0%}".format(category, weighted_percent, weight) - total_percent += weightedPercent + total_percent += weighted_percent section_breakdown += subgrade_result['section_breakdown'] - grade_breakdown.append({'percent': weightedPercent, 'detail': section_detail, 'category': category}) + grade_breakdown.append({'percent': weighted_percent, 'detail': section_detail, 'category': category}) return {'percent': total_percent, 'section_breakdown': section_breakdown, @@ -203,32 +208,33 @@ class SingleSectionGrader(CourseGrader): self.category = category or name def grade(self, grade_sheet, generate_random_scores=False): - foundScore = None + found_score = None if self.type in grade_sheet: for score in grade_sheet[self.type]: if score.section == self.name: - foundScore = score + found_score = score break - if foundScore or generate_random_scores: + if found_score or generate_random_scores: if generate_random_scores: # for debugging! earned = random.randint(2, 15) possible = random.randint(earned, 15) else: # We found the score - earned = foundScore.earned - possible = foundScore.possible + earned = found_score.earned + possible = found_score.possible percent = earned / float(possible) detail = "{name} - {percent:.0%} ({earned:.3n}/{possible:.3n})".format(name=self.name, - percent=percent, - earned=float(earned), - possible=float(possible)) + percent=percent, + earned=float(earned), + possible=float(possible)) else: percent = 0.0 detail = "{name} - 0% (?/?)".format(name=self.name) - breakdown = [{'percent': percent, 'label': self.short_label, 'detail': detail, 'category': self.category, 'prominent': True}] + breakdown = [{'percent': percent, 'label': self.short_label, + 'detail': detail, 'category': self.category, 'prominent': True}] return {'percent': percent, 'section_breakdown': breakdown, @@ -250,6 +256,13 @@ class AssignmentFormatGrader(CourseGrader): show_only_average is to suppress the display of each assignment in this grader and instead only show the total score of this grader in the breakdown. + hide_average is to suppress the display of the total score in this grader and instead + only show each assignment in this grader in the breakdown. + + If there is only a single assignment in this grader, then it acts like a SingleSectionGrader + and returns only one entry for the grader. Since the assignment and the total are the same, + the total is returned but is not labeled as an average. + category should be presentable to the user, but may not appear. When the grade breakdown is displayed, scores from the same category will be similar (for example, by color). @@ -263,7 +276,8 @@ class AssignmentFormatGrader(CourseGrader): min_count = 2 would produce the labels "Assignment 3", "Assignment 4" """ - def __init__(self, type, min_count, drop_count, category=None, section_type=None, short_label=None, show_only_average=False, hide_average=False, starting_index=1): + def __init__(self, type, min_count, drop_count, category=None, section_type=None, short_label=None, + show_only_average=False, hide_average=False, starting_index=1): self.type = type self.min_count = min_count self.drop_count = drop_count @@ -275,7 +289,8 @@ class AssignmentFormatGrader(CourseGrader): self.hide_average = hide_average def grade(self, grade_sheet, generate_random_scores=False): - def totalWithDrops(breakdown, drop_count): + def total_with_drops(breakdown, drop_count): + '''calculates total score for a section while dropping lowest scores''' #create an array of tuples with (index, mark), sorted by mark['percent'] descending sorted_breakdown = sorted(enumerate(breakdown), key=lambda x: -x[1]['percent']) # A list of the indices of the dropped scores @@ -308,33 +323,50 @@ class AssignmentFormatGrader(CourseGrader): section_name = scores[i].section percentage = earned / float(possible) - summary = "{section_type} {index} - {name} - {percent:.0%} ({earned:.3n}/{possible:.3n})".format(index=i + self.starting_index, - section_type=self.section_type, - name=section_name, - percent=percentage, - earned=float(earned), - possible=float(possible)) + summary_format = "{section_type} {index} - {name} - {percent:.0%} ({earned:.3n}/{possible:.3n})" + summary = summary_format.format(index=i + self.starting_index, + section_type=self.section_type, + name=section_name, + percent=percentage, + earned=float(earned), + possible=float(possible)) else: percentage = 0 - summary = "{section_type} {index} Unreleased - 0% (?/?)".format(index=i + self.starting_index, section_type=self.section_type) + summary = "{section_type} {index} Unreleased - 0% (?/?)".format(index=i + self.starting_index, + section_type=self.section_type) - short_label = "{short_label} {index:02d}".format(index=i + self.starting_index, short_label=self.short_label) + short_label = "{short_label} {index:02d}".format(index=i + self.starting_index, + short_label=self.short_label) - breakdown.append({'percent': percentage, 'label': short_label, 'detail': summary, 'category': self.category}) + breakdown.append({'percent': percentage, 'label': short_label, + 'detail': summary, 'category': self.category}) - total_percent, dropped_indices = totalWithDrops(breakdown, self.drop_count) + total_percent, dropped_indices = total_with_drops(breakdown, self.drop_count) for dropped_index in dropped_indices: - breakdown[dropped_index]['mark'] = {'detail': "The lowest {drop_count} {section_type} scores are dropped.".format(drop_count=self.drop_count, section_type=self.section_type)} + breakdown[dropped_index]['mark'] = {'detail': "The lowest {drop_count} {section_type} scores are dropped." + .format(drop_count=self.drop_count, section_type=self.section_type)} - total_detail = "{section_type} Average = {percent:.0%}".format(percent=total_percent, section_type=self.section_type) - total_label = "{short_label} Avg".format(short_label=self.short_label) + if len(breakdown) == 1: + # if there is only one entry in a section, suppress the existing individual entry and the average, + # and just display a single entry for the section. That way it acts automatically like a + # SingleSectionGrader. + total_detail = "{section_type} = {percent:.0%}".format(percent=total_percent, + section_type=self.section_type) + total_label = "{short_label}".format(short_label=self.short_label) + breakdown = [{'percent': total_percent, 'label': total_label, + 'detail': total_detail, 'category': self.category, 'prominent': True}, ] + else: + total_detail = "{section_type} Average = {percent:.0%}".format(percent=total_percent, + section_type=self.section_type) + total_label = "{short_label} Avg".format(short_label=self.short_label) - if self.show_only_average: - breakdown = [] + if self.show_only_average: + breakdown = [] - if not self.hide_average: - breakdown.append({'percent': total_percent, 'label': total_label, 'detail': total_detail, 'category': self.category, 'prominent': True}) + if not self.hide_average: + breakdown.append({'percent': total_percent, 'label': total_label, + 'detail': total_detail, 'category': self.category, 'prominent': True}) return {'percent': total_percent, 'section_breakdown': breakdown, diff --git a/common/lib/xmodule/xmodule/tests/test_graders.py b/common/lib/xmodule/xmodule/tests/test_graders.py index 27416b1d5c..1a9ba50dc4 100644 --- a/common/lib/xmodule/xmodule/tests/test_graders.py +++ b/common/lib/xmodule/xmodule/tests/test_graders.py @@ -6,32 +6,34 @@ from xmodule.graders import Score, aggregate_scores class GradesheetTest(unittest.TestCase): + '''Tests the aggregate_scores method''' def test_weighted_grading(self): scores = [] Score.__sub__ = lambda me, other: (me.earned - other.earned) + (me.possible - other.possible) - all, graded = aggregate_scores(scores) - self.assertEqual(all, Score(earned=0, possible=0, graded=False, section="summary")) - self.assertEqual(graded, Score(earned=0, possible=0, graded=True, section="summary")) + all_total, graded_total = aggregate_scores(scores) + self.assertEqual(all_total, Score(earned=0, possible=0, graded=False, section="summary")) + self.assertEqual(graded_total, Score(earned=0, possible=0, graded=True, section="summary")) scores.append(Score(earned=0, possible=5, graded=False, section="summary")) - all, graded = aggregate_scores(scores) - self.assertEqual(all, Score(earned=0, possible=5, graded=False, section="summary")) - self.assertEqual(graded, Score(earned=0, possible=0, graded=True, section="summary")) + all_total, graded_total = aggregate_scores(scores) + self.assertEqual(all_total, Score(earned=0, possible=5, graded=False, section="summary")) + self.assertEqual(graded_total, Score(earned=0, possible=0, graded=True, section="summary")) scores.append(Score(earned=3, possible=5, graded=True, section="summary")) - all, graded = aggregate_scores(scores) - self.assertAlmostEqual(all, Score(earned=3, possible=10, graded=False, section="summary")) - self.assertAlmostEqual(graded, Score(earned=3, possible=5, graded=True, section="summary")) + all_total, graded_total = aggregate_scores(scores) + self.assertAlmostEqual(all_total, Score(earned=3, possible=10, graded=False, section="summary")) + self.assertAlmostEqual(graded_total, Score(earned=3, possible=5, graded=True, section="summary")) scores.append(Score(earned=2, possible=5, graded=True, section="summary")) - all, graded = aggregate_scores(scores) - self.assertAlmostEqual(all, Score(earned=5, possible=15, graded=False, section="summary")) - self.assertAlmostEqual(graded, Score(earned=5, possible=10, graded=True, section="summary")) + all_total, graded_total = aggregate_scores(scores) + self.assertAlmostEqual(all_total, Score(earned=5, possible=15, graded=False, section="summary")) + self.assertAlmostEqual(graded_total, Score(earned=5, possible=10, graded=True, section="summary")) class GraderTest(unittest.TestCase): + '''Tests grader implementations''' empty_gradesheet = { } @@ -44,136 +46,152 @@ class GraderTest(unittest.TestCase): test_gradesheet = { 'Homework': [Score(earned=2, possible=20.0, graded=True, section='hw1'), - Score(earned=16, possible=16.0, graded=True, section='hw2')], - #The dropped scores should be from the assignments that don't exist yet + Score(earned=16, possible=16.0, graded=True, section='hw2')], + # The dropped scores should be from the assignments that don't exist yet 'Lab': [Score(earned=1, possible=2.0, graded=True, section='lab1'), # Dropped - Score(earned=1, possible=1.0, graded=True, section='lab2'), - Score(earned=1, possible=1.0, graded=True, section='lab3'), - Score(earned=5, possible=25.0, graded=True, section='lab4'), # Dropped - Score(earned=3, possible=4.0, graded=True, section='lab5'), # Dropped - Score(earned=6, possible=7.0, graded=True, section='lab6'), - Score(earned=5, possible=6.0, graded=True, section='lab7')], + Score(earned=1, possible=1.0, graded=True, section='lab2'), + Score(earned=1, possible=1.0, graded=True, section='lab3'), + Score(earned=5, possible=25.0, graded=True, section='lab4'), # Dropped + Score(earned=3, possible=4.0, graded=True, section='lab5'), # Dropped + Score(earned=6, possible=7.0, graded=True, section='lab6'), + Score(earned=5, possible=6.0, graded=True, section='lab7')], 'Midterm': [Score(earned=50.5, possible=100, graded=True, section="Midterm Exam"), ], } - def test_SingleSectionGrader(self): - midtermGrader = graders.SingleSectionGrader("Midterm", "Midterm Exam") - lab4Grader = graders.SingleSectionGrader("Lab", "lab4") - badLabGrader = graders.SingleSectionGrader("Lab", "lab42") + def test_single_section_grader(self): + midterm_grader = graders.SingleSectionGrader("Midterm", "Midterm Exam") + lab4_grader = graders.SingleSectionGrader("Lab", "lab4") + bad_lab_grader = graders.SingleSectionGrader("Lab", "lab42") - for graded in [midtermGrader.grade(self.empty_gradesheet), - midtermGrader.grade(self.incomplete_gradesheet), - badLabGrader.grade(self.test_gradesheet)]: + for graded in [midterm_grader.grade(self.empty_gradesheet), + midterm_grader.grade(self.incomplete_gradesheet), + bad_lab_grader.grade(self.test_gradesheet)]: self.assertEqual(len(graded['section_breakdown']), 1) self.assertEqual(graded['percent'], 0.0) - graded = midtermGrader.grade(self.test_gradesheet) + graded = midterm_grader.grade(self.test_gradesheet) self.assertAlmostEqual(graded['percent'], 0.505) self.assertEqual(len(graded['section_breakdown']), 1) - graded = lab4Grader.grade(self.test_gradesheet) + graded = lab4_grader.grade(self.test_gradesheet) self.assertAlmostEqual(graded['percent'], 0.2) self.assertEqual(len(graded['section_breakdown']), 1) - def test_AssignmentFormatGrader(self): - homeworkGrader = graders.AssignmentFormatGrader("Homework", 12, 2) - noDropGrader = graders.AssignmentFormatGrader("Homework", 12, 0) - #Even though the minimum number is 3, this should grade correctly when 7 assignments are found - overflowGrader = graders.AssignmentFormatGrader("Lab", 3, 2) - labGrader = graders.AssignmentFormatGrader("Lab", 7, 3) + def test_assignment_format_grader(self): + homework_grader = graders.AssignmentFormatGrader("Homework", 12, 2) + no_drop_grader = graders.AssignmentFormatGrader("Homework", 12, 0) + # Even though the minimum number is 3, this should grade correctly when 7 assignments are found + overflow_grader = graders.AssignmentFormatGrader("Lab", 3, 2) + lab_grader = graders.AssignmentFormatGrader("Lab", 7, 3) - #Test the grading of an empty gradesheet - for graded in [homeworkGrader.grade(self.empty_gradesheet), - noDropGrader.grade(self.empty_gradesheet), - homeworkGrader.grade(self.incomplete_gradesheet), - noDropGrader.grade(self.incomplete_gradesheet)]: + # Test the grading of an empty gradesheet + for graded in [homework_grader.grade(self.empty_gradesheet), + no_drop_grader.grade(self.empty_gradesheet), + homework_grader.grade(self.incomplete_gradesheet), + no_drop_grader.grade(self.incomplete_gradesheet)]: self.assertAlmostEqual(graded['percent'], 0.0) - #Make sure the breakdown includes 12 sections, plus one summary + # Make sure the breakdown includes 12 sections, plus one summary self.assertEqual(len(graded['section_breakdown']), 12 + 1) - graded = homeworkGrader.grade(self.test_gradesheet) + graded = homework_grader.grade(self.test_gradesheet) self.assertAlmostEqual(graded['percent'], 0.11) # 100% + 10% / 10 assignments self.assertEqual(len(graded['section_breakdown']), 12 + 1) - graded = noDropGrader.grade(self.test_gradesheet) + graded = no_drop_grader.grade(self.test_gradesheet) self.assertAlmostEqual(graded['percent'], 0.0916666666666666) # 100% + 10% / 12 assignments self.assertEqual(len(graded['section_breakdown']), 12 + 1) - graded = overflowGrader.grade(self.test_gradesheet) + graded = overflow_grader.grade(self.test_gradesheet) self.assertAlmostEqual(graded['percent'], 0.8880952380952382) # 100% + 10% / 5 assignments self.assertEqual(len(graded['section_breakdown']), 7 + 1) - graded = labGrader.grade(self.test_gradesheet) + graded = lab_grader.grade(self.test_gradesheet) self.assertAlmostEqual(graded['percent'], 0.9226190476190477) self.assertEqual(len(graded['section_breakdown']), 7 + 1) - def test_WeightedSubsectionsGrader(self): - #First, a few sub graders - homeworkGrader = graders.AssignmentFormatGrader("Homework", 12, 2) - labGrader = graders.AssignmentFormatGrader("Lab", 7, 3) - midtermGrader = graders.SingleSectionGrader("Midterm", "Midterm Exam") + def test_assignment_format_grader_on_single_section_entry(self): + midterm_grader = graders.AssignmentFormatGrader("Midterm", 1, 0) + # Test the grading on a section with one item: + for graded in [midterm_grader.grade(self.empty_gradesheet), + midterm_grader.grade(self.incomplete_gradesheet)]: + self.assertAlmostEqual(graded['percent'], 0.0) + # Make sure the breakdown includes just the one summary + self.assertEqual(len(graded['section_breakdown']), 0 + 1) + self.assertEqual(graded['section_breakdown'][0]['label'], 'Midterm') - weightedGrader = graders.WeightedSubsectionsGrader([(homeworkGrader, homeworkGrader.category, 0.25), - (labGrader, labGrader.category, 0.25), - (midtermGrader, midtermGrader.category, 0.5)]) + graded = midterm_grader.grade(self.test_gradesheet) + self.assertAlmostEqual(graded['percent'], 0.505) + self.assertEqual(len(graded['section_breakdown']), 0 + 1) - overOneWeightsGrader = graders.WeightedSubsectionsGrader([(homeworkGrader, homeworkGrader.category, 0.5), - (labGrader, labGrader.category, 0.5), - (midtermGrader, midtermGrader.category, 0.5)]) + def test_weighted_subsections_grader(self): + # First, a few sub graders + homework_grader = graders.AssignmentFormatGrader("Homework", 12, 2) + lab_grader = graders.AssignmentFormatGrader("Lab", 7, 3) + # phasing out the use of SingleSectionGraders, and instead using AssignmentFormatGraders that + # will act like SingleSectionGraders on single sections. + midterm_grader = graders.AssignmentFormatGrader("Midterm", 1, 0) - #The midterm should have all weight on this one - zeroWeightsGrader = graders.WeightedSubsectionsGrader([(homeworkGrader, homeworkGrader.category, 0.0), - (labGrader, labGrader.category, 0.0), - (midtermGrader, midtermGrader.category, 0.5)]) + weighted_grader = graders.WeightedSubsectionsGrader([(homework_grader, homework_grader.category, 0.25), + (lab_grader, lab_grader.category, 0.25), + (midterm_grader, midterm_grader.category, 0.5)]) - #This should always have a final percent of zero - allZeroWeightsGrader = graders.WeightedSubsectionsGrader([(homeworkGrader, homeworkGrader.category, 0.0), - (labGrader, labGrader.category, 0.0), - (midtermGrader, midtermGrader.category, 0.0)]) + over_one_weights_grader = graders.WeightedSubsectionsGrader([(homework_grader, homework_grader.category, 0.5), + (lab_grader, lab_grader.category, 0.5), + (midterm_grader, midterm_grader.category, 0.5)]) - emptyGrader = graders.WeightedSubsectionsGrader([]) + # The midterm should have all weight on this one + zero_weights_grader = graders.WeightedSubsectionsGrader([(homework_grader, homework_grader.category, 0.0), + (lab_grader, lab_grader.category, 0.0), + (midterm_grader, midterm_grader.category, 0.5)]) - graded = weightedGrader.grade(self.test_gradesheet) + # This should always have a final percent of zero + all_zero_weights_grader = graders.WeightedSubsectionsGrader([(homework_grader, homework_grader.category, 0.0), + (lab_grader, lab_grader.category, 0.0), + (midterm_grader, midterm_grader.category, 0.0)]) + + empty_grader = graders.WeightedSubsectionsGrader([]) + + graded = weighted_grader.grade(self.test_gradesheet) self.assertAlmostEqual(graded['percent'], 0.5106547619047619) self.assertEqual(len(graded['section_breakdown']), (12 + 1) + (7 + 1) + 1) self.assertEqual(len(graded['grade_breakdown']), 3) - graded = overOneWeightsGrader.grade(self.test_gradesheet) + graded = over_one_weights_grader.grade(self.test_gradesheet) self.assertAlmostEqual(graded['percent'], 0.7688095238095238) self.assertEqual(len(graded['section_breakdown']), (12 + 1) + (7 + 1) + 1) self.assertEqual(len(graded['grade_breakdown']), 3) - graded = zeroWeightsGrader.grade(self.test_gradesheet) + graded = zero_weights_grader.grade(self.test_gradesheet) self.assertAlmostEqual(graded['percent'], 0.2525) self.assertEqual(len(graded['section_breakdown']), (12 + 1) + (7 + 1) + 1) self.assertEqual(len(graded['grade_breakdown']), 3) - graded = allZeroWeightsGrader.grade(self.test_gradesheet) + graded = all_zero_weights_grader.grade(self.test_gradesheet) self.assertAlmostEqual(graded['percent'], 0.0) self.assertEqual(len(graded['section_breakdown']), (12 + 1) + (7 + 1) + 1) self.assertEqual(len(graded['grade_breakdown']), 3) - for graded in [weightedGrader.grade(self.empty_gradesheet), - weightedGrader.grade(self.incomplete_gradesheet), - zeroWeightsGrader.grade(self.empty_gradesheet), - allZeroWeightsGrader.grade(self.empty_gradesheet)]: + for graded in [weighted_grader.grade(self.empty_gradesheet), + weighted_grader.grade(self.incomplete_gradesheet), + zero_weights_grader.grade(self.empty_gradesheet), + all_zero_weights_grader.grade(self.empty_gradesheet)]: self.assertAlmostEqual(graded['percent'], 0.0) self.assertEqual(len(graded['section_breakdown']), (12 + 1) + (7 + 1) + 1) self.assertEqual(len(graded['grade_breakdown']), 3) - graded = emptyGrader.grade(self.test_gradesheet) + graded = empty_grader.grade(self.test_gradesheet) self.assertAlmostEqual(graded['percent'], 0.0) self.assertEqual(len(graded['section_breakdown']), 0) self.assertEqual(len(graded['grade_breakdown']), 0) - def test_graderFromConf(self): + def test_grader_from_conf(self): - #Confs always produce a graders.WeightedSubsectionsGrader, so we test this by repeating the test - #in test_graders.WeightedSubsectionsGrader, but generate the graders with confs. + # Confs always produce a graders.WeightedSubsectionsGrader, so we test this by repeating the test + # in test_graders.WeightedSubsectionsGrader, but generate the graders with confs. - weightedGrader = graders.grader_from_conf([ + weighted_grader = graders.grader_from_conf([ { 'type': "Homework", 'min_count': 12, @@ -196,25 +214,25 @@ class GraderTest(unittest.TestCase): }, ]) - emptyGrader = graders.grader_from_conf([]) + empty_grader = graders.grader_from_conf([]) - graded = weightedGrader.grade(self.test_gradesheet) + graded = weighted_grader.grade(self.test_gradesheet) self.assertAlmostEqual(graded['percent'], 0.5106547619047619) self.assertEqual(len(graded['section_breakdown']), (12 + 1) + (7 + 1) + 1) self.assertEqual(len(graded['grade_breakdown']), 3) - graded = emptyGrader.grade(self.test_gradesheet) + graded = empty_grader.grade(self.test_gradesheet) self.assertAlmostEqual(graded['percent'], 0.0) self.assertEqual(len(graded['section_breakdown']), 0) self.assertEqual(len(graded['grade_breakdown']), 0) - #Test that graders can also be used instead of lists of dictionaries - homeworkGrader = graders.AssignmentFormatGrader("Homework", 12, 2) - homeworkGrader2 = graders.grader_from_conf(homeworkGrader) + # Test that graders can also be used instead of lists of dictionaries + homework_grader = graders.AssignmentFormatGrader("Homework", 12, 2) + homework_grader2 = graders.grader_from_conf(homework_grader) - graded = homeworkGrader2.grade(self.test_gradesheet) + graded = homework_grader2.grade(self.test_gradesheet) self.assertAlmostEqual(graded['percent'], 0.11) self.assertEqual(len(graded['section_breakdown']), 12 + 1) - #TODO: How do we test failure cases? The parser only logs an error when - #it can't parse something. Maybe it should throw exceptions? + # TODO: How do we test failure cases? The parser only logs an error when + # it can't parse something. Maybe it should throw exceptions?