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
OpenEdx
edx-platform
Commits
652c3eb1
Commit
652c3eb1
authored
Sep 22, 2017
by
Nimisha Asthagiri
Committed by
Dillon Dumesnil
Oct 31, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Grades cleanup: remove no longer needed management commands
EDUCATOR-178
parent
ec112168
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
2 additions
and
314 deletions
+2
-314
lms/djangoapps/grades/management/commands/get_grades.py
+0
-136
lms/djangoapps/grades/management/commands/reset_grades.py
+0
-144
lms/djangoapps/grades/management/commands/tests/test_reset_grades.py
+0
-0
lms/djangoapps/grades/models.py
+2
-34
No files found.
lms/djangoapps/grades/management/commands/get_grades.py
deleted
100644 → 0
View file @
ec112168
"""
Management command to generate a list of grades for
all students that are enrolled in a course.
"""
import
csv
import
datetime
import
os
from
optparse
import
make_option
from
django.contrib.auth.models
import
User
from
django.core.handlers.base
import
BaseHandler
from
django.core.management.base
import
BaseCommand
,
CommandError
from
django.test.client
import
RequestFactory
from
opaque_keys.edx.keys
import
CourseKey
from
lms.djangoapps.certificates.models
import
GeneratedCertificate
from
lms.djangoapps.courseware
import
courses
from
lms.djangoapps.grades.new.course_grade_factory
import
CourseGradeFactory
class
RequestMock
(
RequestFactory
):
"""
Class to create a mock request.
"""
def
request
(
self
,
**
request
):
"Construct a generic request object."
request
=
RequestFactory
.
request
(
self
,
**
request
)
handler
=
BaseHandler
()
handler
.
load_middleware
()
for
middleware_method
in
handler
.
_request_middleware
:
# pylint: disable=protected-access
if
middleware_method
(
request
):
raise
Exception
(
"Couldn't create request mock object - "
"request middleware returned a response"
)
return
request
class
Command
(
BaseCommand
):
"""
Management command for get_grades
"""
help
=
"""
Generate a list of grades for all students
that are enrolled in a course.
CSV will include the following:
- username
- email
- grade in the certificate table if it exists
- computed grade
- grade breakdown
Outputs grades to a csv file.
Example:
sudo -u www-data SERVICE_VARIANT=lms /opt/edx/bin/django-admin.py get_grades
\
-c MITx/Chi6.00intro/A_Taste_of_Python_Programming -o /tmp/20130813-6.00x.csv
\
--settings=lms.envs.aws --pythonpath=/opt/wwc/edx-platform
"""
option_list
=
BaseCommand
.
option_list
+
(
make_option
(
'-c'
,
'--course'
,
metavar
=
'COURSE_ID'
,
dest
=
'course'
,
default
=
False
,
help
=
'Course ID for grade distribution'
),
make_option
(
'-o'
,
'--output'
,
metavar
=
'FILE'
,
dest
=
'output'
,
default
=
False
,
help
=
'Filename for grade output'
))
def
handle
(
self
,
*
args
,
**
options
):
if
os
.
path
.
exists
(
options
[
'output'
]):
raise
CommandError
(
"File {0} already exists"
.
format
(
options
[
'output'
]))
status_interval
=
100
# parse out the course into a coursekey
if
options
[
'course'
]:
course_key
=
CourseKey
.
from_string
(
options
[
'course'
])
print
"Fetching enrolled students for {0}"
.
format
(
course_key
)
enrolled_students
=
User
.
objects
.
filter
(
courseenrollment__course_id
=
course_key
)
factory
=
RequestMock
()
request
=
factory
.
get
(
'/'
)
total
=
enrolled_students
.
count
()
print
"Total enrolled: {0}"
.
format
(
total
)
course
=
courses
.
get_course_by_id
(
course_key
)
total
=
enrolled_students
.
count
()
start
=
datetime
.
datetime
.
now
()
rows
=
[]
header
=
None
print
"Fetching certificate data"
cert_grades
=
{
cert
.
user
.
username
:
cert
.
grade
for
cert
in
list
(
GeneratedCertificate
.
objects
.
filter
(
# pylint: disable=no-member
course_id
=
course_key
)
.
prefetch_related
(
'user'
)
)
}
print
"Grading students"
for
count
,
student
in
enumerate
(
enrolled_students
):
count
+=
1
if
count
%
status_interval
==
0
:
# Print a status update with an approximation of
# how much time is left based on how long the last
# interval took
diff
=
datetime
.
datetime
.
now
()
-
start
timeleft
=
diff
*
(
total
-
count
)
/
status_interval
hours
,
remainder
=
divmod
(
timeleft
.
seconds
,
3600
)
minutes
,
__
=
divmod
(
remainder
,
60
)
print
"{0}/{1} completed ~{2:02}:{3:02}m remaining"
.
format
(
count
,
total
,
hours
,
minutes
)
start
=
datetime
.
datetime
.
now
()
request
.
user
=
student
grade
=
CourseGradeFactory
()
.
create
(
student
,
course
)
if
not
header
:
header
=
[
section
[
'label'
]
for
section
in
grade
.
summary
[
u'section_breakdown'
]]
rows
.
append
([
"email"
,
"username"
,
"certificate-grade"
,
"grade"
]
+
header
)
percents
=
{
section
[
'label'
]:
section
[
'percent'
]
for
section
in
grade
.
summary
[
u'section_breakdown'
]}
row_percents
=
[
percents
[
label
]
for
label
in
header
]
if
student
.
username
in
cert_grades
:
rows
.
append
(
[
student
.
email
,
student
.
username
,
cert_grades
[
student
.
username
],
grade
.
percent
]
+
row_percents
,
)
else
:
rows
.
append
([
student
.
email
,
student
.
username
,
"N/A"
,
grade
.
percent
]
+
row_percents
)
with
open
(
options
[
'output'
],
'wb'
)
as
f
:
writer
=
csv
.
writer
(
f
)
writer
.
writerows
(
rows
)
lms/djangoapps/grades/management/commands/reset_grades.py
deleted
100644 → 0
View file @
ec112168
"""
Reset persistent grades for learners.
"""
import
logging
from
datetime
import
datetime
from
textwrap
import
dedent
from
django.core.management.base
import
BaseCommand
,
CommandError
from
django.db.models
import
Count
from
pytz
import
utc
from
lms.djangoapps.grades.models
import
PersistentCourseGrade
,
PersistentSubsectionGrade
from
openedx.core.lib.command_utils
import
get_mutually_exclusive_required_option
,
parse_course_keys
log
=
logging
.
getLogger
(
__name__
)
DATE_FORMAT
=
"
%
Y-
%
m-
%
d
%
H:
%
M"
class
Command
(
BaseCommand
):
"""
Reset persistent grades for learners.
"""
help
=
dedent
(
__doc__
)
.
strip
()
def
add_arguments
(
self
,
parser
):
"""
Add arguments to the command parser.
"""
parser
.
add_argument
(
'--dry_run'
,
action
=
'store_true'
,
default
=
False
,
dest
=
'dry_run'
,
help
=
"Output what we're going to do, but don't actually do it. To actually delete, use --delete instead."
)
parser
.
add_argument
(
'--delete'
,
action
=
'store_true'
,
default
=
False
,
dest
=
'delete'
,
help
=
"Actually perform the deletions of the course. For a Dry Run, use --dry_run instead."
)
parser
.
add_argument
(
'--courses'
,
dest
=
'courses'
,
nargs
=
'+'
,
help
=
'Reset persistent grades for the list of courses provided.'
,
)
parser
.
add_argument
(
'--all_courses'
,
action
=
'store_true'
,
dest
=
'all_courses'
,
default
=
False
,
help
=
'Reset persistent grades for all courses.'
,
)
parser
.
add_argument
(
'--modified_start'
,
dest
=
'modified_start'
,
help
=
'Starting range for modified date (inclusive): e.g. "2016-08-23 16:43"; expected in UTC.'
,
)
parser
.
add_argument
(
'--modified_end'
,
dest
=
'modified_end'
,
help
=
'Ending range for modified date (inclusive): e.g. "2016-12-23 16:43"; expected in UTC.'
,
)
parser
.
add_argument
(
'--db_table'
,
dest
=
'db_table'
,
help
=
'Specify "subsection" to reset subsection grades or "course" to reset course grades. If absent, both '
'are reset.'
,
)
def
handle
(
self
,
*
args
,
**
options
):
course_keys
=
None
modified_start
=
None
modified_end
=
None
run_mode
=
get_mutually_exclusive_required_option
(
options
,
'delete'
,
'dry_run'
)
courses_mode
=
get_mutually_exclusive_required_option
(
options
,
'courses'
,
'all_courses'
)
db_table
=
options
.
get
(
'db_table'
)
if
db_table
not
in
{
'subsection'
,
'course'
,
None
}:
raise
CommandError
(
'Invalid value for db_table. Valid options are "subsection" or "course" only.'
)
if
options
.
get
(
'modified_start'
):
modified_start
=
utc
.
localize
(
datetime
.
strptime
(
options
[
'modified_start'
],
DATE_FORMAT
))
if
options
.
get
(
'modified_end'
):
if
not
modified_start
:
raise
CommandError
(
'Optional value for modified_end provided without a value for modified_start.'
)
modified_end
=
utc
.
localize
(
datetime
.
strptime
(
options
[
'modified_end'
],
DATE_FORMAT
))
if
courses_mode
==
'courses'
:
course_keys
=
parse_course_keys
(
options
[
'courses'
])
log
.
info
(
"reset_grade: Started in
%
s mode!"
,
run_mode
)
operation
=
self
.
_query_grades
if
run_mode
==
'dry_run'
else
self
.
_delete_grades
if
db_table
==
'subsection'
or
db_table
is
None
:
operation
(
PersistentSubsectionGrade
,
course_keys
,
modified_start
,
modified_end
)
if
db_table
==
'course'
or
db_table
is
None
:
operation
(
PersistentCourseGrade
,
course_keys
,
modified_start
,
modified_end
)
log
.
info
(
"reset_grade: Finished in
%
s mode!"
,
run_mode
)
def
_delete_grades
(
self
,
grade_model_class
,
*
args
,
**
kwargs
):
"""
Deletes the requested grades in the given model, filtered by the provided args and kwargs.
"""
grades_query_set
=
grade_model_class
.
query_grades
(
*
args
,
**
kwargs
)
num_rows_to_delete
=
grades_query_set
.
count
()
log
.
info
(
"reset_grade: Deleting
%
s:
%
d row(s)."
,
grade_model_class
.
__name__
,
num_rows_to_delete
)
grade_model_class
.
delete_grades
(
*
args
,
**
kwargs
)
log
.
info
(
"reset_grade: Deleted
%
s:
%
d row(s)."
,
grade_model_class
.
__name__
,
num_rows_to_delete
)
def
_query_grades
(
self
,
grade_model_class
,
*
args
,
**
kwargs
):
"""
Queries the requested grades in the given model, filtered by the provided args and kwargs.
"""
total_for_all_courses
=
0
grades_query_set
=
grade_model_class
.
query_grades
(
*
args
,
**
kwargs
)
grades_stats
=
grades_query_set
.
values
(
'course_id'
)
.
order_by
()
.
annotate
(
total
=
Count
(
'course_id'
))
for
stat
in
grades_stats
:
total_for_all_courses
+=
stat
[
'total'
]
log
.
info
(
"reset_grade: Would delete
%
s for COURSE
%
s:
%
d row(s)."
,
grade_model_class
.
__name__
,
stat
[
'course_id'
],
stat
[
'total'
],
)
log
.
info
(
"reset_grade: Would delete
%
s in TOTAL:
%
d row(s)."
,
grade_model_class
.
__name__
,
total_for_all_courses
,
)
lms/djangoapps/grades/management/commands/tests/test_reset_grades.py
deleted
100644 → 0
View file @
ec112168
This diff is collapsed.
Click to expand it.
lms/djangoapps/grades/models.py
View file @
652c3eb1
...
@@ -39,38 +39,6 @@ BLOCK_RECORD_LIST_VERSION = 1
...
@@ -39,38 +39,6 @@ BLOCK_RECORD_LIST_VERSION = 1
BlockRecord
=
namedtuple
(
'BlockRecord'
,
[
'locator'
,
'weight'
,
'raw_possible'
,
'graded'
])
BlockRecord
=
namedtuple
(
'BlockRecord'
,
[
'locator'
,
'weight'
,
'raw_possible'
,
'graded'
])
class
DeleteGradesMixin
(
object
):
"""
A Mixin class that provides functionality to delete grades.
"""
@classmethod
def
query_grades
(
cls
,
course_ids
=
None
,
modified_start
=
None
,
modified_end
=
None
):
"""
Queries all the grades in the table, filtered by the provided arguments.
"""
kwargs
=
{}
if
course_ids
:
kwargs
[
'course_id__in'
]
=
[
course_id
for
course_id
in
course_ids
]
if
modified_start
:
if
modified_end
:
kwargs
[
'modified__range'
]
=
(
modified_start
,
modified_end
)
else
:
kwargs
[
'modified__gt'
]
=
modified_start
return
cls
.
objects
.
filter
(
**
kwargs
)
@classmethod
def
delete_grades
(
cls
,
*
args
,
**
kwargs
):
"""
Deletes all the grades in the table, filtered by the provided arguments.
"""
query
=
cls
.
query_grades
(
*
args
,
**
kwargs
)
query
.
delete
()
class
BlockRecordList
(
tuple
):
class
BlockRecordList
(
tuple
):
"""
"""
An immutable ordered list of BlockRecord objects.
An immutable ordered list of BlockRecord objects.
...
@@ -285,7 +253,7 @@ class VisibleBlocks(models.Model):
...
@@ -285,7 +253,7 @@ class VisibleBlocks(models.Model):
return
u"visible_blocks_cache.{}"
.
format
(
course_key
)
return
u"visible_blocks_cache.{}"
.
format
(
course_key
)
class
PersistentSubsectionGrade
(
DeleteGradesMixin
,
TimeStampedModel
):
class
PersistentSubsectionGrade
(
TimeStampedModel
):
"""
"""
A django model tracking persistent grades at the subsection level.
A django model tracking persistent grades at the subsection level.
"""
"""
...
@@ -546,7 +514,7 @@ class PersistentSubsectionGrade(DeleteGradesMixin, TimeStampedModel):
...
@@ -546,7 +514,7 @@ class PersistentSubsectionGrade(DeleteGradesMixin, TimeStampedModel):
)
)
class
PersistentCourseGrade
(
DeleteGradesMixin
,
TimeStampedModel
):
class
PersistentCourseGrade
(
TimeStampedModel
):
"""
"""
A django model tracking persistent course grades.
A django model tracking persistent course grades.
"""
"""
...
...
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