Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d26bbd0
Added print statements for determining how to add in-progress grade.
Jul 8, 2013
6711120
More experimenting
Jul 8, 2013
6559f17
More more experimenting
Jul 8, 2013
788098d
More more more experiment
Jul 8, 2013
913bebc
Now have number for in-progress grade
Jul 8, 2013
73c7ebf
Added attempted_percent
Jul 8, 2013
2a16044
Working again
Jul 8, 2013
4176be5
Graphing happens correctly, with tooltips. End of first prototype.
fephsun Jul 8, 2013
71d2fb6
Refactored, removed print statements and other cruft. Projected-grade…
Jul 8, 2013
82701dd
Removed a couple of print statements
Jul 9, 2013
0f60a57
Tiny formatting change
Jul 9, 2013
9468f2c
Refactored some of the code in grade() to helper functions.
Jul 9, 2013
c23934a
Added test for grades.py to make sure that the grader plays nice with…
fephsun Jul 9, 2013
f6df687
Fixed KeyError bug
Jul 9, 2013
2b751ae
More refactoring.
Jul 9, 2013
dda9cad
Added TestFindShouldGradeSection. All tests currently pass.
Jul 9, 2013
36c4ccb
Added docstring to find_should_grade_section helper method
Jul 9, 2013
5606ef8
Added TestFindAttempted, all tests currently pass.
Jul 9, 2013
84da9c5
added docstring for find_attempted
Jul 9, 2013
6e5ed30
Rebase fix.
fephsun Jul 9, 2013
c799929
Merge branch 'csvoss/refactor-grades' of github.com:edx/edx-platform …
Jul 9, 2013
a642663
Rebase fix.
fephsun Jul 9, 2013
39d0f55
Added docstring to TestFindAttempted
Jul 9, 2013
682f6c9
Removed old argument related to projected grades`
Jul 9, 2013
c071ea3
Reverted accidental deletion of line.
Jul 9, 2013
77e9b33
Merge branch 'csvoss/refactor-grades' of github.com:edx/edx-platform …
Jul 9, 2013
140912c
Added test for grades.grade
fephsun Jul 9, 2013
a41c36e
Added TestGetScore with first few methods. Deleted old print statemen…
Jul 9, 2013
9fc960c
Finished with TestFindAttempted, all tests pass.
Jul 9, 2013
50531fb
Merge branch 'csvoss/refactor-grades' of github.com:edx/edx-platform …
Jul 9, 2013
4d2c828
Fixed a test in xmodule.graders.
fephsun Jul 9, 2013
963a494
Merge branch 'csvoss/refactor-grades' of github.com:edx/edx-platform …
Jul 9, 2013
f4e2ab7
Added docstring to compute_graded_total
Jul 9, 2013
06e186b
Potential fix for dropping-lowest issue.
Jul 10, 2013
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 111 additions & 32 deletions common/lib/xmodule/xmodule/graders.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@

# This is a tuple for holding scores, either from problems or sections.
# Section either indicates the name of the problem or the name of the section
Score = namedtuple("Score", "earned possible graded section")
ScoreT = namedtuple("Score", "earned possible graded section attempted")


def Score(earned, possible, graded, section, attempted=True):
return ScoreT(earned, possible, graded, section, attempted)


def aggregate_scores(scores, section_name="summary"):
Expand All @@ -26,16 +30,25 @@ def aggregate_scores(scores, section_name="summary"):
total_correct = sum(score.earned for score in scores)
total_possible = sum(score.possible for score in scores)

any_attempted = True in (score.attempted for score in scores)
any_attempted_graded = True in (score.attempted for score in scores if score.graded)

#regardless of whether or not it is graded
all_total = Score(total_correct,
total_possible,
False,
section_name)
all_total = Score(
total_correct,
total_possible,
False,
section_name,
any_attempted
)
#selecting only graded things
graded_total = Score(total_correct_graded,
total_possible_graded,
True,
section_name)
graded_total = Score(
total_correct_graded,
total_possible_graded,
True,
section_name,
any_attempted_graded
)

return all_total, graded_total

Expand Down Expand Up @@ -176,22 +189,46 @@ def __init__(self, sections):

def grade(self, grade_sheet, generate_random_scores=False):
total_percent = 0.0
total_weight = 0.0
section_breakdown = []
grade_breakdown = []

total_projected_percent = 0.0

for subgrader, category, weight in self.sections:
subgrade_result = subgrader.grade(grade_sheet, generate_random_scores)

weighted_percent = subgrade_result['percent'] * weight
section_detail = "{0} = {1:.1%} of a possible {2:.0%}".format(category, weighted_percent, weight)

if category in grade_sheet:
attempted_overall = True in (score.attempted for score in grade_sheet[category])
if attempted_overall:
total_weight += weight

weighted_projected_percent = subgrade_result['projected_percent'] * weight
total_projected_percent += weighted_projected_percent

total_percent += weighted_percent
section_breakdown += subgrade_result['section_breakdown']
grade_breakdown.append({'percent': weighted_percent, 'detail': section_detail, 'category': category})
grade_breakdown.append({
'percent': weighted_percent,
'detail': section_detail,
'category': category,
'projected_percent': weighted_projected_percent,
})

if total_weight == 0:
projected_percent = 0
else:
projected_percent = total_projected_percent / total_weight

return {'percent': total_percent,
'section_breakdown': section_breakdown,
'grade_breakdown': grade_breakdown}
return {
'percent': total_percent,
'section_breakdown': section_breakdown,
'grade_breakdown': grade_breakdown,
'projected_percent': projected_percent,
}


class SingleSectionGrader(CourseGrader):
Expand Down Expand Up @@ -219,27 +256,44 @@ def grade(self, grade_sheet, generate_random_scores=False):
if generate_random_scores: # for debugging!
earned = random.randint(2, 15)
possible = random.randint(earned, 15)
attempted = True
else: # We found the score
earned = found_score.earned
possible = found_score.possible
attempted = found_score.attempted

percent = earned / float(possible)
detail = "{name} - {percent:.0%} ({earned:.3n}/{possible:.3n})".format(name=self.name,
percent=percent,
earned=float(earned),
possible=float(possible))
detail = "{name} - {percent:.0%} ({earned:.3n}/{possible:.3n})".format(
name=self.name,
percent=percent,
earned=float(earned),
possible=float(possible)
)
if attempted:
projected_percent = percent
else:
projected_percent = None

else:
percent = 0.0
detail = "{name} - 0% (?/?)".format(name=self.name)
projected_percent = None

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,
'projected_percent': projected_percent,
}]

return {'percent': percent,
'section_breakdown': breakdown,
#No grade_breakdown here
}
return {
'percent': percent,
'section_breakdown': breakdown,
#No grade_breakdown here
'projected_percent': projected_percent,
}


class AssignmentFormatGrader(CourseGrader):
Expand Down Expand Up @@ -310,17 +364,20 @@ def total_with_drops(breakdown, drop_count):
#Figure the homework scores
scores = grade_sheet.get(self.type, [])
breakdown = []
projected_breakdown = []
for i in range(max(self.min_count, len(scores))):
if i < len(scores) or generate_random_scores:
if generate_random_scores: # for debugging!
earned = random.randint(2, 15)
possible = random.randint(earned, 15)
section_name = "Generated"
attempted = True

else:
earned = scores[i].earned
possible = scores[i].possible
section_name = scores[i].section
attempted = scores[i].attempted

percentage = earned / float(possible)
summary_format = "{section_type} {index} - {name} - {percent:.0%} ({earned:.3n}/{possible:.3n})"
Expand All @@ -330,16 +387,31 @@ def total_with_drops(breakdown, drop_count):
percent=percentage,
earned=float(earned),
possible=float(possible))
if attempted:
projected_percentage = percentage
else:
#TODO -- if the deadline has passed, then projected_percentage should be 0 and not None; else None
projected_percentage = None
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
)
projected_percentage = None

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})
'detail': summary, 'category': self.category, })
if projected_percentage is not None:
projected_breakdown.append({'percent': projected_percentage, 'label': short_label,
'detail': summary, 'category': self.category, })

drop_for_projected = max(len(projected_breakdown) - len(breakdown) + self.drop_count, 0)

projected_total_percent, _ = total_with_drops(projected_breakdown, drop_for_projected)

total_percent, dropped_indices = total_with_drops(breakdown, self.drop_count)

Expand All @@ -353,22 +425,29 @@ def total_with_drops(breakdown, drop_count):
# SingleSectionGrader.
total_detail = "{section_type} = {percent:.0%}".format(percent=total_percent,
section_type=self.section_type)
projected_detail = total_detail
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)
projected_detail = "Projected {section_type} Average = {percent:.0%}".format(percent=projected_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 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,
#No grade_breakdown here
}
'detail': total_detail, 'projected_detail': projected_detail,
'category': self.category, 'prominent': True,
'projected_percent': projected_total_percent})

return {
'percent': total_percent,
'section_breakdown': breakdown,
#No grade_breakdown here
'projected_percent': projected_total_percent,
}
14 changes: 7 additions & 7 deletions common/lib/xmodule/xmodule/tests/test_graders.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,20 @@ def test_weighted_grading(self):
Score.__sub__ = lambda me, other: (me.earned - other.earned) + (me.possible - other.possible)

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"))
self.assertEqual(all_total, Score(earned=0, possible=0, graded=False, section="summary", attempted=False))
self.assertEqual(graded_total, Score(earned=0, possible=0, graded=True, section="summary", attempted=False))

scores.append(Score(earned=0, possible=5, graded=False, section="summary"))
scores.append(Score(earned=0, possible=5, graded=False, section="summary", attempted=False))
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"))
self.assertEqual(all_total, Score(earned=0, possible=5, graded=False, section="summary", attempted=False))
self.assertEqual(graded_total, Score(earned=0, possible=0, graded=True, section="summary", attempted=False))

scores.append(Score(earned=3, possible=5, graded=True, section="summary"))
scores.append(Score(earned=3, possible=5, graded=True, section="summary", attempted=True))
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"))
scores.append(Score(earned=2, possible=5, graded=True, section="summary", attempted=True))
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"))
Expand Down
Loading