Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
3860f333
Commit
3860f333
authored
Apr 16, 2012
by
Bridger Maxwell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added parser for creating graders from a dictionary representation.
parent
795fba04
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
123 additions
and
46 deletions
+123
-46
djangoapps/courseware/grades.py
+36
-6
djangoapps/courseware/tests.py
+87
-40
No files found.
djangoapps/courseware/grades.py
View file @
3860f333
...
...
@@ -59,6 +59,36 @@ class CourseGrader(object):
def
grade
(
self
,
grade_sheet
):
raise
NotImplementedError
@classmethod
def
graderFromConf
(
cls
,
conf
):
if
isinstance
(
conf
,
CourseGrader
):
return
conf
subgraders
=
[]
for
subgraderconf
in
conf
:
subgraderconf
=
subgraderconf
.
copy
()
weight
=
subgraderconf
.
pop
(
"weight"
,
0
)
try
:
if
'min_count'
in
subgraderconf
:
#This is an AssignmentFormatGrader
subgrader
=
AssignmentFormatGrader
(
**
subgraderconf
)
subgraders
.
append
(
(
subgrader
,
subgrader
.
category
,
weight
)
)
elif
'section_name'
in
subgraderconf
:
#This is an SingleSectionGrader
subgrader
=
SingleSectionGrader
(
**
subgraderconf
)
subgraders
.
append
(
(
subgrader
,
subgrader
.
category
,
weight
)
)
else
:
raise
ValueError
(
"Configuration has no appropriate grader class."
)
except
TypeError
as
error
:
log
.
error
(
"Unable to parse grader configuration:
\n
"
+
subgraderconf
+
"
\n
Error was:
\n
"
+
error
)
except
ValueError
as
error
:
log
.
error
(
"Unable to parse grader configuration:
\n
"
+
subgraderconf
+
"
\n
Error was:
\n
"
+
error
)
return
WeightedSubsectionsGrader
(
subgraders
)
class
WeightedSubsectionsGrader
(
CourseGrader
):
"""
This grader takes a list of tuples containing (grader, category_name, weight) and computes
...
...
@@ -153,9 +183,9 @@ class AssignmentFormatGrader(CourseGrader):
sections in this format must be specified (even if those sections haven't been
written yet).
min_
number
defines how many assignments are expected throughout the course. Placeholder
scores (of 0) will be inserted if the number of matching sections in the course is < min_
number
.
If there number of matching sections in the course is > min_
number, min_number
will be ignored.
min_
count
defines how many assignments are expected throughout the course. Placeholder
scores (of 0) will be inserted if the number of matching sections in the course is < min_
count
.
If there number of matching sections in the course is > min_
count, min_count
will be ignored.
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).
...
...
@@ -167,9 +197,9 @@ class AssignmentFormatGrader(CourseGrader):
"HW".
"""
def
__init__
(
self
,
course_format
,
min_
number
,
drop_count
,
category
=
None
,
section_type
=
None
,
short_label
=
None
):
def
__init__
(
self
,
course_format
,
min_
count
,
drop_count
,
category
=
None
,
section_type
=
None
,
short_label
=
None
):
self
.
course_format
=
course_format
self
.
min_
number
=
min_number
self
.
min_
count
=
min_count
self
.
drop_count
=
drop_count
self
.
category
=
category
or
self
.
course_format
self
.
section_type
=
section_type
or
self
.
course_format
...
...
@@ -196,7 +226,7 @@ class AssignmentFormatGrader(CourseGrader):
#Figure the homework scores
scores
=
grade_sheet
.
get
(
self
.
course_format
,
[])
breakdown
=
[]
for
i
in
range
(
max
(
self
.
min_
number
,
len
(
scores
))
):
for
i
in
range
(
max
(
self
.
min_
count
,
len
(
scores
))
):
if
i
<
len
(
scores
):
percentage
=
scores
[
i
]
.
earned
/
float
(
scores
[
i
]
.
possible
)
summary
=
"{section_type} {index} - {name} - {percent:.0
%
} ({earned:.3n}/{possible:.3n})"
.
format
(
index
=
i
+
1
,
...
...
djangoapps/courseware/tests.py
View file @
3860f333
...
...
@@ -4,7 +4,7 @@ import numpy
import
courseware.modules
import
courseware.capa.calc
as
calc
from
grades
import
Score
,
aggregate_scores
,
WeightedSubsectionsGrader
,
SingleSectionGrader
,
AssignmentFormatGrader
from
grades
import
Score
,
aggregate_scores
,
CourseGrader
,
WeightedSubsectionsGrader
,
SingleSectionGrader
,
AssignmentFormatGrader
class
ModelsTest
(
unittest
.
TestCase
):
def
setUp
(
self
):
...
...
@@ -140,6 +140,41 @@ class GraderTest(unittest.TestCase):
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.2
)
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
1
)
def
test_assignmentFormatGrader
(
self
):
homeworkGrader
=
AssignmentFormatGrader
(
"Homework"
,
12
,
2
)
noDropGrader
=
AssignmentFormatGrader
(
"Homework"
,
12
,
0
)
#Even though the minimum number is 3, this should grade correctly when 7 assignments are found
overflowGrader
=
AssignmentFormatGrader
(
"Lab"
,
3
,
2
)
labGrader
=
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
)
]:
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.0
)
#Make sure the breakdown includes 12 sections, plus one summary
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
12
+
1
)
graded
=
homeworkGrader
.
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
)
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.0916666666666666
)
# 100% + 10% / 12 assignments
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
12
+
1
)
graded
=
overflowGrader
.
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
)
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.9226190476190477
)
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
7
+
1
)
def
test_WeightedSubsectionsGrader
(
self
):
#First, a few sub graders
homeworkGrader
=
AssignmentFormatGrader
(
"Homework"
,
12
,
2
)
...
...
@@ -160,6 +195,7 @@ class GraderTest(unittest.TestCase):
allZeroWeightsGrader
=
WeightedSubsectionsGrader
(
[(
homeworkGrader
,
homeworkGrader
.
category
,
0.0
),
(
labGrader
,
labGrader
.
category
,
0.0
),
(
midtermGrader
,
midtermGrader
.
category
,
0.0
)]
)
emptyGrader
=
WeightedSubsectionsGrader
(
[]
)
graded
=
weightedGrader
.
grade
(
self
.
test_gradesheet
)
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.5106547619047619
)
...
...
@@ -187,53 +223,64 @@ class GraderTest(unittest.TestCase):
zeroWeightsGrader
.
grade
(
self
.
empty_gradesheet
),
allZeroWeightsGrader
.
grade
(
self
.
empty_gradesheet
)]:
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.0
)
#section_breakdown should have all subsections from before
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
(
12
+
1
)
+
(
7
+
1
)
+
1
)
self
.
assertEqual
(
len
(
graded
[
'grade_breakdown'
]),
3
)
graded
=
emptyGrader
.
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_assignmentFormatGrader
(
self
):
homeworkGrader
=
AssignmentFormatGrader
(
"Homework"
,
12
,
2
)
noDropGrader
=
AssignmentFormatGrader
(
"Homework"
,
12
,
0
)
#Even though the minimum number is 3, this should grade correctly when 7 assignments are found
overflowGrader
=
AssignmentFormatGrader
(
"Lab"
,
3
,
2
)
labGrader
=
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
)
]:
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.0
)
#Make sure the breakdown includes 12 sections, plus one summary
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
12
+
1
)
graded
=
homeworkGrader
.
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
)
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.0916666666666666
)
# 100% + 10% / 12 assignments
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
12
+
1
)
graded
=
overflowGrader
.
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
)
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.9226190476190477
)
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
7
+
1
)
def
test_graderFromConf
(
self
):
#Confs always produce a WeightedSubsectionsGrader, so we test this by repeating the test
#in test_WeightedSubsectionsGrader, but generate the graders with confs.
weightedGrader
=
CourseGrader
.
graderFromConf
([
{
'course_format'
:
"Homework"
,
'min_count'
:
12
,
'drop_count'
:
2
,
'short_label'
:
"HW"
,
'weight'
:
0.25
,
},
{
'course_format'
:
"Lab"
,
'min_count'
:
7
,
'drop_count'
:
3
,
'category'
:
"Labs"
,
'weight'
:
0.25
},
{
'section_format'
:
"Midterm"
,
'section_name'
:
"Midterm Exam"
,
'short_label'
:
"Midterm"
,
'weight'
:
0.5
,
},
])
emptyGrader
=
CourseGrader
.
graderFromConf
([])
graded
=
weightedGrader
.
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
)
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
=
AssignmentFormatGrader
(
"Homework"
,
12
,
2
)
homeworkGrader2
=
CourseGrader
.
graderFromConf
(
homeworkGrader
)
graded
=
homeworkGrader2
.
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?
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment