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
420b0920
Commit
420b0920
authored
Jul 23, 2013
by
Adam
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #467 from edx/fix/adam/file-upload
Fix/adam/file upload
parents
86ee2bca
2b404622
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
245 additions
and
166 deletions
+245
-166
common/djangoapps/heartbeat/views.py
+3
-1
common/djangoapps/student/models.py
+30
-28
common/djangoapps/student/views.py
+4
-4
common/lib/capa/capa/capa_problem.py
+47
-21
common/lib/capa/capa/correctmap.py
+19
-15
common/lib/capa/capa/responsetypes.py
+16
-11
common/lib/capa/capa/tests/test_responsetypes.py
+11
-6
common/lib/capa/capa/xqueue_interface.py
+10
-9
common/lib/xmodule/xmodule/capa_module.py
+6
-2
common/lib/xmodule/xmodule/fields.py
+1
-0
common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py
+3
-2
common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py
+7
-3
common/lib/xmodule/xmodule/tests/test_combined_open_ended.py
+4
-2
i18n/tests/test_extract.py
+15
-10
i18n/tests/test_generate.py
+8
-3
lms/djangoapps/certificates/management/commands/ungenerated_certs.py
+4
-4
lms/djangoapps/courseware/module_render.py
+57
-45
No files found.
common/djangoapps/heartbeat/views.py
View file @
420b0920
import
json
import
json
from
datetime
import
datetime
from
datetime
import
datetime
from
pytz
import
UTC
from
django.http
import
HttpResponse
from
django.http
import
HttpResponse
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
from
dogapi
import
dog_stats_api
from
dogapi
import
dog_stats_api
@dog_stats_api.timed
(
'edxapp.heartbeat'
)
@dog_stats_api.timed
(
'edxapp.heartbeat'
)
def
heartbeat
(
request
):
def
heartbeat
(
request
):
"""
"""
Simple view that a loadbalancer can check to verify that the app is up
Simple view that a loadbalancer can check to verify that the app is up
"""
"""
output
=
{
output
=
{
'date'
:
datetime
.
now
()
.
isoformat
(),
'date'
:
datetime
.
now
(
UTC
)
.
isoformat
(),
'courses'
:
[
course
.
location
.
url
()
for
course
in
modulestore
()
.
get_courses
()],
'courses'
:
[
course
.
location
.
url
()
for
course
in
modulestore
()
.
get_courses
()],
}
}
return
HttpResponse
(
json
.
dumps
(
output
,
indent
=
4
))
return
HttpResponse
(
json
.
dumps
(
output
,
indent
=
4
))
common/djangoapps/student/models.py
View file @
420b0920
...
@@ -69,30 +69,33 @@ class UserProfile(models.Model):
...
@@ -69,30 +69,33 @@ class UserProfile(models.Model):
location
=
models
.
CharField
(
blank
=
True
,
max_length
=
255
,
db_index
=
True
)
location
=
models
.
CharField
(
blank
=
True
,
max_length
=
255
,
db_index
=
True
)
# Optional demographic data we started capturing from Fall 2012
# Optional demographic data we started capturing from Fall 2012
this_year
=
datetime
.
now
()
.
year
this_year
=
datetime
.
now
(
UTC
)
.
year
VALID_YEARS
=
range
(
this_year
,
this_year
-
120
,
-
1
)
VALID_YEARS
=
range
(
this_year
,
this_year
-
120
,
-
1
)
year_of_birth
=
models
.
IntegerField
(
blank
=
True
,
null
=
True
,
db_index
=
True
)
year_of_birth
=
models
.
IntegerField
(
blank
=
True
,
null
=
True
,
db_index
=
True
)
GENDER_CHOICES
=
((
'm'
,
'Male'
),
(
'f'
,
'Female'
),
(
'o'
,
'Other'
))
GENDER_CHOICES
=
((
'm'
,
'Male'
),
(
'f'
,
'Female'
),
(
'o'
,
'Other'
))
gender
=
models
.
CharField
(
blank
=
True
,
null
=
True
,
max_length
=
6
,
db_index
=
True
,
gender
=
models
.
CharField
(
choices
=
GENDER_CHOICES
)
blank
=
True
,
null
=
True
,
max_length
=
6
,
db_index
=
True
,
choices
=
GENDER_CHOICES
)
# [03/21/2013] removed these, but leaving comment since there'll still be
# [03/21/2013] removed these, but leaving comment since there'll still be
# p_se and p_oth in the existing data in db.
# p_se and p_oth in the existing data in db.
# ('p_se', 'Doctorate in science or engineering'),
# ('p_se', 'Doctorate in science or engineering'),
# ('p_oth', 'Doctorate in another field'),
# ('p_oth', 'Doctorate in another field'),
LEVEL_OF_EDUCATION_CHOICES
=
((
'p'
,
'Doctorate'
),
LEVEL_OF_EDUCATION_CHOICES
=
(
(
'm'
,
"Master's or professional degree"
),
(
'p'
,
'Doctorate'
),
(
'b'
,
"Bachelor's degree"
),
(
'm'
,
"Master's or professional degree"
),
(
'a'
,
"Associate's degree"
),
(
'b'
,
"Bachelor's degree"
),
(
'hs'
,
"Secondary/high school"
),
(
'a'
,
"Associate's degree"
),
(
'jhs'
,
"Junior secondary/junior high/middle school"
),
(
'hs'
,
"Secondary/high school"
),
(
'el'
,
"Elementary/primary school"
),
(
'jhs'
,
"Junior secondary/junior high/middle school"
),
(
'none'
,
"None"
),
(
'el'
,
"Elementary/primary school"
),
(
'other'
,
"Other"
))
(
'none'
,
"None"
),
(
'other'
,
"Other"
)
)
level_of_education
=
models
.
CharField
(
level_of_education
=
models
.
CharField
(
blank
=
True
,
null
=
True
,
max_length
=
6
,
db_index
=
True
,
blank
=
True
,
null
=
True
,
max_length
=
6
,
db_index
=
True
,
choices
=
LEVEL_OF_EDUCATION_CHOICES
choices
=
LEVEL_OF_EDUCATION_CHOICES
)
)
mailing_address
=
models
.
TextField
(
blank
=
True
,
null
=
True
)
mailing_address
=
models
.
TextField
(
blank
=
True
,
null
=
True
)
goals
=
models
.
TextField
(
blank
=
True
,
null
=
True
)
goals
=
models
.
TextField
(
blank
=
True
,
null
=
True
)
allow_certificate
=
models
.
BooleanField
(
default
=
1
)
allow_certificate
=
models
.
BooleanField
(
default
=
1
)
...
@@ -307,18 +310,18 @@ class TestCenterUserForm(ModelForm):
...
@@ -307,18 +310,18 @@ class TestCenterUserForm(ModelForm):
ACCOMMODATION_REJECTED_CODE
=
'NONE'
ACCOMMODATION_REJECTED_CODE
=
'NONE'
ACCOMMODATION_CODES
=
(
ACCOMMODATION_CODES
=
(
(
ACCOMMODATION_REJECTED_CODE
,
'No Accommodation Granted'
),
(
ACCOMMODATION_REJECTED_CODE
,
'No Accommodation Granted'
),
(
'EQPMNT'
,
'Equipment'
),
(
'EQPMNT'
,
'Equipment'
),
(
'ET12ET'
,
'Extra Time - 1/2 Exam Time'
),
(
'ET12ET'
,
'Extra Time - 1/2 Exam Time'
),
(
'ET30MN'
,
'Extra Time - 30 Minutes'
),
(
'ET30MN'
,
'Extra Time - 30 Minutes'
),
(
'ETDBTM'
,
'Extra Time - Double Time'
),
(
'ETDBTM'
,
'Extra Time - Double Time'
),
(
'SEPRMM'
,
'Separate Room'
),
(
'SEPRMM'
,
'Separate Room'
),
(
'SRREAD'
,
'Separate Room and Reader'
),
(
'SRREAD'
,
'Separate Room and Reader'
),
(
'SRRERC'
,
'Separate Room and Reader/Recorder'
),
(
'SRRERC'
,
'Separate Room and Reader/Recorder'
),
(
'SRRECR'
,
'Separate Room and Recorder'
),
(
'SRRECR'
,
'Separate Room and Recorder'
),
(
'SRSEAN'
,
'Separate Room and Service Animal'
),
(
'SRSEAN'
,
'Separate Room and Service Animal'
),
(
'SRSGNR'
,
'Separate Room and Sign Language Interpreter'
),
(
'SRSGNR'
,
'Separate Room and Sign Language Interpreter'
),
)
)
ACCOMMODATION_CODE_DICT
=
{
code
:
name
for
(
code
,
name
)
in
ACCOMMODATION_CODES
}
ACCOMMODATION_CODE_DICT
=
{
code
:
name
for
(
code
,
name
)
in
ACCOMMODATION_CODES
}
...
@@ -572,7 +575,6 @@ class TestCenterRegistrationForm(ModelForm):
...
@@ -572,7 +575,6 @@ class TestCenterRegistrationForm(ModelForm):
return
code
return
code
def
get_testcenter_registration
(
user
,
course_id
,
exam_series_code
):
def
get_testcenter_registration
(
user
,
course_id
,
exam_series_code
):
try
:
try
:
tcu
=
TestCenterUser
.
objects
.
get
(
user
=
user
)
tcu
=
TestCenterUser
.
objects
.
get
(
user
=
user
)
...
...
common/djangoapps/student/views.py
View file @
420b0920
...
@@ -111,9 +111,9 @@ def get_date_for_press(publish_date):
...
@@ -111,9 +111,9 @@ def get_date_for_press(publish_date):
# strip off extra months, and just use the first:
# strip off extra months, and just use the first:
date
=
re
.
sub
(
multimonth_pattern
,
", "
,
publish_date
)
date
=
re
.
sub
(
multimonth_pattern
,
", "
,
publish_date
)
if
re
.
search
(
day_pattern
,
date
):
if
re
.
search
(
day_pattern
,
date
):
date
=
datetime
.
datetime
.
strptime
(
date
,
"
%
B
%
d,
%
Y"
)
date
=
datetime
.
datetime
.
strptime
(
date
,
"
%
B
%
d,
%
Y"
)
.
replace
(
tzinfo
=
UTC
)
else
:
else
:
date
=
datetime
.
datetime
.
strptime
(
date
,
"
%
B,
%
Y"
)
date
=
datetime
.
datetime
.
strptime
(
date
,
"
%
B,
%
Y"
)
.
replace
(
tzinfo
=
UTC
)
return
date
return
date
...
@@ -1100,7 +1100,7 @@ def confirm_email_change(request, key):
...
@@ -1100,7 +1100,7 @@ def confirm_email_change(request, key):
meta
=
up
.
get_meta
()
meta
=
up
.
get_meta
()
if
'old_emails'
not
in
meta
:
if
'old_emails'
not
in
meta
:
meta
[
'old_emails'
]
=
[]
meta
[
'old_emails'
]
=
[]
meta
[
'old_emails'
]
.
append
([
user
.
email
,
datetime
.
datetime
.
now
()
.
isoformat
()])
meta
[
'old_emails'
]
.
append
([
user
.
email
,
datetime
.
datetime
.
now
(
UTC
)
.
isoformat
()])
up
.
set_meta
(
meta
)
up
.
set_meta
(
meta
)
up
.
save
()
up
.
save
()
# Send it to the old email...
# Send it to the old email...
...
@@ -1198,7 +1198,7 @@ def accept_name_change_by_id(id):
...
@@ -1198,7 +1198,7 @@ def accept_name_change_by_id(id):
meta
=
up
.
get_meta
()
meta
=
up
.
get_meta
()
if
'old_names'
not
in
meta
:
if
'old_names'
not
in
meta
:
meta
[
'old_names'
]
=
[]
meta
[
'old_names'
]
=
[]
meta
[
'old_names'
]
.
append
([
up
.
name
,
pnc
.
rationale
,
datetime
.
datetime
.
now
()
.
isoformat
()])
meta
[
'old_names'
]
.
append
([
up
.
name
,
pnc
.
rationale
,
datetime
.
datetime
.
now
(
UTC
)
.
isoformat
()])
up
.
set_meta
(
meta
)
up
.
set_meta
(
meta
)
up
.
name
=
pnc
.
new_name
up
.
name
=
pnc
.
new_name
...
...
common/lib/capa/capa/capa_problem.py
View file @
420b0920
...
@@ -32,6 +32,8 @@ import capa.xqueue_interface as xqueue_interface
...
@@ -32,6 +32,8 @@ import capa.xqueue_interface as xqueue_interface
import
capa.responsetypes
as
responsetypes
import
capa.responsetypes
as
responsetypes
from
capa.safe_exec
import
safe_exec
from
capa.safe_exec
import
safe_exec
from
pytz
import
UTC
# dict of tagname, Response Class -- this should come from auto-registering
# dict of tagname, Response Class -- this should come from auto-registering
response_tag_dict
=
dict
([(
x
.
response_tag
,
x
)
for
x
in
responsetypes
.
__all__
])
response_tag_dict
=
dict
([(
x
.
response_tag
,
x
)
for
x
in
responsetypes
.
__all__
])
...
@@ -42,13 +44,22 @@ solution_tags = ['solution']
...
@@ -42,13 +44,22 @@ solution_tags = ['solution']
response_properties
=
[
"codeparam"
,
"responseparam"
,
"answer"
,
"openendedparam"
]
response_properties
=
[
"codeparam"
,
"responseparam"
,
"answer"
,
"openendedparam"
]
# special problem tags which should be turned into innocuous HTML
# special problem tags which should be turned into innocuous HTML
html_transforms
=
{
'problem'
:
{
'tag'
:
'div'
},
html_transforms
=
{
'text'
:
{
'tag'
:
'span'
},
'problem'
:
{
'tag'
:
'div'
},
'math'
:
{
'tag'
:
'span'
},
'text'
:
{
'tag'
:
'span'
},
}
'math'
:
{
'tag'
:
'span'
},
}
# These should be removed from HTML output, including all subelements
# These should be removed from HTML output, including all subelements
html_problem_semantics
=
[
"codeparam"
,
"responseparam"
,
"answer"
,
"script"
,
"hintgroup"
,
"openendedparam"
,
"openendedrubric"
]
html_problem_semantics
=
[
"codeparam"
,
"responseparam"
,
"answer"
,
"script"
,
"hintgroup"
,
"openendedparam"
,
"openendedrubric"
]
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -242,11 +253,15 @@ class LoncapaProblem(object):
...
@@ -242,11 +253,15 @@ class LoncapaProblem(object):
return
None
return
None
# Get a list of timestamps of all queueing requests, then convert it to a DateTime object
# Get a list of timestamps of all queueing requests, then convert it to a DateTime object
queuetime_strs
=
[
self
.
correct_map
.
get_queuetime_str
(
answer_id
)
queuetime_strs
=
[
for
answer_id
in
self
.
correct_map
self
.
correct_map
.
get_queuetime_str
(
answer_id
)
if
self
.
correct_map
.
is_queued
(
answer_id
)]
for
answer_id
in
self
.
correct_map
queuetimes
=
[
datetime
.
strptime
(
qt_str
,
xqueue_interface
.
dateformat
)
if
self
.
correct_map
.
is_queued
(
answer_id
)
for
qt_str
in
queuetime_strs
]
]
queuetimes
=
[
datetime
.
strptime
(
qt_str
,
xqueue_interface
.
dateformat
)
.
replace
(
tzinfo
=
UTC
)
for
qt_str
in
queuetime_strs
]
return
max
(
queuetimes
)
return
max
(
queuetimes
)
...
@@ -404,10 +419,16 @@ class LoncapaProblem(object):
...
@@ -404,10 +419,16 @@ class LoncapaProblem(object):
# open using ModuleSystem OSFS filestore
# open using ModuleSystem OSFS filestore
ifp
=
self
.
system
.
filestore
.
open
(
filename
)
ifp
=
self
.
system
.
filestore
.
open
(
filename
)
except
Exception
as
err
:
except
Exception
as
err
:
log
.
warning
(
'Error
%
s in problem xml include:
%
s'
%
(
log
.
warning
(
err
,
etree
.
tostring
(
inc
,
pretty_print
=
True
)))
'Error
%
s in problem xml include:
%
s'
%
(
log
.
warning
(
'Cannot find file
%
s in
%
s'
%
(
err
,
etree
.
tostring
(
inc
,
pretty_print
=
True
)
filename
,
self
.
system
.
filestore
))
)
)
log
.
warning
(
'Cannot find file
%
s in
%
s'
%
(
filename
,
self
.
system
.
filestore
)
)
# if debugging, don't fail - just log error
# if debugging, don't fail - just log error
# TODO (vshnayder): need real error handling, display to users
# TODO (vshnayder): need real error handling, display to users
if
not
self
.
system
.
get
(
'DEBUG'
):
if
not
self
.
system
.
get
(
'DEBUG'
):
...
@@ -418,8 +439,11 @@ class LoncapaProblem(object):
...
@@ -418,8 +439,11 @@ class LoncapaProblem(object):
# read in and convert to XML
# read in and convert to XML
incxml
=
etree
.
XML
(
ifp
.
read
())
incxml
=
etree
.
XML
(
ifp
.
read
())
except
Exception
as
err
:
except
Exception
as
err
:
log
.
warning
(
'Error
%
s in problem xml include:
%
s'
%
(
log
.
warning
(
err
,
etree
.
tostring
(
inc
,
pretty_print
=
True
)))
'Error
%
s in problem xml include:
%
s'
%
(
err
,
etree
.
tostring
(
inc
,
pretty_print
=
True
)
)
)
log
.
warning
(
'Cannot parse XML in
%
s'
%
(
filename
))
log
.
warning
(
'Cannot parse XML in
%
s'
%
(
filename
))
# if debugging, don't fail - just log error
# if debugging, don't fail - just log error
# TODO (vshnayder): same as above
# TODO (vshnayder): same as above
...
@@ -579,8 +603,9 @@ class LoncapaProblem(object):
...
@@ -579,8 +603,9 @@ class LoncapaProblem(object):
# let each Response render itself
# let each Response render itself
if
problemtree
in
self
.
responders
:
if
problemtree
in
self
.
responders
:
overall_msg
=
self
.
correct_map
.
get_overall_message
()
overall_msg
=
self
.
correct_map
.
get_overall_message
()
return
self
.
responders
[
problemtree
]
.
render_html
(
self
.
_extract_html
,
return
self
.
responders
[
problemtree
]
.
render_html
(
response_msg
=
overall_msg
)
self
.
_extract_html
,
response_msg
=
overall_msg
)
# let each custom renderer render itself:
# let each custom renderer render itself:
if
problemtree
.
tag
in
customrender
.
registry
.
registered_tags
():
if
problemtree
.
tag
in
customrender
.
registry
.
registered_tags
():
...
@@ -628,9 +653,10 @@ class LoncapaProblem(object):
...
@@ -628,9 +653,10 @@ class LoncapaProblem(object):
answer_id
=
1
answer_id
=
1
input_tags
=
inputtypes
.
registry
.
registered_tags
()
input_tags
=
inputtypes
.
registry
.
registered_tags
()
inputfields
=
tree
.
xpath
(
"|"
.
join
([
'//'
+
response
.
tag
+
'[@id=$id]//'
+
x
inputfields
=
tree
.
xpath
(
for
x
in
(
input_tags
+
solution_tags
)]),
"|"
.
join
([
'//'
+
response
.
tag
+
'[@id=$id]//'
+
x
for
x
in
(
input_tags
+
solution_tags
)]),
id
=
response_id_str
)
id
=
response_id_str
)
# assign one answer_id for each input type or solution type
# assign one answer_id for each input type or solution type
for
entry
in
inputfields
:
for
entry
in
inputfields
:
...
...
common/lib/capa/capa/correctmap.py
View file @
420b0920
...
@@ -37,23 +37,27 @@ class CorrectMap(object):
...
@@ -37,23 +37,27 @@ class CorrectMap(object):
return
self
.
cmap
.
__iter__
()
return
self
.
cmap
.
__iter__
()
# See the documentation for 'set_dict' for the use of kwargs
# See the documentation for 'set_dict' for the use of kwargs
def
set
(
self
,
def
set
(
answer_id
=
None
,
self
,
correctness
=
None
,
answer_id
=
None
,
npoints
=
None
,
correctness
=
None
,
msg
=
''
,
npoints
=
None
,
hint
=
''
,
msg
=
''
,
hintmode
=
None
,
hint
=
''
,
queuestate
=
None
,
**
kwargs
):
hintmode
=
None
,
queuestate
=
None
,
**
kwargs
):
if
answer_id
is
not
None
:
if
answer_id
is
not
None
:
self
.
cmap
[
str
(
answer_id
)]
=
{
'correctness'
:
correctness
,
self
.
cmap
[
str
(
answer_id
)]
=
{
'npoints'
:
npoints
,
'correctness'
:
correctness
,
'msg'
:
msg
,
'npoints'
:
npoints
,
'hint'
:
hint
,
'msg'
:
msg
,
'hintmode'
:
hintmode
,
'hint'
:
hint
,
'queuestate'
:
queuestate
,
'hintmode'
:
hintmode
,
}
'queuestate'
:
queuestate
,
}
def
__repr__
(
self
):
def
__repr__
(
self
):
return
repr
(
self
.
cmap
)
return
repr
(
self
.
cmap
)
...
...
common/lib/capa/capa/responsetypes.py
View file @
420b0920
...
@@ -33,6 +33,7 @@ from shapely.geometry import Point, MultiPoint
...
@@ -33,6 +33,7 @@ from shapely.geometry import Point, MultiPoint
from
calc
import
evaluator
,
UndefinedVariable
from
calc
import
evaluator
,
UndefinedVariable
from
.
import
correctmap
from
.
import
correctmap
from
datetime
import
datetime
from
datetime
import
datetime
from
pytz
import
UTC
from
.util
import
*
from
.util
import
*
from
lxml
import
etree
from
lxml
import
etree
from
lxml.html.soupparser
import
fromstring
as
fromstring_bs
# uses Beautiful Soup!!! FIXME?
from
lxml.html.soupparser
import
fromstring
as
fromstring_bs
# uses Beautiful Soup!!! FIXME?
...
@@ -1365,9 +1366,11 @@ class CodeResponse(LoncapaResponse):
...
@@ -1365,9 +1366,11 @@ class CodeResponse(LoncapaResponse):
# Note that submission can be a file
# Note that submission can be a file
submission
=
student_answers
[
self
.
answer_id
]
submission
=
student_answers
[
self
.
answer_id
]
except
Exception
as
err
:
except
Exception
as
err
:
log
.
error
(
'Error in CodeResponse
%
s: cannot get student answer for
%
s;'
log
.
error
(
' student_answers=
%
s'
%
'Error in CodeResponse
%
s: cannot get student answer for
%
s;'
(
err
,
self
.
answer_id
,
convert_files_to_filenames
(
student_answers
)))
' student_answers=
%
s'
%
(
err
,
self
.
answer_id
,
convert_files_to_filenames
(
student_answers
))
)
raise
Exception
(
err
)
raise
Exception
(
err
)
# We do not support xqueue within Studio.
# We do not support xqueue within Studio.
...
@@ -1381,19 +1384,20 @@ class CodeResponse(LoncapaResponse):
...
@@ -1381,19 +1384,20 @@ class CodeResponse(LoncapaResponse):
#------------------------------------------------------------
#------------------------------------------------------------
qinterface
=
self
.
system
.
xqueue
[
'interface'
]
qinterface
=
self
.
system
.
xqueue
[
'interface'
]
qtime
=
datetime
.
strftime
(
datetime
.
now
(),
xqueue_interface
.
dateformat
)
qtime
=
datetime
.
strftime
(
datetime
.
now
(
UTC
),
xqueue_interface
.
dateformat
)
anonymous_student_id
=
self
.
system
.
anonymous_student_id
anonymous_student_id
=
self
.
system
.
anonymous_student_id
# Generate header
# Generate header
queuekey
=
xqueue_interface
.
make_hashkey
(
str
(
self
.
system
.
seed
)
+
qtime
+
queuekey
=
xqueue_interface
.
make_hashkey
(
anonymous_student_id
+
str
(
self
.
system
.
seed
)
+
qtime
+
anonymous_student_id
+
self
.
answer_id
self
.
answer_id
)
)
callback_url
=
self
.
system
.
xqueue
[
'construct_callback'
]()
callback_url
=
self
.
system
.
xqueue
[
'construct_callback'
]()
xheader
=
xqueue_interface
.
make_xheader
(
xheader
=
xqueue_interface
.
make_xheader
(
lms_callback_url
=
callback_url
,
lms_callback_url
=
callback_url
,
lms_key
=
queuekey
,
lms_key
=
queuekey
,
queue_name
=
self
.
queue_name
)
queue_name
=
self
.
queue_name
)
# Generate body
# Generate body
if
is_list_of_files
(
submission
):
if
is_list_of_files
(
submission
):
...
@@ -1406,9 +1410,10 @@ class CodeResponse(LoncapaResponse):
...
@@ -1406,9 +1410,10 @@ class CodeResponse(LoncapaResponse):
# Metadata related to the student submission revealed to the external
# Metadata related to the student submission revealed to the external
# grader
# grader
student_info
=
{
'anonymous_student_id'
:
anonymous_student_id
,
student_info
=
{
'submission_time'
:
qtime
,
'anonymous_student_id'
:
anonymous_student_id
,
}
'submission_time'
:
qtime
,
}
contents
.
update
({
'student_info'
:
json
.
dumps
(
student_info
)})
contents
.
update
({
'student_info'
:
json
.
dumps
(
student_info
)})
# Submit request. When successful, 'msg' is the prior length of the
# Submit request. When successful, 'msg' is the prior length of the
...
...
common/lib/capa/capa/tests/test_responsetypes.py
View file @
420b0920
...
@@ -18,6 +18,8 @@ from capa.correctmap import CorrectMap
...
@@ -18,6 +18,8 @@ from capa.correctmap import CorrectMap
from
capa.util
import
convert_files_to_filenames
from
capa.util
import
convert_files_to_filenames
from
capa.xqueue_interface
import
dateformat
from
capa.xqueue_interface
import
dateformat
from
pytz
import
UTC
class
ResponseTest
(
unittest
.
TestCase
):
class
ResponseTest
(
unittest
.
TestCase
):
""" Base class for tests of capa responses."""
""" Base class for tests of capa responses."""
...
@@ -333,8 +335,9 @@ class SymbolicResponseTest(ResponseTest):
...
@@ -333,8 +335,9 @@ class SymbolicResponseTest(ResponseTest):
correct_map
=
problem
.
grade_answers
(
input_dict
)
correct_map
=
problem
.
grade_answers
(
input_dict
)
self
.
assertEqual
(
correct_map
.
get_correctness
(
'1_2_1'
),
self
.
assertEqual
(
expected_correctness
)
correct_map
.
get_correctness
(
'1_2_1'
),
expected_correctness
)
class
OptionResponseTest
(
ResponseTest
):
class
OptionResponseTest
(
ResponseTest
):
...
@@ -702,7 +705,7 @@ class CodeResponseTest(ResponseTest):
...
@@ -702,7 +705,7 @@ class CodeResponseTest(ResponseTest):
# Now we queue the LCP
# Now we queue the LCP
cmap
=
CorrectMap
()
cmap
=
CorrectMap
()
for
i
,
answer_id
in
enumerate
(
answer_ids
):
for
i
,
answer_id
in
enumerate
(
answer_ids
):
queuestate
=
CodeResponseTest
.
make_queuestate
(
i
,
datetime
.
now
())
queuestate
=
CodeResponseTest
.
make_queuestate
(
i
,
datetime
.
now
(
UTC
))
cmap
.
update
(
CorrectMap
(
answer_id
=
answer_ids
[
i
],
queuestate
=
queuestate
))
cmap
.
update
(
CorrectMap
(
answer_id
=
answer_ids
[
i
],
queuestate
=
queuestate
))
self
.
problem
.
correct_map
.
update
(
cmap
)
self
.
problem
.
correct_map
.
update
(
cmap
)
...
@@ -718,7 +721,7 @@ class CodeResponseTest(ResponseTest):
...
@@ -718,7 +721,7 @@ class CodeResponseTest(ResponseTest):
old_cmap
=
CorrectMap
()
old_cmap
=
CorrectMap
()
for
i
,
answer_id
in
enumerate
(
answer_ids
):
for
i
,
answer_id
in
enumerate
(
answer_ids
):
queuekey
=
1000
+
i
queuekey
=
1000
+
i
queuestate
=
CodeResponseTest
.
make_queuestate
(
queuekey
,
datetime
.
now
())
queuestate
=
CodeResponseTest
.
make_queuestate
(
queuekey
,
datetime
.
now
(
UTC
))
old_cmap
.
update
(
CorrectMap
(
answer_id
=
answer_ids
[
i
],
queuestate
=
queuestate
))
old_cmap
.
update
(
CorrectMap
(
answer_id
=
answer_ids
[
i
],
queuestate
=
queuestate
))
# Message format common to external graders
# Message format common to external graders
...
@@ -778,13 +781,15 @@ class CodeResponseTest(ResponseTest):
...
@@ -778,13 +781,15 @@ class CodeResponseTest(ResponseTest):
cmap
=
CorrectMap
()
cmap
=
CorrectMap
()
for
i
,
answer_id
in
enumerate
(
answer_ids
):
for
i
,
answer_id
in
enumerate
(
answer_ids
):
queuekey
=
1000
+
i
queuekey
=
1000
+
i
latest_timestamp
=
datetime
.
now
()
latest_timestamp
=
datetime
.
now
(
UTC
)
queuestate
=
CodeResponseTest
.
make_queuestate
(
queuekey
,
latest_timestamp
)
queuestate
=
CodeResponseTest
.
make_queuestate
(
queuekey
,
latest_timestamp
)
cmap
.
update
(
CorrectMap
(
answer_id
=
answer_id
,
queuestate
=
queuestate
))
cmap
.
update
(
CorrectMap
(
answer_id
=
answer_id
,
queuestate
=
queuestate
))
self
.
problem
.
correct_map
.
update
(
cmap
)
self
.
problem
.
correct_map
.
update
(
cmap
)
# Queue state only tracks up to second
# Queue state only tracks up to second
latest_timestamp
=
datetime
.
strptime
(
datetime
.
strftime
(
latest_timestamp
,
dateformat
),
dateformat
)
latest_timestamp
=
datetime
.
strptime
(
datetime
.
strftime
(
latest_timestamp
,
dateformat
),
dateformat
)
.
replace
(
tzinfo
=
UTC
)
self
.
assertEquals
(
self
.
problem
.
get_recentmost_queuetime
(),
latest_timestamp
)
self
.
assertEquals
(
self
.
problem
.
get_recentmost_queuetime
(),
latest_timestamp
)
...
...
common/lib/capa/capa/xqueue_interface.py
View file @
420b0920
...
@@ -30,9 +30,11 @@ def make_xheader(lms_callback_url, lms_key, queue_name):
...
@@ -30,9 +30,11 @@ def make_xheader(lms_callback_url, lms_key, queue_name):
'queue_name': designate a specific queue within xqueue server, e.g. 'MITx-6.00x' (string)
'queue_name': designate a specific queue within xqueue server, e.g. 'MITx-6.00x' (string)
}
}
"""
"""
return
json
.
dumps
({
'lms_callback_url'
:
lms_callback_url
,
return
json
.
dumps
({
'lms_key'
:
lms_key
,
'lms_callback_url'
:
lms_callback_url
,
'queue_name'
:
queue_name
})
'lms_key'
:
lms_key
,
'queue_name'
:
queue_name
})
def
parse_xreply
(
xreply
):
def
parse_xreply
(
xreply
):
...
@@ -60,7 +62,7 @@ class XQueueInterface(object):
...
@@ -60,7 +62,7 @@ class XQueueInterface(object):
'''
'''
def
__init__
(
self
,
url
,
django_auth
,
requests_auth
=
None
):
def
__init__
(
self
,
url
,
django_auth
,
requests_auth
=
None
):
self
.
url
=
url
self
.
url
=
url
self
.
auth
=
django_auth
self
.
auth
=
django_auth
self
.
session
=
requests
.
session
(
auth
=
requests_auth
)
self
.
session
=
requests
.
session
(
auth
=
requests_auth
)
...
@@ -95,13 +97,13 @@ class XQueueInterface(object):
...
@@ -95,13 +97,13 @@ class XQueueInterface(object):
return
(
error
,
msg
)
return
(
error
,
msg
)
def
_login
(
self
):
def
_login
(
self
):
payload
=
{
'username'
:
self
.
auth
[
'username'
],
payload
=
{
'password'
:
self
.
auth
[
'password'
]}
'username'
:
self
.
auth
[
'username'
],
'password'
:
self
.
auth
[
'password'
]
}
return
self
.
_http_post
(
self
.
url
+
'/xqueue/login/'
,
payload
)
return
self
.
_http_post
(
self
.
url
+
'/xqueue/login/'
,
payload
)
def
_send_to_queue
(
self
,
header
,
body
,
files_to_upload
):
def
_send_to_queue
(
self
,
header
,
body
,
files_to_upload
):
payload
=
{
'xqueue_header'
:
header
,
payload
=
{
'xqueue_header'
:
header
,
'xqueue_body'
:
body
}
'xqueue_body'
:
body
}
...
@@ -112,7 +114,6 @@ class XQueueInterface(object):
...
@@ -112,7 +114,6 @@ class XQueueInterface(object):
return
self
.
_http_post
(
self
.
url
+
'/xqueue/submit/'
,
payload
,
files
=
files
)
return
self
.
_http_post
(
self
.
url
+
'/xqueue/submit/'
,
payload
,
files
=
files
)
def
_http_post
(
self
,
url
,
data
,
files
=
None
):
def
_http_post
(
self
,
url
,
data
,
files
=
None
):
try
:
try
:
r
=
self
.
session
.
post
(
url
,
data
=
data
,
files
=
files
)
r
=
self
.
session
.
post
(
url
,
data
=
data
,
files
=
files
)
...
...
common/lib/xmodule/xmodule/capa_module.py
View file @
420b0920
...
@@ -1126,8 +1126,12 @@ class CapaDescriptor(CapaFields, RawDescriptor):
...
@@ -1126,8 +1126,12 @@ class CapaDescriptor(CapaFields, RawDescriptor):
mako_template
=
"widgets/problem-edit.html"
mako_template
=
"widgets/problem-edit.html"
js
=
{
'coffee'
:
[
resource_string
(
__name__
,
'js/src/problem/edit.coffee'
)]}
js
=
{
'coffee'
:
[
resource_string
(
__name__
,
'js/src/problem/edit.coffee'
)]}
js_module_name
=
"MarkdownEditingDescriptor"
js_module_name
=
"MarkdownEditingDescriptor"
css
=
{
'scss'
:
[
resource_string
(
__name__
,
'css/editor/edit.scss'
),
css
=
{
resource_string
(
__name__
,
'css/problem/edit.scss'
)]}
'scss'
:
[
resource_string
(
__name__
,
'css/editor/edit.scss'
),
resource_string
(
__name__
,
'css/problem/edit.scss'
)
]
}
# Capa modules have some additional metadata:
# Capa modules have some additional metadata:
# TODO (vshnayder): do problems have any other metadata? Do they
# TODO (vshnayder): do problems have any other metadata? Do they
...
...
common/lib/xmodule/xmodule/fields.py
View file @
420b0920
...
@@ -80,6 +80,7 @@ class Date(ModelType):
...
@@ -80,6 +80,7 @@ class Date(ModelType):
TIMEDELTA_REGEX
=
re
.
compile
(
r'^((?P<days>\d+?) day(?:s?))?(\s)?((?P<hours>\d+?) hour(?:s?))?(\s)?((?P<minutes>\d+?) minute(?:s)?)?(\s)?((?P<seconds>\d+?) second(?:s)?)?$'
)
TIMEDELTA_REGEX
=
re
.
compile
(
r'^((?P<days>\d+?) day(?:s?))?(\s)?((?P<hours>\d+?) hour(?:s?))?(\s)?((?P<minutes>\d+?) minute(?:s)?)?(\s)?((?P<seconds>\d+?) second(?:s)?)?$'
)
class
Timedelta
(
ModelType
):
class
Timedelta
(
ModelType
):
def
from_json
(
self
,
time_str
):
def
from_json
(
self
,
time_str
):
"""
"""
...
...
common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py
View file @
420b0920
...
@@ -19,6 +19,7 @@ import openendedchild
...
@@ -19,6 +19,7 @@ import openendedchild
from
numpy
import
median
from
numpy
import
median
from
datetime
import
datetime
from
datetime
import
datetime
from
pytz
import
UTC
from
.combined_open_ended_rubric
import
CombinedOpenEndedRubric
from
.combined_open_ended_rubric
import
CombinedOpenEndedRubric
...
@@ -170,7 +171,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
...
@@ -170,7 +171,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
if
xqueue
is
None
:
if
xqueue
is
None
:
return
{
'success'
:
False
,
'msg'
:
"Couldn't submit feedback."
}
return
{
'success'
:
False
,
'msg'
:
"Couldn't submit feedback."
}
qinterface
=
xqueue
[
'interface'
]
qinterface
=
xqueue
[
'interface'
]
qtime
=
datetime
.
strftime
(
datetime
.
now
(),
xqueue_interface
.
dateformat
)
qtime
=
datetime
.
strftime
(
datetime
.
now
(
UTC
),
xqueue_interface
.
dateformat
)
anonymous_student_id
=
system
.
anonymous_student_id
anonymous_student_id
=
system
.
anonymous_student_id
queuekey
=
xqueue_interface
.
make_hashkey
(
str
(
system
.
seed
)
+
qtime
+
queuekey
=
xqueue_interface
.
make_hashkey
(
str
(
system
.
seed
)
+
qtime
+
anonymous_student_id
+
anonymous_student_id
+
...
@@ -224,7 +225,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
...
@@ -224,7 +225,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
if
xqueue
is
None
:
if
xqueue
is
None
:
return
False
return
False
qinterface
=
xqueue
[
'interface'
]
qinterface
=
xqueue
[
'interface'
]
qtime
=
datetime
.
strftime
(
datetime
.
now
(),
xqueue_interface
.
dateformat
)
qtime
=
datetime
.
strftime
(
datetime
.
now
(
UTC
),
xqueue_interface
.
dateformat
)
anonymous_student_id
=
system
.
anonymous_student_id
anonymous_student_id
=
system
.
anonymous_student_id
...
...
common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py
View file @
420b0920
...
@@ -5,6 +5,7 @@ import re
...
@@ -5,6 +5,7 @@ import re
import
open_ended_image_submission
import
open_ended_image_submission
from
xmodule.progress
import
Progress
from
xmodule.progress
import
Progress
import
capa.xqueue_interface
as
xqueue_interface
from
capa.util
import
*
from
capa.util
import
*
from
.peer_grading_service
import
PeerGradingService
,
MockPeerGradingService
from
.peer_grading_service
import
PeerGradingService
,
MockPeerGradingService
import
controller_query_service
import
controller_query_service
...
@@ -334,12 +335,15 @@ class OpenEndedChild(object):
...
@@ -334,12 +335,15 @@ class OpenEndedChild(object):
log
.
exception
(
"Could not create image and check it."
)
log
.
exception
(
"Could not create image and check it."
)
if
image_ok
:
if
image_ok
:
image_key
=
image_data
.
name
+
datetime
.
now
()
.
strftime
(
"
%
Y
%
m
%
d
%
H
%
M
%
S"
)
image_key
=
image_data
.
name
+
datetime
.
now
(
UTC
)
.
strftime
(
xqueue_interface
.
dateformat
)
try
:
try
:
image_data
.
seek
(
0
)
image_data
.
seek
(
0
)
success
,
s3_public_url
=
open_ended_image_submission
.
upload_to_s3
(
image_data
,
image_key
,
success
,
s3_public_url
=
open_ended_image_submission
.
upload_to_s3
(
self
.
s3_interface
)
image_data
,
image_key
,
self
.
s3_interface
)
except
:
except
:
log
.
exception
(
"Could not upload image to S3."
)
log
.
exception
(
"Could not upload image to S3."
)
...
...
common/lib/xmodule/xmodule/tests/test_combined_open_ended.py
View file @
420b0920
...
@@ -14,6 +14,7 @@ from xmodule.modulestore import Location
...
@@ -14,6 +14,7 @@ from xmodule.modulestore import Location
from
lxml
import
etree
from
lxml
import
etree
import
capa.xqueue_interface
as
xqueue_interface
import
capa.xqueue_interface
as
xqueue_interface
from
datetime
import
datetime
from
datetime
import
datetime
from
pytz
import
UTC
import
logging
import
logging
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -212,7 +213,7 @@ class OpenEndedModuleTest(unittest.TestCase):
...
@@ -212,7 +213,7 @@ class OpenEndedModuleTest(unittest.TestCase):
'submission_id'
:
'1'
,
'submission_id'
:
'1'
,
'grader_id'
:
'1'
,
'grader_id'
:
'1'
,
'score'
:
3
}
'score'
:
3
}
qtime
=
datetime
.
strftime
(
datetime
.
now
(),
xqueue_interface
.
dateformat
)
qtime
=
datetime
.
strftime
(
datetime
.
now
(
UTC
),
xqueue_interface
.
dateformat
)
student_info
=
{
'anonymous_student_id'
:
self
.
test_system
.
anonymous_student_id
,
student_info
=
{
'anonymous_student_id'
:
self
.
test_system
.
anonymous_student_id
,
'submission_time'
:
qtime
}
'submission_time'
:
qtime
}
contents
=
{
contents
=
{
...
@@ -233,7 +234,7 @@ class OpenEndedModuleTest(unittest.TestCase):
...
@@ -233,7 +234,7 @@ class OpenEndedModuleTest(unittest.TestCase):
def
test_send_to_grader
(
self
):
def
test_send_to_grader
(
self
):
submission
=
"This is a student submission"
submission
=
"This is a student submission"
qtime
=
datetime
.
strftime
(
datetime
.
now
(),
xqueue_interface
.
dateformat
)
qtime
=
datetime
.
strftime
(
datetime
.
now
(
UTC
),
xqueue_interface
.
dateformat
)
student_info
=
{
'anonymous_student_id'
:
self
.
test_system
.
anonymous_student_id
,
student_info
=
{
'anonymous_student_id'
:
self
.
test_system
.
anonymous_student_id
,
'submission_time'
:
qtime
}
'submission_time'
:
qtime
}
contents
=
self
.
openendedmodule
.
payload
.
copy
()
contents
=
self
.
openendedmodule
.
payload
.
copy
()
...
@@ -632,6 +633,7 @@ class OpenEndedModuleXmlTest(unittest.TestCase, DummyModulestore):
...
@@ -632,6 +633,7 @@ class OpenEndedModuleXmlTest(unittest.TestCase, DummyModulestore):
module
.
handle_ajax
(
"reset"
,
{})
module
.
handle_ajax
(
"reset"
,
{})
self
.
assertEqual
(
module
.
state
,
"initial"
)
self
.
assertEqual
(
module
.
state
,
"initial"
)
class
OpenEndedModuleXmlAttemptTest
(
unittest
.
TestCase
,
DummyModulestore
):
class
OpenEndedModuleXmlAttemptTest
(
unittest
.
TestCase
,
DummyModulestore
):
"""
"""
Test if student is able to reset the problem
Test if student is able to reset the problem
...
...
i18n/tests/test_extract.py
View file @
420b0920
import
os
,
polib
import
os
import
polib
from
unittest
import
TestCase
from
unittest
import
TestCase
from
nose.plugins.skip
import
SkipTest
from
nose.plugins.skip
import
SkipTest
from
datetime
import
datetime
,
timedelta
from
datetime
import
datetime
,
timedelta
from
pytz
import
UTC
import
extract
import
extract
from
config
import
CONFIGURATION
from
config
import
CONFIGURATION
...
@@ -9,6 +11,7 @@ from config import CONFIGURATION
...
@@ -9,6 +11,7 @@ from config import CONFIGURATION
# Make sure setup runs only once
# Make sure setup runs only once
SETUP_HAS_RUN
=
False
SETUP_HAS_RUN
=
False
class
TestExtract
(
TestCase
):
class
TestExtract
(
TestCase
):
"""
"""
Tests functionality of i18n/extract.py
Tests functionality of i18n/extract.py
...
@@ -19,20 +22,20 @@ class TestExtract(TestCase):
...
@@ -19,20 +22,20 @@ class TestExtract(TestCase):
# Skip this test because it takes too long (>1 minute)
# Skip this test because it takes too long (>1 minute)
# TODO: figure out how to declare a "long-running" test suite
# TODO: figure out how to declare a "long-running" test suite
# and add this test to it.
# and add this test to it.
raise
SkipTest
()
raise
SkipTest
()
global
SETUP_HAS_RUN
global
SETUP_HAS_RUN
# Subtract 1 second to help comparisons with file-modify time succeed,
# Subtract 1 second to help comparisons with file-modify time succeed,
# since os.path.getmtime() is not millisecond-accurate
# since os.path.getmtime() is not millisecond-accurate
self
.
start_time
=
datetime
.
now
()
-
timedelta
(
seconds
=
1
)
self
.
start_time
=
datetime
.
now
(
UTC
)
-
timedelta
(
seconds
=
1
)
super
(
TestExtract
,
self
)
.
setUp
()
super
(
TestExtract
,
self
)
.
setUp
()
if
not
SETUP_HAS_RUN
:
if
not
SETUP_HAS_RUN
:
# Run extraction script. Warning, this takes 1 minute or more
# Run extraction script. Warning, this takes 1 minute or more
extract
.
main
()
extract
.
main
()
SETUP_HAS_RUN
=
True
SETUP_HAS_RUN
=
True
def
get_files
(
self
):
def
get_files
(
self
):
"""
"""
This is a generator.
This is a generator.
Returns the fully expanded filenames for all extracted files
Returns the fully expanded filenames for all extracted files
...
@@ -65,19 +68,21 @@ class TestExtract(TestCase):
...
@@ -65,19 +68,21 @@ class TestExtract(TestCase):
entry2
.
msgid
=
"This is not a keystring"
entry2
.
msgid
=
"This is not a keystring"
self
.
assertTrue
(
extract
.
is_key_string
(
entry1
.
msgid
))
self
.
assertTrue
(
extract
.
is_key_string
(
entry1
.
msgid
))
self
.
assertFalse
(
extract
.
is_key_string
(
entry2
.
msgid
))
self
.
assertFalse
(
extract
.
is_key_string
(
entry2
.
msgid
))
def
test_headers
(
self
):
def
test_headers
(
self
):
"""Verify all headers have been modified"""
"""Verify all headers have been modified"""
for
path
in
self
.
get_files
():
for
path
in
self
.
get_files
():
po
=
polib
.
pofile
(
path
)
po
=
polib
.
pofile
(
path
)
header
=
po
.
header
header
=
po
.
header
self
.
assertEqual
(
header
.
find
(
'edX translation file'
),
0
,
self
.
assertEqual
(
msg
=
'Missing header in
%
s:
\n
"
%
s"'
%
\
header
.
find
(
'edX translation file'
),
(
os
.
path
.
basename
(
path
),
header
))
0
,
msg
=
'Missing header in
%
s:
\n
"
%
s"'
%
(
os
.
path
.
basename
(
path
),
header
)
)
def
test_metadata
(
self
):
def
test_metadata
(
self
):
"""Verify all metadata has been modified"""
"""Verify all metadata has been modified"""
for
path
in
self
.
get_files
():
for
path
in
self
.
get_files
():
po
=
polib
.
pofile
(
path
)
po
=
polib
.
pofile
(
path
)
metadata
=
po
.
metadata
metadata
=
po
.
metadata
value
=
metadata
[
'Report-Msgid-Bugs-To'
]
value
=
metadata
[
'Report-Msgid-Bugs-To'
]
...
...
i18n/tests/test_generate.py
View file @
420b0920
import
os
,
string
,
random
,
re
import
os
import
string
import
random
import
re
from
polib
import
pofile
from
polib
import
pofile
from
unittest
import
TestCase
from
unittest
import
TestCase
from
datetime
import
datetime
,
timedelta
from
datetime
import
datetime
,
timedelta
from
pytz
import
UTC
import
generate
import
generate
from
config
import
CONFIGURATION
from
config
import
CONFIGURATION
class
TestGenerate
(
TestCase
):
class
TestGenerate
(
TestCase
):
"""
"""
Tests functionality of i18n/generate.py
Tests functionality of i18n/generate.py
...
@@ -15,7 +20,7 @@ class TestGenerate(TestCase):
...
@@ -15,7 +20,7 @@ class TestGenerate(TestCase):
def
setUp
(
self
):
def
setUp
(
self
):
# Subtract 1 second to help comparisons with file-modify time succeed,
# Subtract 1 second to help comparisons with file-modify time succeed,
# since os.path.getmtime() is not millisecond-accurate
# since os.path.getmtime() is not millisecond-accurate
self
.
start_time
=
datetime
.
now
()
-
timedelta
(
seconds
=
1
)
self
.
start_time
=
datetime
.
now
(
UTC
)
-
timedelta
(
seconds
=
1
)
def
test_merge
(
self
):
def
test_merge
(
self
):
"""
"""
...
@@ -49,7 +54,7 @@ class TestGenerate(TestCase):
...
@@ -49,7 +54,7 @@ class TestGenerate(TestCase):
"""
"""
This is invoked by test_main to ensure that it runs after
This is invoked by test_main to ensure that it runs after
calling generate.main().
calling generate.main().
There should be exactly three merge comment headers
There should be exactly three merge comment headers
in our merged .po file. This counts them to be sure.
in our merged .po file. This counts them to be sure.
A merge comment looks like this:
A merge comment looks like this:
...
...
lms/djangoapps/certificates/management/commands/ungenerated_certs.py
View file @
420b0920
...
@@ -8,6 +8,7 @@ from xmodule.course_module import CourseDescriptor
...
@@ -8,6 +8,7 @@ from xmodule.course_module import CourseDescriptor
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
from
certificates.models
import
CertificateStatuses
from
certificates.models
import
CertificateStatuses
import
datetime
import
datetime
from
pytz
import
UTC
class
Command
(
BaseCommand
):
class
Command
(
BaseCommand
):
...
@@ -41,7 +42,6 @@ class Command(BaseCommand):
...
@@ -41,7 +42,6 @@ class Command(BaseCommand):
'whose entry in the certificate table matches STATUS. '
'whose entry in the certificate table matches STATUS. '
'STATUS can be generating, unavailable, deleted, error '
'STATUS can be generating, unavailable, deleted, error '
'or notpassing.'
),
'or notpassing.'
),
)
)
def
handle
(
self
,
*
args
,
**
options
):
def
handle
(
self
,
*
args
,
**
options
):
...
@@ -83,20 +83,20 @@ class Command(BaseCommand):
...
@@ -83,20 +83,20 @@ class Command(BaseCommand):
xq
=
XQueueCertInterface
()
xq
=
XQueueCertInterface
()
total
=
enrolled_students
.
count
()
total
=
enrolled_students
.
count
()
count
=
0
count
=
0
start
=
datetime
.
datetime
.
now
()
start
=
datetime
.
datetime
.
now
(
UTC
)
for
student
in
enrolled_students
:
for
student
in
enrolled_students
:
count
+=
1
count
+=
1
if
count
%
STATUS_INTERVAL
==
0
:
if
count
%
STATUS_INTERVAL
==
0
:
# Print a status update with an approximation of
# Print a status update with an approximation of
# how much time is left based on how long the last
# how much time is left based on how long the last
# interval took
# interval took
diff
=
datetime
.
datetime
.
now
()
-
start
diff
=
datetime
.
datetime
.
now
(
UTC
)
-
start
timeleft
=
diff
*
(
total
-
count
)
/
STATUS_INTERVAL
timeleft
=
diff
*
(
total
-
count
)
/
STATUS_INTERVAL
hours
,
remainder
=
divmod
(
timeleft
.
seconds
,
3600
)
hours
,
remainder
=
divmod
(
timeleft
.
seconds
,
3600
)
minutes
,
seconds
=
divmod
(
remainder
,
60
)
minutes
,
seconds
=
divmod
(
remainder
,
60
)
print
"{0}/{1} completed ~{2:02}:{3:02}m remaining"
.
format
(
print
"{0}/{1} completed ~{2:02}:{3:02}m remaining"
.
format
(
count
,
total
,
hours
,
minutes
)
count
,
total
,
hours
,
minutes
)
start
=
datetime
.
datetime
.
now
()
start
=
datetime
.
datetime
.
now
(
UTC
)
if
certificate_status_for_student
(
if
certificate_status_for_student
(
student
,
course_id
)[
'status'
]
in
valid_statuses
:
student
,
course_id
)[
'status'
]
in
valid_statuses
:
...
...
lms/djangoapps/courseware/module_render.py
View file @
420b0920
...
@@ -213,22 +213,28 @@ def get_module_for_descriptor_internal(user, descriptor, model_data_cache, cours
...
@@ -213,22 +213,28 @@ def get_module_for_descriptor_internal(user, descriptor, model_data_cache, cours
return
None
return
None
# Setup system context for module instance
# Setup system context for module instance
ajax_url
=
reverse
(
'modx_dispatch'
,
ajax_url
=
reverse
(
kwargs
=
dict
(
course_id
=
course_id
,
'modx_dispatch'
,
location
=
descriptor
.
location
.
url
(),
kwargs
=
dict
(
dispatch
=
''
),
course_id
=
course_id
,
)
location
=
descriptor
.
location
.
url
(),
dispatch
=
''
),
)
# Intended use is as {ajax_url}/{dispatch_command}, so get rid of the trailing slash.
# Intended use is as {ajax_url}/{dispatch_command}, so get rid of the trailing slash.
ajax_url
=
ajax_url
.
rstrip
(
'/'
)
ajax_url
=
ajax_url
.
rstrip
(
'/'
)
def
make_xqueue_callback
(
dispatch
=
'score_update'
):
def
make_xqueue_callback
(
dispatch
=
'score_update'
):
# Fully qualified callback URL for external queueing system
# Fully qualified callback URL for external queueing system
relative_xqueue_callback_url
=
reverse
(
'xqueue_callback'
,
relative_xqueue_callback_url
=
reverse
(
kwargs
=
dict
(
course_id
=
course_id
,
'xqueue_callback'
,
userid
=
str
(
user
.
id
),
kwargs
=
dict
(
mod_id
=
descriptor
.
location
.
url
(),
course_id
=
course_id
,
dispatch
=
dispatch
),
userid
=
str
(
user
.
id
),
)
mod_id
=
descriptor
.
location
.
url
(),
dispatch
=
dispatch
),
)
return
xqueue_callback_url_prefix
+
relative_xqueue_callback_url
return
xqueue_callback_url_prefix
+
relative_xqueue_callback_url
# Default queuename is course-specific and is derived from the course that
# Default queuename is course-specific and is derived from the course that
...
@@ -313,10 +319,12 @@ def get_module_for_descriptor_internal(user, descriptor, model_data_cache, cours
...
@@ -313,10 +319,12 @@ def get_module_for_descriptor_internal(user, descriptor, model_data_cache, cours
score_bucket
=
get_score_bucket
(
student_module
.
grade
,
student_module
.
max_grade
)
score_bucket
=
get_score_bucket
(
student_module
.
grade
,
student_module
.
max_grade
)
org
,
course_num
,
run
=
course_id
.
split
(
"/"
)
org
,
course_num
,
run
=
course_id
.
split
(
"/"
)
tags
=
[
"org:{0}"
.
format
(
org
),
tags
=
[
"course:{0}"
.
format
(
course_num
),
"org:{0}"
.
format
(
org
),
"run:{0}"
.
format
(
run
),
"course:{0}"
.
format
(
course_num
),
"score_bucket:{0}"
.
format
(
score_bucket
)]
"run:{0}"
.
format
(
run
),
"score_bucket:{0}"
.
format
(
score_bucket
)
]
if
grade_bucket_type
is
not
None
:
if
grade_bucket_type
is
not
None
:
tags
.
append
(
'type:
%
s'
%
grade_bucket_type
)
tags
.
append
(
'type:
%
s'
%
grade_bucket_type
)
...
@@ -326,38 +334,41 @@ def get_module_for_descriptor_internal(user, descriptor, model_data_cache, cours
...
@@ -326,38 +334,41 @@ def get_module_for_descriptor_internal(user, descriptor, model_data_cache, cours
# TODO (cpennington): When modules are shared between courses, the static
# TODO (cpennington): When modules are shared between courses, the static
# prefix is going to have to be specific to the module, not the directory
# prefix is going to have to be specific to the module, not the directory
# that the xml was loaded from
# that the xml was loaded from
system
=
ModuleSystem
(
track_function
=
track_function
,
system
=
ModuleSystem
(
render_template
=
render_to_string
,
track_function
=
track_function
,
ajax_url
=
ajax_url
,
render_template
=
render_to_string
,
xqueue
=
xqueue
,
ajax_url
=
ajax_url
,
# TODO (cpennington): Figure out how to share info between systems
xqueue
=
xqueue
,
filestore
=
descriptor
.
system
.
resources_fs
,
# TODO (cpennington): Figure out how to share info between systems
get_module
=
inner_get_module
,
filestore
=
descriptor
.
system
.
resources_fs
,
user
=
user
,
get_module
=
inner_get_module
,
# TODO (cpennington): This should be removed when all html from
user
=
user
,
# a module is coming through get_html and is therefore covered
# TODO (cpennington): This should be removed when all html from
# by the replace_static_urls code below
# a module is coming through get_html and is therefore covered
replace_urls
=
partial
(
# by the replace_static_urls code below
static_replace
.
replace_static_urls
,
replace_urls
=
partial
(
data_directory
=
getattr
(
descriptor
,
'data_dir'
,
None
),
static_replace
.
replace_static_urls
,
course_namespace
=
descriptor
.
location
.
_replace
(
category
=
None
,
name
=
None
),
data_directory
=
getattr
(
descriptor
,
'data_dir'
,
None
),
),
course_namespace
=
descriptor
.
location
.
_replace
(
category
=
None
,
name
=
None
),
node_path
=
settings
.
NODE_PATH
,
),
xblock_model_data
=
xblock_model_data
,
node_path
=
settings
.
NODE_PATH
,
publish
=
publish
,
xblock_model_data
=
xblock_model_data
,
anonymous_student_id
=
unique_id_for_user
(
user
),
publish
=
publish
,
course_id
=
course_id
,
anonymous_student_id
=
unique_id_for_user
(
user
),
open_ended_grading_interface
=
open_ended_grading_interface
,
course_id
=
course_id
,
s3_interface
=
s3_interface
,
open_ended_grading_interface
=
open_ended_grading_interface
,
cache
=
cache
,
s3_interface
=
s3_interface
,
can_execute_unsafe_code
=
(
lambda
:
can_execute_unsafe_code
(
course_id
)),
cache
=
cache
,
)
can_execute_unsafe_code
=
(
lambda
:
can_execute_unsafe_code
(
course_id
)),
)
# pass position specified in URL to module through ModuleSystem
# pass position specified in URL to module through ModuleSystem
system
.
set
(
'position'
,
position
)
system
.
set
(
'position'
,
position
)
system
.
set
(
'DEBUG'
,
settings
.
DEBUG
)
system
.
set
(
'DEBUG'
,
settings
.
DEBUG
)
if
settings
.
MITX_FEATURES
.
get
(
'ENABLE_PSYCHOMETRICS'
):
if
settings
.
MITX_FEATURES
.
get
(
'ENABLE_PSYCHOMETRICS'
):
system
.
set
(
'psychometrics_handler'
,
# set callback for updating PsychometricsData
system
.
set
(
make_psychometrics_data_update_handler
(
course_id
,
user
,
descriptor
.
location
.
url
()))
'psychometrics_handler'
,
# set callback for updating PsychometricsData
make_psychometrics_data_update_handler
(
course_id
,
user
,
descriptor
.
location
.
url
())
)
try
:
try
:
module
=
descriptor
.
xmodule
(
system
)
module
=
descriptor
.
xmodule
(
system
)
...
@@ -381,13 +392,14 @@ def get_module_for_descriptor_internal(user, descriptor, model_data_cache, cours
...
@@ -381,13 +392,14 @@ def get_module_for_descriptor_internal(user, descriptor, model_data_cache, cours
system
.
set
(
'user_is_staff'
,
has_access
(
user
,
descriptor
.
location
,
'staff'
,
course_id
))
system
.
set
(
'user_is_staff'
,
has_access
(
user
,
descriptor
.
location
,
'staff'
,
course_id
))
_get_html
=
module
.
get_html
_get_html
=
module
.
get_html
if
wrap_xmodule_display
==
True
:
if
wrap_xmodule_display
is
True
:
_get_html
=
wrap_xmodule
(
module
.
get_html
,
module
,
'xmodule_display.html'
)
_get_html
=
wrap_xmodule
(
module
.
get_html
,
module
,
'xmodule_display.html'
)
module
.
get_html
=
replace_static_urls
(
module
.
get_html
=
replace_static_urls
(
_get_html
,
_get_html
,
getattr
(
descriptor
,
'data_dir'
,
None
),
getattr
(
descriptor
,
'data_dir'
,
None
),
course_namespace
=
module
.
location
.
_replace
(
category
=
None
,
name
=
None
))
course_namespace
=
module
.
location
.
_replace
(
category
=
None
,
name
=
None
)
)
# Allow URLs of the form '/course/' refer to the root of multicourse directory
# Allow URLs of the form '/course/' refer to the root of multicourse directory
# hierarchy of this course
# hierarchy of this course
...
...
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