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
bf421108
Commit
bf421108
authored
Aug 25, 2016
by
Eric Fischer
Committed by
GitHub
Aug 25, 2016
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #13295 from edx/efischer/opt_grade
Grades cleanup
parents
fb20312e
13687e47
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
46 additions
and
43 deletions
+46
-43
common/lib/xmodule/xmodule/graders.py
+4
-15
lms/djangoapps/grades/models.py
+11
-12
lms/djangoapps/grades/new/subsection_grade.py
+9
-5
lms/djangoapps/grades/tests/test_models.py
+3
-3
lms/djangoapps/grades/tests/test_new.py
+5
-4
lms/djangoapps/grades/tests/test_signals.py
+4
-4
lms/djangoapps/grades/tests/utils.py
+10
-0
No files found.
common/lib/xmodule/xmodule/graders.py
View file @
bf421108
...
@@ -40,22 +40,11 @@ def aggregate_scores(scores, section_name="summary", location=None):
...
@@ -40,22 +40,11 @@ def aggregate_scores(scores, section_name="summary", location=None):
total_correct
=
float_sum
(
score
.
earned
for
score
in
scores
)
total_correct
=
float_sum
(
score
.
earned
for
score
in
scores
)
total_possible
=
float_sum
(
score
.
possible
for
score
in
scores
)
total_possible
=
float_sum
(
score
.
possible
for
score
in
scores
)
#regardless of whether or not it is graded
#regardless of whether it is graded
all_total
=
Score
(
all_total
=
Score
(
total_correct
,
total_possible
,
False
,
section_name
,
location
)
total_correct
,
total_possible
,
False
,
section_name
,
location
,
)
#selecting only graded things
#selecting only graded things
graded_total
=
Score
(
graded_total
=
Score
(
total_correct_graded
,
total_possible_graded
,
True
,
section_name
,
location
)
total_correct_graded
,
total_possible_graded
,
True
,
section_name
,
location
,
)
return
all_total
,
graded_total
return
all_total
,
graded_total
...
...
lms/djangoapps/grades/models.py
View file @
bf421108
...
@@ -12,7 +12,7 @@ import json
...
@@ -12,7 +12,7 @@ import json
import
logging
import
logging
from
operator
import
attrgetter
from
operator
import
attrgetter
from
django.db
import
models
from
django.db
import
models
,
transaction
from
django.db.utils
import
IntegrityError
from
django.db.utils
import
IntegrityError
from
model_utils.models
import
TimeStampedModel
from
model_utils.models
import
TimeStampedModel
...
@@ -107,7 +107,8 @@ class VisibleBlocksQuerySet(models.QuerySet):
...
@@ -107,7 +107,8 @@ class VisibleBlocksQuerySet(models.QuerySet):
blocks
=
BlockRecordSet
(
blocks
)
blocks
=
BlockRecordSet
(
blocks
)
try
:
try
:
model
=
self
.
create_from_blockrecords
(
blocks
)
with
transaction
.
atomic
():
model
=
self
.
create_from_blockrecords
(
blocks
)
except
IntegrityError
:
except
IntegrityError
:
# If an integrity error occurs, the VisibleBlocks model we want to
# If an integrity error occurs, the VisibleBlocks model we want to
# create already exists. The hash is still the correct value.
# create already exists. The hash is still the correct value.
...
@@ -172,13 +173,10 @@ class PersistentSubsectionGradeQuerySet(models.QuerySet):
...
@@ -172,13 +173,10 @@ class PersistentSubsectionGradeQuerySet(models.QuerySet):
kwargs
[
'course_id'
]
=
kwargs
[
'usage_key'
]
.
course_key
kwargs
[
'course_id'
]
=
kwargs
[
'usage_key'
]
.
course_key
visible_blocks_hash
=
VisibleBlocks
.
objects
.
hash_from_blockrecords
(
blocks
=
visible_blocks
)
visible_blocks_hash
=
VisibleBlocks
.
objects
.
hash_from_blockrecords
(
blocks
=
visible_blocks
)
grade
=
self
.
model
(
return
super
(
PersistentSubsectionGradeQuerySet
,
self
)
.
create
(
visible_blocks_id
=
visible_blocks_hash
,
visible_blocks_id
=
visible_blocks_hash
,
**
kwargs
**
kwargs
)
)
grade
.
full_clean
()
grade
.
save
()
return
grade
class
PersistentSubsectionGrade
(
TimeStampedModel
):
class
PersistentSubsectionGrade
(
TimeStampedModel
):
...
@@ -244,12 +242,13 @@ class PersistentSubsectionGrade(TimeStampedModel):
...
@@ -244,12 +242,13 @@ class PersistentSubsectionGrade(TimeStampedModel):
user_id
=
kwargs
.
pop
(
'user_id'
)
user_id
=
kwargs
.
pop
(
'user_id'
)
usage_key
=
kwargs
.
pop
(
'usage_key'
)
usage_key
=
kwargs
.
pop
(
'usage_key'
)
try
:
try
:
grade
,
is_created
=
cls
.
objects
.
get_or_create
(
with
transaction
.
atomic
():
user_id
=
user_id
,
grade
,
is_created
=
cls
.
objects
.
get_or_create
(
course_id
=
usage_key
.
course_key
,
user_id
=
user_id
,
usage_key
=
usage_key
,
course_id
=
usage_key
.
course_key
,
defaults
=
kwargs
,
usage_key
=
usage_key
,
)
defaults
=
kwargs
,
)
except
IntegrityError
:
except
IntegrityError
:
cls
.
update_grade
(
user_id
=
user_id
,
usage_key
=
usage_key
,
**
kwargs
)
cls
.
update_grade
(
user_id
=
user_id
,
usage_key
=
usage_key
,
**
kwargs
)
else
:
else
:
...
...
lms/djangoapps/grades/new/subsection_grade.py
View file @
bf421108
...
@@ -44,11 +44,15 @@ class SubsectionGrade(object):
...
@@ -44,11 +44,15 @@ class SubsectionGrade(object):
"""
"""
Compute the grade of this subsection for the given student and course.
Compute the grade of this subsection for the given student and course.
"""
"""
for
descendant_key
in
course_structure
.
post_order_traversal
(
try
:
filter_func
=
possibly_scored
,
for
descendant_key
in
course_structure
.
post_order_traversal
(
start_node
=
self
.
location
,
filter_func
=
possibly_scored
,
):
start_node
=
self
.
location
,
self
.
_compute_block_score
(
student
,
descendant_key
,
course_structure
,
scores_client
,
submissions_scores
)
):
self
.
_compute_block_score
(
student
,
descendant_key
,
course_structure
,
scores_client
,
submissions_scores
)
finally
:
# self.scores may hold outdated data, force it to refresh on next access
lazy
.
invalidate
(
self
,
'scores'
)
self
.
all_total
,
self
.
graded_total
=
graders
.
aggregate_scores
(
self
.
scores
,
self
.
display_name
,
self
.
location
)
self
.
all_total
,
self
.
graded_total
=
graders
.
aggregate_scores
(
self
.
scores
,
self
.
display_name
,
self
.
location
)
...
...
lms/djangoapps/grades/tests/test_models.py
View file @
bf421108
...
@@ -8,7 +8,7 @@ from hashlib import sha1
...
@@ -8,7 +8,7 @@ from hashlib import sha1
import
json
import
json
from
mock
import
patch
from
mock
import
patch
from
django.
core.exceptions
import
Validation
Error
from
django.
db.utils
import
Integrity
Error
from
django.test
import
TestCase
from
django.test
import
TestCase
from
opaque_keys.edx.locator
import
CourseLocator
,
BlockUsageLocator
from
opaque_keys.edx.locator
import
CourseLocator
,
BlockUsageLocator
...
@@ -156,7 +156,7 @@ class PersistentSubsectionGradeTest(GradesModelTestCase):
...
@@ -156,7 +156,7 @@ class PersistentSubsectionGradeTest(GradesModelTestCase):
usage_key
=
self
.
params
[
"usage_key"
],
usage_key
=
self
.
params
[
"usage_key"
],
)
)
self
.
assertEqual
(
created_grade
,
read_grade
)
self
.
assertEqual
(
created_grade
,
read_grade
)
with
self
.
assertRaises
(
Validation
Error
):
with
self
.
assertRaises
(
Integrity
Error
):
created_grade
=
PersistentSubsectionGrade
.
objects
.
create
(
**
self
.
params
)
created_grade
=
PersistentSubsectionGrade
.
objects
.
create
(
**
self
.
params
)
def
test_create_bad_params
(
self
):
def
test_create_bad_params
(
self
):
...
@@ -164,7 +164,7 @@ class PersistentSubsectionGradeTest(GradesModelTestCase):
...
@@ -164,7 +164,7 @@ class PersistentSubsectionGradeTest(GradesModelTestCase):
Confirms create will fail if params are missing.
Confirms create will fail if params are missing.
"""
"""
del
self
.
params
[
"earned_graded"
]
del
self
.
params
[
"earned_graded"
]
with
self
.
assertRaises
(
Validation
Error
):
with
self
.
assertRaises
(
Integrity
Error
):
PersistentSubsectionGrade
.
objects
.
create
(
**
self
.
params
)
PersistentSubsectionGrade
.
objects
.
create
(
**
self
.
params
)
def
test_course_version_is_optional
(
self
):
def
test_course_version_is_optional
(
self
):
...
...
lms/djangoapps/grades/tests/test_new.py
View file @
bf421108
...
@@ -17,6 +17,7 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
...
@@ -17,6 +17,7 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
from
..models
import
PersistentSubsectionGrade
from
..models
import
PersistentSubsectionGrade
from
..new.course_grade
import
CourseGradeFactory
from
..new.course_grade
import
CourseGradeFactory
from
..new.subsection_grade
import
SubsectionGrade
,
SubsectionGradeFactory
from
..new.subsection_grade
import
SubsectionGrade
,
SubsectionGradeFactory
from
lms.djangoapps.grades.tests.utils
import
mock_get_score
class
GradeTestBase
(
SharedModuleStoreTestCase
):
class
GradeTestBase
(
SharedModuleStoreTestCase
):
...
@@ -128,7 +129,7 @@ class SubsectionGradeFactoryTest(GradeTestBase):
...
@@ -128,7 +129,7 @@ class SubsectionGradeFactoryTest(GradeTestBase):
'lms.djangoapps.grades.new.subsection_grade.SubsectionGradeFactory._get_saved_grade'
,
'lms.djangoapps.grades.new.subsection_grade.SubsectionGradeFactory._get_saved_grade'
,
wraps
=
self
.
subsection_grade_factory
.
_get_saved_grade
# pylint: disable=protected-access
wraps
=
self
.
subsection_grade_factory
.
_get_saved_grade
# pylint: disable=protected-access
)
as
mock_get_saved_grade
:
)
as
mock_get_saved_grade
:
with
self
.
assertNumQueries
(
1
7
):
with
self
.
assertNumQueries
(
1
9
):
grade_a
=
self
.
subsection_grade_factory
.
create
(
self
.
sequence
,
self
.
course_structure
,
self
.
course
)
grade_a
=
self
.
subsection_grade_factory
.
create
(
self
.
sequence
,
self
.
course_structure
,
self
.
course
)
self
.
assertTrue
(
mock_get_saved_grade
.
called
)
self
.
assertTrue
(
mock_get_saved_grade
.
called
)
self
.
assertTrue
(
mock_save_grades
.
called
)
self
.
assertTrue
(
mock_save_grades
.
called
)
...
@@ -180,11 +181,11 @@ class SubsectionGradeTest(GradeTestBase):
...
@@ -180,11 +181,11 @@ class SubsectionGradeTest(GradeTestBase):
Assuming the underlying score reporting methods work, test that the score is calculated properly.
Assuming the underlying score reporting methods work, test that the score is calculated properly.
"""
"""
grade
=
self
.
subsection_grade_factory
.
create
(
self
.
sequence
,
self
.
course_structure
,
self
.
course
)
grade
=
self
.
subsection_grade_factory
.
create
(
self
.
sequence
,
self
.
course_structure
,
self
.
course
)
with
patch
(
'lms.djangoapps.grades.new.subsection_grade.get_score'
,
return_value
=
(
0
,
1
)
):
with
mock_get_score
(
1
,
2
):
# The final 2 parameters are only passed through to our mocked-out get_score method
# The final 2 parameters are only passed through to our mocked-out get_score method
grade
.
compute
(
self
.
request
.
user
,
self
.
course_structure
,
None
,
None
)
grade
.
compute
(
self
.
request
.
user
,
self
.
course_structure
,
None
,
None
)
self
.
assertEqual
(
grade
.
all_total
.
earned
,
0
)
self
.
assertEqual
(
grade
.
all_total
.
earned
,
1
)
self
.
assertEqual
(
grade
.
all_total
.
possible
,
1
)
self
.
assertEqual
(
grade
.
all_total
.
possible
,
2
)
def
test_save_and_load
(
self
):
def
test_save_and_load
(
self
):
"""
"""
...
...
lms/djangoapps/grades/tests/test_signals.py
View file @
bf421108
...
@@ -203,7 +203,7 @@ class ScoreChangedUpdatesSubsectionGradeTest(ModuleStoreTestCase):
...
@@ -203,7 +203,7 @@ class ScoreChangedUpdatesSubsectionGradeTest(ModuleStoreTestCase):
def
test_subsection_grade_updated_on_signal
(
self
,
default_store
):
def
test_subsection_grade_updated_on_signal
(
self
,
default_store
):
with
self
.
store
.
default_store
(
default_store
):
with
self
.
store
.
default_store
(
default_store
):
self
.
set_up_course
()
self
.
set_up_course
()
with
check_mongo_calls
(
2
)
and
self
.
assertNumQueries
(
1
3
):
with
check_mongo_calls
(
2
)
and
self
.
assertNumQueries
(
1
5
):
recalculate_subsection_grade_handler
(
None
,
**
self
.
score_changed_kwargs
)
recalculate_subsection_grade_handler
(
None
,
**
self
.
score_changed_kwargs
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
...
@@ -212,7 +212,7 @@ class ScoreChangedUpdatesSubsectionGradeTest(ModuleStoreTestCase):
...
@@ -212,7 +212,7 @@ class ScoreChangedUpdatesSubsectionGradeTest(ModuleStoreTestCase):
self
.
set_up_course
()
self
.
set_up_course
()
ItemFactory
.
create
(
parent
=
self
.
sequential
,
category
=
'problem'
,
display_name
=
'problem2'
)
ItemFactory
.
create
(
parent
=
self
.
sequential
,
category
=
'problem'
,
display_name
=
'problem2'
)
ItemFactory
.
create
(
parent
=
self
.
sequential
,
category
=
'problem'
,
display_name
=
'problem3'
)
ItemFactory
.
create
(
parent
=
self
.
sequential
,
category
=
'problem'
,
display_name
=
'problem3'
)
with
check_mongo_calls
(
2
)
and
self
.
assertNumQueries
(
1
3
):
with
check_mongo_calls
(
2
)
and
self
.
assertNumQueries
(
1
5
):
recalculate_subsection_grade_handler
(
None
,
**
self
.
score_changed_kwargs
)
recalculate_subsection_grade_handler
(
None
,
**
self
.
score_changed_kwargs
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
...
@@ -238,8 +238,8 @@ class ScoreChangedUpdatesSubsectionGradeTest(ModuleStoreTestCase):
...
@@ -238,8 +238,8 @@ class ScoreChangedUpdatesSubsectionGradeTest(ModuleStoreTestCase):
SCORE_CHANGED
.
send
(
sender
=
None
,
**
self
.
score_changed_kwargs
)
SCORE_CHANGED
.
send
(
sender
=
None
,
**
self
.
score_changed_kwargs
)
@ddt.data
(
@ddt.data
(
(
'points_possible'
,
2
,
1
3
),
(
'points_possible'
,
2
,
1
5
),
(
'points_earned'
,
2
,
1
3
),
(
'points_earned'
,
2
,
1
5
),
(
'user'
,
0
,
0
),
(
'user'
,
0
,
0
),
(
'course_id'
,
0
,
0
),
(
'course_id'
,
0
,
0
),
(
'usage_id'
,
0
,
0
),
(
'usage_id'
,
0
,
0
),
...
...
lms/djangoapps/grades/tests/utils.py
View file @
bf421108
...
@@ -13,3 +13,13 @@ def mock_passing_grade(grade_pass='Pass', percent=0.75):
...
@@ -13,3 +13,13 @@ def mock_passing_grade(grade_pass='Pass', percent=0.75):
with
patch
(
'lms.djangoapps.grades.course_grades.summary'
)
as
mock_grade
:
with
patch
(
'lms.djangoapps.grades.course_grades.summary'
)
as
mock_grade
:
mock_grade
.
return_value
=
{
'grade'
:
grade_pass
,
'percent'
:
percent
}
mock_grade
.
return_value
=
{
'grade'
:
grade_pass
,
'percent'
:
percent
}
yield
yield
@contextmanager
def
mock_get_score
(
earned
=
0
,
possible
=
1
):
"""
Mocks the get_score function to return a valid grade.
"""
with
patch
(
'lms.djangoapps.grades.new.subsection_grade.get_score'
)
as
mock_score
:
mock_score
.
return_value
=
(
earned
,
possible
)
yield
mock_score
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