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
d7194e6b
Commit
d7194e6b
authored
May 29, 2013
by
Don Mitchell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
struct_time to datetime conversion.
parent
673c015e
Hide whitespace changes
Inline
Side-by-side
Showing
32 changed files
with
293 additions
and
339 deletions
+293
-339
cms/djangoapps/contentstore/tests/test_contentstore.py
+3
-3
cms/djangoapps/contentstore/tests/test_course_settings.py
+2
-8
cms/djangoapps/contentstore/views/assets.py
+3
-3
cms/djangoapps/contentstore/views/course.py
+17
-16
cms/djangoapps/models/settings/course_details.py
+10
-10
cms/templates/edit_subsection.html
+14
-6
cms/templates/overview.html
+5
-4
common/djangoapps/contentserver/middleware.py
+2
-5
common/djangoapps/student/management/commands/pearson_make_tc_registration.py
+2
-3
common/djangoapps/student/models.py
+8
-9
common/djangoapps/xmodule_modifiers.py
+5
-4
common/lib/capa/capa/inputtypes.py
+6
-6
common/lib/xmodule/xmodule/capa_module.py
+4
-4
common/lib/xmodule/xmodule/course_module.py
+23
-22
common/lib/xmodule/xmodule/fields.py
+16
-8
common/lib/xmodule/xmodule/foldit_module.py
+3
-6
common/lib/xmodule/xmodule/modulestore/draft.py
+2
-1
common/lib/xmodule/xmodule/modulestore/xml.py
+3
-3
common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py
+18
-17
common/lib/xmodule/xmodule/peer_grading_module.py
+24
-23
common/lib/xmodule/xmodule/tests/test_course_module.py
+5
-6
common/lib/xmodule/xmodule/tests/test_date_utils.py
+0
-35
common/lib/xmodule/xmodule/tests/test_fields.py
+8
-17
common/lib/xmodule/xmodule/tests/test_import.py
+21
-8
common/lib/xmodule/xmodule/timeinfo.py
+1
-2
common/lib/xmodule/xmodule/timeparse.py
+5
-6
common/lib/xmodule/xmodule/util/date_utils.py
+13
-27
common/test/data/full/sequential/Administrivia_and_Circuit_Elements.xml
+31
-21
lms/djangoapps/courseware/access.py
+8
-12
lms/djangoapps/courseware/tests/test_access.py
+6
-12
lms/djangoapps/courseware/tests/tests.py
+20
-21
lms/djangoapps/django_comment_client/utils.py
+5
-11
No files found.
cms/djangoapps/contentstore/tests/test_contentstore.py
View file @
d7194e6b
...
@@ -271,7 +271,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -271,7 +271,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
)
)
self
.
assertTrue
(
getattr
(
draft_problem
,
'is_draft'
,
False
))
self
.
assertTrue
(
getattr
(
draft_problem
,
'is_draft'
,
False
))
#now requery with depth
#
now requery with depth
course
=
modulestore
(
'draft'
)
.
get_item
(
course
=
modulestore
(
'draft'
)
.
get_item
(
Location
([
'i4x'
,
'edX'
,
'simple'
,
'course'
,
'2012_Fall'
,
None
]),
Location
([
'i4x'
,
'edX'
,
'simple'
,
'course'
,
'2012_Fall'
,
None
]),
depth
=
None
depth
=
None
...
@@ -539,7 +539,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -539,7 +539,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
on_disk
=
loads
(
grading_policy
.
read
())
on_disk
=
loads
(
grading_policy
.
read
())
self
.
assertEqual
(
on_disk
,
course
.
grading_policy
)
self
.
assertEqual
(
on_disk
,
course
.
grading_policy
)
#check for policy.json
#
check for policy.json
self
.
assertTrue
(
filesystem
.
exists
(
'policy.json'
))
self
.
assertTrue
(
filesystem
.
exists
(
'policy.json'
))
# compare what's on disk to what we have in the course module
# compare what's on disk to what we have in the course module
...
@@ -990,7 +990,7 @@ class ContentStoreTest(ModuleStoreTestCase):
...
@@ -990,7 +990,7 @@ class ContentStoreTest(ModuleStoreTestCase):
def
test_metadata_inheritance
(
self
):
def
test_metadata_inheritance
(
self
):
module_store
=
modulestore
(
'direct'
)
module_store
=
modulestore
(
'direct'
)
import_from_xml
(
module_store
,
'common/test/data/'
,
[
'full'
])
import_from_xml
(
module_store
,
'common/test/data/'
,
[
'full'
]
,
verbose
=
True
)
course
=
module_store
.
get_item
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'course'
,
'6.002_Spring_2012'
,
None
]))
course
=
module_store
.
get_item
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'course'
,
'6.002_Spring_2012'
,
None
]))
...
...
cms/djangoapps/contentstore/tests/test_course_settings.py
View file @
d7194e6b
...
@@ -151,22 +151,16 @@ class CourseDetailsViewTest(CourseTestCase):
...
@@ -151,22 +151,16 @@ class CourseDetailsViewTest(CourseTestCase):
self
.
assertEqual
(
details
[
'intro_video'
],
encoded
.
get
(
'intro_video'
,
None
),
context
+
" intro_video not =="
)
self
.
assertEqual
(
details
[
'intro_video'
],
encoded
.
get
(
'intro_video'
,
None
),
context
+
" intro_video not =="
)
self
.
assertEqual
(
details
[
'effort'
],
encoded
[
'effort'
],
context
+
" efforts not =="
)
self
.
assertEqual
(
details
[
'effort'
],
encoded
[
'effort'
],
context
+
" efforts not =="
)
@staticmethod
def
struct_to_datetime
(
struct_time
):
return
datetime
.
datetime
(
*
struct_time
[:
6
],
tzinfo
=
UTC
())
def
compare_date_fields
(
self
,
details
,
encoded
,
context
,
field
):
def
compare_date_fields
(
self
,
details
,
encoded
,
context
,
field
):
if
details
[
field
]
is
not
None
:
if
details
[
field
]
is
not
None
:
date
=
Date
()
date
=
Date
()
if
field
in
encoded
and
encoded
[
field
]
is
not
None
:
if
field
in
encoded
and
encoded
[
field
]
is
not
None
:
encoded_encoded
=
date
.
from_json
(
encoded
[
field
])
dt1
=
date
.
from_json
(
encoded
[
field
])
dt1
=
CourseDetailsViewTest
.
struct_to_datetime
(
encoded_encoded
)
if
isinstance
(
details
[
field
],
datetime
.
datetime
):
if
isinstance
(
details
[
field
],
datetime
.
datetime
):
dt2
=
details
[
field
]
dt2
=
details
[
field
]
else
:
else
:
details_encoded
=
date
.
from_json
(
details
[
field
])
dt2
=
date
.
from_json
(
details
[
field
])
dt2
=
CourseDetailsViewTest
.
struct_to_datetime
(
details_encoded
)
expected_delta
=
datetime
.
timedelta
(
0
)
expected_delta
=
datetime
.
timedelta
(
0
)
self
.
assertEqual
(
dt1
-
dt2
,
expected_delta
,
str
(
dt1
)
+
"!="
+
str
(
dt2
)
+
" at "
+
context
)
self
.
assertEqual
(
dt1
-
dt2
,
expected_delta
,
str
(
dt1
)
+
"!="
+
str
(
dt2
)
+
" at "
+
context
)
...
...
cms/djangoapps/contentstore/views/assets.py
View file @
d7194e6b
...
@@ -62,7 +62,7 @@ def asset_index(request, org, course, name):
...
@@ -62,7 +62,7 @@ def asset_index(request, org, course, name):
asset_id
=
asset
[
'_id'
]
asset_id
=
asset
[
'_id'
]
display_info
=
{}
display_info
=
{}
display_info
[
'displayname'
]
=
asset
[
'displayname'
]
display_info
[
'displayname'
]
=
asset
[
'displayname'
]
display_info
[
'uploadDate'
]
=
get_default_time_display
(
asset
[
'uploadDate'
]
.
timetuple
()
)
display_info
[
'uploadDate'
]
=
get_default_time_display
(
asset
[
'uploadDate'
])
asset_location
=
StaticContent
.
compute_location
(
asset_id
[
'org'
],
asset_id
[
'course'
],
asset_id
[
'name'
])
asset_location
=
StaticContent
.
compute_location
(
asset_id
[
'org'
],
asset_id
[
'course'
],
asset_id
[
'name'
])
display_info
[
'url'
]
=
StaticContent
.
get_url_path_from_location
(
asset_location
)
display_info
[
'url'
]
=
StaticContent
.
get_url_path_from_location
(
asset_location
)
...
@@ -131,7 +131,7 @@ def upload_asset(request, org, course, coursename):
...
@@ -131,7 +131,7 @@ def upload_asset(request, org, course, coursename):
readback
=
contentstore
()
.
find
(
content
.
location
)
readback
=
contentstore
()
.
find
(
content
.
location
)
response_payload
=
{
'displayname'
:
content
.
name
,
response_payload
=
{
'displayname'
:
content
.
name
,
'uploadDate'
:
get_default_time_display
(
readback
.
last_modified_at
.
timetuple
()
),
'uploadDate'
:
get_default_time_display
(
readback
.
last_modified_at
),
'url'
:
StaticContent
.
get_url_path_from_location
(
content
.
location
),
'url'
:
StaticContent
.
get_url_path_from_location
(
content
.
location
),
'thumb_url'
:
StaticContent
.
get_url_path_from_location
(
thumbnail_location
)
if
thumbnail_content
is
not
None
else
None
,
'thumb_url'
:
StaticContent
.
get_url_path_from_location
(
thumbnail_location
)
if
thumbnail_content
is
not
None
else
None
,
'msg'
:
'Upload completed'
'msg'
:
'Upload completed'
...
@@ -231,7 +231,7 @@ def generate_export_course(request, org, course, name):
...
@@ -231,7 +231,7 @@ def generate_export_course(request, org, course, name):
logging
.
debug
(
'root = {0}'
.
format
(
root_dir
))
logging
.
debug
(
'root = {0}'
.
format
(
root_dir
))
export_to_xml
(
modulestore
(
'direct'
),
contentstore
(),
loc
,
root_dir
,
name
,
modulestore
())
export_to_xml
(
modulestore
(
'direct'
),
contentstore
(),
loc
,
root_dir
,
name
,
modulestore
())
#filename = root_dir / name + '.tar.gz'
#
filename = root_dir / name + '.tar.gz'
logging
.
debug
(
'tar file being generated at {0}'
.
format
(
export_file
.
name
))
logging
.
debug
(
'tar file being generated at {0}'
.
format
(
export_file
.
name
))
tar_file
=
tarfile
.
open
(
name
=
export_file
.
name
,
mode
=
'w:gz'
)
tar_file
=
tarfile
.
open
(
name
=
export_file
.
name
,
mode
=
'w:gz'
)
...
...
cms/djangoapps/contentstore/views/course.py
View file @
d7194e6b
...
@@ -2,7 +2,6 @@
...
@@ -2,7 +2,6 @@
Views related to operations on course objects
Views related to operations on course objects
"""
"""
import
json
import
json
import
time
from
django.contrib.auth.decorators
import
login_required
from
django.contrib.auth.decorators
import
login_required
from
django_future.csrf
import
ensure_csrf_cookie
from
django_future.csrf
import
ensure_csrf_cookie
...
@@ -32,6 +31,8 @@ from .component import OPEN_ENDED_COMPONENT_TYPES, \
...
@@ -32,6 +31,8 @@ from .component import OPEN_ENDED_COMPONENT_TYPES, \
NOTE_COMPONENT_TYPES
,
ADVANCED_COMPONENT_POLICY_KEY
NOTE_COMPONENT_TYPES
,
ADVANCED_COMPONENT_POLICY_KEY
from
django_comment_common.utils
import
seed_permissions_roles
from
django_comment_common.utils
import
seed_permissions_roles
import
datetime
from
django.utils.timezone
import
UTC
# TODO: should explicitly enumerate exports with __all__
# TODO: should explicitly enumerate exports with __all__
...
@@ -130,7 +131,7 @@ def create_new_course(request):
...
@@ -130,7 +131,7 @@ def create_new_course(request):
new_course
.
display_name
=
display_name
new_course
.
display_name
=
display_name
# set a default start date to now
# set a default start date to now
new_course
.
start
=
time
.
gmtime
(
)
new_course
.
start
=
datetime
.
datetime
.
now
(
UTC
()
)
initialize_course_tabs
(
new_course
)
initialize_course_tabs
(
new_course
)
...
@@ -357,49 +358,49 @@ def course_advanced_updates(request, org, course, name):
...
@@ -357,49 +358,49 @@ def course_advanced_updates(request, org, course, name):
# Whether or not to filter the tabs key out of the settings metadata
# Whether or not to filter the tabs key out of the settings metadata
filter_tabs
=
True
filter_tabs
=
True
#Check to see if the user instantiated any advanced components. This is a hack
#
Check to see if the user instantiated any advanced components. This is a hack
#
that does the following :
#
that does the following :
# 1) adds/removes the open ended panel tab to a course automatically if the user
# 1) adds/removes the open ended panel tab to a course automatically if the user
# has indicated that they want to edit the combinedopendended or peergrading module
# has indicated that they want to edit the combinedopendended or peergrading module
# 2) adds/removes the notes panel tab to a course automatically if the user has
# 2) adds/removes the notes panel tab to a course automatically if the user has
# indicated that they want the notes module enabled in their course
# indicated that they want the notes module enabled in their course
# TODO refactor the above into distinct advanced policy settings
# TODO refactor the above into distinct advanced policy settings
if
ADVANCED_COMPONENT_POLICY_KEY
in
request_body
:
if
ADVANCED_COMPONENT_POLICY_KEY
in
request_body
:
#Get the course so that we can scrape current tabs
#
Get the course so that we can scrape current tabs
course_module
=
modulestore
()
.
get_item
(
location
)
course_module
=
modulestore
()
.
get_item
(
location
)
#
Maps tab types to components
#
Maps tab types to components
tab_component_map
=
{
tab_component_map
=
{
'open_ended'
:
OPEN_ENDED_COMPONENT_TYPES
,
'open_ended'
:
OPEN_ENDED_COMPONENT_TYPES
,
'notes'
:
NOTE_COMPONENT_TYPES
,
'notes'
:
NOTE_COMPONENT_TYPES
,
}
}
#Check to see if the user instantiated any notes or open ended components
#
Check to see if the user instantiated any notes or open ended components
for
tab_type
in
tab_component_map
.
keys
():
for
tab_type
in
tab_component_map
.
keys
():
component_types
=
tab_component_map
.
get
(
tab_type
)
component_types
=
tab_component_map
.
get
(
tab_type
)
found_ac_type
=
False
found_ac_type
=
False
for
ac_type
in
component_types
:
for
ac_type
in
component_types
:
if
ac_type
in
request_body
[
ADVANCED_COMPONENT_POLICY_KEY
]:
if
ac_type
in
request_body
[
ADVANCED_COMPONENT_POLICY_KEY
]:
#Add tab to the course if needed
#
Add tab to the course if needed
changed
,
new_tabs
=
add_extra_panel_tab
(
tab_type
,
course_module
)
changed
,
new_tabs
=
add_extra_panel_tab
(
tab_type
,
course_module
)
#If a tab has been added to the course, then send the metadata along to CourseMetadata.update_from_json
#
If a tab has been added to the course, then send the metadata along to CourseMetadata.update_from_json
if
changed
:
if
changed
:
course_module
.
tabs
=
new_tabs
course_module
.
tabs
=
new_tabs
request_body
.
update
({
'tabs'
:
new_tabs
})
request_body
.
update
({
'tabs'
:
new_tabs
})
#Indicate that tabs should not be filtered out of the metadata
#
Indicate that tabs should not be filtered out of the metadata
filter_tabs
=
False
filter_tabs
=
False
#Set this flag to avoid the tab removal code below.
#
Set this flag to avoid the tab removal code below.
found_ac_type
=
True
found_ac_type
=
True
break
break
#If we did not find a module type in the advanced settings,
#
If we did not find a module type in the advanced settings,
# we may need to remove the tab from the course.
# we may need to remove the tab from the course.
if
not
found_ac_type
:
if
not
found_ac_type
:
#Remove tab from the course if needed
#
Remove tab from the course if needed
changed
,
new_tabs
=
remove_extra_panel_tab
(
tab_type
,
course_module
)
changed
,
new_tabs
=
remove_extra_panel_tab
(
tab_type
,
course_module
)
if
changed
:
if
changed
:
course_module
.
tabs
=
new_tabs
course_module
.
tabs
=
new_tabs
request_body
.
update
({
'tabs'
:
new_tabs
})
request_body
.
update
({
'tabs'
:
new_tabs
})
#Indicate that tabs should *not* be filtered out of the metadata
#
Indicate that tabs should *not* be filtered out of the metadata
filter_tabs
=
False
filter_tabs
=
False
response_json
=
json
.
dumps
(
CourseMetadata
.
update_from_json
(
location
,
response_json
=
json
.
dumps
(
CourseMetadata
.
update_from_json
(
location
,
...
...
cms/djangoapps/models/settings/course_details.py
View file @
d7194e6b
...
@@ -3,26 +3,26 @@ from xmodule.modulestore.exceptions import ItemNotFoundError
...
@@ -3,26 +3,26 @@ from xmodule.modulestore.exceptions import ItemNotFoundError
from
xmodule.modulestore.inheritance
import
own_metadata
from
xmodule.modulestore.inheritance
import
own_metadata
import
json
import
json
from
json.encoder
import
JSONEncoder
from
json.encoder
import
JSONEncoder
import
time
from
contentstore.utils
import
get_modulestore
from
contentstore.utils
import
get_modulestore
from
models.settings
import
course_grading
from
models.settings
import
course_grading
from
contentstore.utils
import
update_item
from
contentstore.utils
import
update_item
from
xmodule.fields
import
Date
from
xmodule.fields
import
Date
import
re
import
re
import
logging
import
logging
import
datetime
class
CourseDetails
(
object
):
class
CourseDetails
(
object
):
def
__init__
(
self
,
location
):
def
__init__
(
self
,
location
):
self
.
course_location
=
location
# a Location obj
self
.
course_location
=
location
# a Location obj
self
.
start_date
=
None
# 'start'
self
.
start_date
=
None
# 'start'
self
.
end_date
=
None
# 'end'
self
.
end_date
=
None
# 'end'
self
.
enrollment_start
=
None
self
.
enrollment_start
=
None
self
.
enrollment_end
=
None
self
.
enrollment_end
=
None
self
.
syllabus
=
None
# a pdf file asset
self
.
syllabus
=
None
# a pdf file asset
self
.
overview
=
""
# html to render as the overview
self
.
overview
=
""
# html to render as the overview
self
.
intro_video
=
None
# a video pointer
self
.
intro_video
=
None
# a video pointer
self
.
effort
=
None
# int hours/week
self
.
effort
=
None
# int hours/week
@classmethod
@classmethod
def
fetch
(
cls
,
course_location
):
def
fetch
(
cls
,
course_location
):
...
@@ -73,9 +73,9 @@ class CourseDetails(object):
...
@@ -73,9 +73,9 @@ class CourseDetails(object):
"""
"""
Decode the json into CourseDetails and save any changed attrs to the db
Decode the json into CourseDetails and save any changed attrs to the db
"""
"""
## TODO make it an error for this to be undefined & for it to not be retrievable from modulestore
#
# TODO make it an error for this to be undefined & for it to not be retrievable from modulestore
course_location
=
jsondict
[
'course_location'
]
course_location
=
jsondict
[
'course_location'
]
## Will probably want to cache the inflight courses because every blur generates an update
#
# Will probably want to cache the inflight courses because every blur generates an update
descriptor
=
get_modulestore
(
course_location
)
.
get_item
(
course_location
)
descriptor
=
get_modulestore
(
course_location
)
.
get_item
(
course_location
)
dirty
=
False
dirty
=
False
...
@@ -181,7 +181,7 @@ class CourseSettingsEncoder(json.JSONEncoder):
...
@@ -181,7 +181,7 @@ class CourseSettingsEncoder(json.JSONEncoder):
return
obj
.
__dict__
return
obj
.
__dict__
elif
isinstance
(
obj
,
Location
):
elif
isinstance
(
obj
,
Location
):
return
obj
.
dict
()
return
obj
.
dict
()
elif
isinstance
(
obj
,
time
.
struct_
time
):
elif
isinstance
(
obj
,
datetime
.
date
time
):
return
Date
()
.
to_json
(
obj
)
return
Date
()
.
to_json
(
obj
)
else
:
else
:
return
JSONEncoder
.
default
(
self
,
obj
)
return
JSONEncoder
.
default
(
self
,
obj
)
cms/templates/edit_subsection.html
View file @
d7194e6b
<
%
inherit
file=
"base.html"
/>
<
%
inherit
file=
"base.html"
/>
<
%!
<
%!
import
logging
import
logging
from
xmodule
.
util
.
date_utils
import
get_
time_struct
_display
from
xmodule
.
util
.
date_utils
import
get_
default_time
_display
%
>
%
>
<
%!
from
django
.
core
.
urlresolvers
import
reverse
%
>
<
%!
from
django
.
core
.
urlresolvers
import
reverse
%
>
...
@@ -36,11 +36,15 @@
...
@@ -36,11 +36,15 @@
<div
class=
"datepair"
data-language=
"javascript"
>
<div
class=
"datepair"
data-language=
"javascript"
>
<div
class=
"field field-start-date"
>
<div
class=
"field field-start-date"
>
<label
for=
"start_date"
>
Release Day
</label>
<label
for=
"start_date"
>
Release Day
</label>
<input
type=
"text"
id=
"start_date"
name=
"start_date"
value=
"${get_time_struct_display(subsection.lms.start, '%m/%d/%Y')}"
placeholder=
"MM/DD/YYYY"
class=
"date"
size=
'15'
autocomplete=
"off"
/>
<input
type=
"text"
id=
"start_date"
name=
"start_date"
value=
"${subsection.lms.start.strftime('%m/%d/%Y')}"
placeholder=
"MM/DD/YYYY"
class=
"date"
size=
'15'
autocomplete=
"off"
/>
</div>
</div>
<div
class=
"field field-start-time"
>
<div
class=
"field field-start-time"
>
<label
for=
"start_time"
>
Release Time (
<abbr
title=
"Coordinated Universal Time"
>
UTC
</abbr>
)
</label>
<label
for=
"start_time"
>
Release Time (
<abbr
title=
"Coordinated Universal Time"
>
UTC
</abbr>
)
</label>
<input
type=
"text"
id=
"start_time"
name=
"start_time"
value=
"${get_time_struct_display(subsection.lms.start, '%H:%M')}"
placeholder=
"HH:MM"
class=
"time"
size=
'10'
autocomplete=
"off"
/>
<input
type=
"text"
id=
"start_time"
name=
"start_time"
value=
"${subsection.lms.start.strftime('%H:%M')}"
placeholder=
"HH:MM"
class=
"time"
size=
'10'
autocomplete=
"off"
/>
</div>
</div>
</div>
</div>
% if subsection.lms.start != parent_item.lms.start and subsection.lms.start:
% if subsection.lms.start != parent_item.lms.start and subsection.lms.start:
...
@@ -48,7 +52,7 @@
...
@@ -48,7 +52,7 @@
<p
class=
"notice"
>
The date above differs from the release date of ${parent_item.display_name_with_default}, which is unset.
<p
class=
"notice"
>
The date above differs from the release date of ${parent_item.display_name_with_default}, which is unset.
% else:
% else:
<p
class=
"notice"
>
The date above differs from the release date of ${parent_item.display_name_with_default} –
<p
class=
"notice"
>
The date above differs from the release date of ${parent_item.display_name_with_default} –
${get_
time_struct_display(parent_item.lms.start, '%m/%d/%Y at %H:%M UTC'
)}.
${get_
default_time_display(parent_item.lms.start
)}.
% endif
% endif
<a
href=
"#"
class=
"sync-date no-spinner"
>
Sync to ${parent_item.display_name_with_default}.
</a></p>
<a
href=
"#"
class=
"sync-date no-spinner"
>
Sync to ${parent_item.display_name_with_default}.
</a></p>
% endif
% endif
...
@@ -65,11 +69,15 @@
...
@@ -65,11 +69,15 @@
<div
class=
"datepair date-setter"
>
<div
class=
"datepair date-setter"
>
<div
class=
"field field-start-date"
>
<div
class=
"field field-start-date"
>
<label
for=
"due_date"
>
Due Day
</label>
<label
for=
"due_date"
>
Due Day
</label>
<input
type=
"text"
id=
"due_date"
name=
"due_date"
value=
"${get_time_struct_display(subsection.lms.due, '%m/%d/%Y')}"
placeholder=
"MM/DD/YYYY"
class=
"date"
size=
'15'
autocomplete=
"off"
/>
<input
type=
"text"
id=
"due_date"
name=
"due_date"
value=
"${subsection.lms.due.strftime('%m/%d/%Y') if subsection.lms.due else ''}"
placeholder=
"MM/DD/YYYY"
class=
"date"
size=
'15'
autocomplete=
"off"
/>
</div>
</div>
<div
class=
"field field-start-time"
>
<div
class=
"field field-start-time"
>
<label
for=
"due_time"
>
Due Time (
<abbr
title=
"Coordinated Universal Time"
>
UTC
</abbr>
)
</label>
<label
for=
"due_time"
>
Due Time (
<abbr
title=
"Coordinated Universal Time"
>
UTC
</abbr>
)
</label>
<input
type=
"text"
id=
"due_time"
name=
"due_time"
value=
"${get_time_struct_display(subsection.lms.due, '%H:%M')}"
placeholder=
"HH:MM"
class=
"time"
size=
'10'
autocomplete=
"off"
/>
<input
type=
"text"
id=
"due_time"
name=
"due_time"
value=
"${subsection.lms.due.strftime('%H:%M') if subsection.lms.due else ''}"
placeholder=
"HH:MM"
class=
"time"
size=
'10'
autocomplete=
"off"
/>
</div>
</div>
<a
href=
"#"
class=
"remove-date"
>
Remove due date
</a>
<a
href=
"#"
class=
"remove-date"
>
Remove due date
</a>
</div>
</div>
...
...
cms/templates/overview.html
View file @
d7194e6b
<
%
inherit
file=
"base.html"
/>
<
%
inherit
file=
"base.html"
/>
<
%!
<
%!
import
logging
import
logging
from
xmodule
.
util
.
date_utils
import
get_time_struct_display
from
xmodule
.
util
import
date_utils
%
>
%
>
<
%!
from
django
.
core
.
urlresolvers
import
reverse
%
>
<
%!
from
django
.
core
.
urlresolvers
import
reverse
%
>
<
%
block
name=
"title"
>
Course Outline
</
%
block>
<
%
block
name=
"title"
>
Course Outline
</
%
block>
...
@@ -154,14 +154,15 @@
...
@@ -154,14 +154,15 @@
<h3
class=
"section-name"
data-name=
"${section.display_name_with_default | h}"
></h3>
<h3
class=
"section-name"
data-name=
"${section.display_name_with_default | h}"
></h3>
<div
class=
"section-published-date"
>
<div
class=
"section-published-date"
>
<
%
<
%
start_date_str =
get_time_struct_display(section.lms.start,
'%
m
/%
d
/%
Y
')
start_date_str =
section.lms.start.strftime(
'%m/%d/%Y')
start_time_str =
get_time_struct_display(section.lms.start,
'%
H:
%
M
')
start_time_str =
section.lms.start.strftime(
'%H:%M')
%
>
%
>
%if section.lms.start is None:
%if section.lms.start is None:
<span
class=
"published-status"
>
This section has not been released.
</span>
<span
class=
"published-status"
>
This section has not been released.
</span>
<a
href=
"#"
class=
"schedule-button"
data-date=
""
data-time=
""
data-id=
"${section.location}"
>
Schedule
</a>
<a
href=
"#"
class=
"schedule-button"
data-date=
""
data-time=
""
data-id=
"${section.location}"
>
Schedule
</a>
%else:
%else:
<span
class=
"published-status"
><strong>
Will Release:
</strong>
${get_time_struct_display(section.lms.start, '%m/%d/%Y at %H:%M UTC')}
</span>
<span
class=
"published-status"
><strong>
Will Release:
</strong>
${date_utils.get_default_time_display(section.lms.start)}
</span>
<a
href=
"#"
class=
"edit-button"
data-date=
"${start_date_str}"
data-time=
"${start_time_str}"
data-id=
"${section.location}"
>
Edit
</a>
<a
href=
"#"
class=
"edit-button"
data-date=
"${start_date_str}"
data-time=
"${start_time_str}"
data-id=
"${section.location}"
>
Edit
</a>
%endif
%endif
</div>
</div>
...
...
common/djangoapps/contentserver/middleware.py
View file @
d7194e6b
import
logging
from
django.http
import
HttpResponse
,
HttpResponseNotModified
import
time
from
django.http
import
HttpResponse
,
Http404
,
HttpResponseNotModified
from
xmodule.contentstore.django
import
contentstore
from
xmodule.contentstore.django
import
contentstore
from
xmodule.contentstore.content
import
StaticContent
,
XASSET_LOCATION_TAG
from
xmodule.contentstore.content
import
StaticContent
,
XASSET_LOCATION_TAG
...
@@ -20,7 +17,7 @@ class StaticContentServer(object):
...
@@ -20,7 +17,7 @@ class StaticContentServer(object):
# return a 'Bad Request' to browser as we have a malformed Location
# return a 'Bad Request' to browser as we have a malformed Location
response
=
HttpResponse
()
response
=
HttpResponse
()
response
.
status_code
=
400
response
.
status_code
=
400
return
response
return
response
# first look in our cache so we don't have to round-trip to the DB
# first look in our cache so we don't have to round-trip to the DB
content
=
get_cached_content
(
loc
)
content
=
get_cached_content
(
loc
)
...
...
common/djangoapps/student/management/commands/pearson_make_tc_registration.py
View file @
d7194e6b
from
optparse
import
make_option
from
optparse
import
make_option
from
time
import
strftime
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
django.core.management.base
import
BaseCommand
,
CommandError
from
django.core.management.base
import
BaseCommand
,
CommandError
...
@@ -128,8 +127,8 @@ class Command(BaseCommand):
...
@@ -128,8 +127,8 @@ class Command(BaseCommand):
exam
=
CourseDescriptor
.
TestCenterExam
(
course_id
,
exam_name
,
exam_info
)
exam
=
CourseDescriptor
.
TestCenterExam
(
course_id
,
exam_name
,
exam_info
)
# update option values for date_first and date_last to use YYYY-MM-DD format
# update option values for date_first and date_last to use YYYY-MM-DD format
# instead of YYYY-MM-DDTHH:MM
# instead of YYYY-MM-DDTHH:MM
our_options
[
'eligibility_appointment_date_first'
]
=
strftime
(
"
%
Y-
%
m-
%
d"
,
exam
.
first_eligible_appointment_date
)
our_options
[
'eligibility_appointment_date_first'
]
=
exam
.
first_eligible_appointment_date
.
strftime
(
"
%
Y-
%
m-
%
d"
)
our_options
[
'eligibility_appointment_date_last'
]
=
strftime
(
"
%
Y-
%
m-
%
d"
,
exam
.
last_eligible_appointment_date
)
our_options
[
'eligibility_appointment_date_last'
]
=
exam
.
last_eligible_appointment_date
.
strftime
(
"
%
Y-
%
m-
%
d"
)
if
exam
is
None
:
if
exam
is
None
:
raise
CommandError
(
"Exam for course_id {} does not exist"
.
format
(
course_id
))
raise
CommandError
(
"Exam for course_id {} does not exist"
.
format
(
course_id
))
...
...
common/djangoapps/student/models.py
View file @
d7194e6b
...
@@ -16,7 +16,6 @@ import json
...
@@ -16,7 +16,6 @@ import json
import
logging
import
logging
import
uuid
import
uuid
from
random
import
randint
from
random
import
randint
from
time
import
strftime
from
django.conf
import
settings
from
django.conf
import
settings
...
@@ -54,7 +53,7 @@ class UserProfile(models.Model):
...
@@ -54,7 +53,7 @@ class UserProfile(models.Model):
class
Meta
:
class
Meta
:
db_table
=
"auth_userprofile"
db_table
=
"auth_userprofile"
## CRITICAL TODO/SECURITY
#
# CRITICAL TODO/SECURITY
# Sanitize all fields.
# Sanitize all fields.
# This is not visible to other users, but could introduce holes later
# This is not visible to other users, but could introduce holes later
user
=
models
.
OneToOneField
(
User
,
unique
=
True
,
db_index
=
True
,
related_name
=
'profile'
)
user
=
models
.
OneToOneField
(
User
,
unique
=
True
,
db_index
=
True
,
related_name
=
'profile'
)
...
@@ -429,8 +428,8 @@ class TestCenterRegistration(models.Model):
...
@@ -429,8 +428,8 @@ class TestCenterRegistration(models.Model):
registration
.
course_id
=
exam
.
course_id
registration
.
course_id
=
exam
.
course_id
registration
.
accommodation_request
=
accommodation_request
.
strip
()
registration
.
accommodation_request
=
accommodation_request
.
strip
()
registration
.
exam_series_code
=
exam
.
exam_series_code
registration
.
exam_series_code
=
exam
.
exam_series_code
registration
.
eligibility_appointment_date_first
=
strftime
(
"
%
Y-
%
m-
%
d"
,
exam
.
first_eligible_appointment_date
)
registration
.
eligibility_appointment_date_first
=
exam
.
first_eligible_appointment_date
.
strftime
(
"
%
Y-
%
m-
%
d"
)
registration
.
eligibility_appointment_date_last
=
strftime
(
"
%
Y-
%
m-
%
d"
,
exam
.
last_eligible_appointment_date
)
registration
.
eligibility_appointment_date_last
=
exam
.
last_eligible_appointment_date
.
strftime
(
"
%
Y-
%
m-
%
d"
)
registration
.
client_authorization_id
=
cls
.
_create_client_authorization_id
()
registration
.
client_authorization_id
=
cls
.
_create_client_authorization_id
()
# accommodation_code remains blank for now, along with Pearson confirmation information
# accommodation_code remains blank for now, along with Pearson confirmation information
return
registration
return
registration
...
@@ -598,7 +597,7 @@ def unique_id_for_user(user):
...
@@ -598,7 +597,7 @@ def unique_id_for_user(user):
return
h
.
hexdigest
()
return
h
.
hexdigest
()
## TODO: Should be renamed to generic UserGroup, and possibly
#
# TODO: Should be renamed to generic UserGroup, and possibly
# Given an optional field for type of group
# Given an optional field for type of group
class
UserTestGroup
(
models
.
Model
):
class
UserTestGroup
(
models
.
Model
):
users
=
models
.
ManyToManyField
(
User
,
db_index
=
True
)
users
=
models
.
ManyToManyField
(
User
,
db_index
=
True
)
...
@@ -626,7 +625,7 @@ class Registration(models.Model):
...
@@ -626,7 +625,7 @@ class Registration(models.Model):
def
activate
(
self
):
def
activate
(
self
):
self
.
user
.
is_active
=
True
self
.
user
.
is_active
=
True
self
.
user
.
save
()
self
.
user
.
save
()
#self.delete()
#
self.delete()
class
PendingNameChange
(
models
.
Model
):
class
PendingNameChange
(
models
.
Model
):
...
@@ -648,7 +647,7 @@ class CourseEnrollment(models.Model):
...
@@ -648,7 +647,7 @@ class CourseEnrollment(models.Model):
created
=
models
.
DateTimeField
(
auto_now_add
=
True
,
null
=
True
,
db_index
=
True
)
created
=
models
.
DateTimeField
(
auto_now_add
=
True
,
null
=
True
,
db_index
=
True
)
class
Meta
:
class
Meta
:
unique_together
=
((
'user'
,
'course_id'
),
)
unique_together
=
((
'user'
,
'course_id'
),)
def
__unicode__
(
self
):
def
__unicode__
(
self
):
return
"[CourseEnrollment]
%
s:
%
s (
%
s)"
%
(
self
.
user
,
self
.
course_id
,
self
.
created
)
return
"[CourseEnrollment]
%
s:
%
s (
%
s)"
%
(
self
.
user
,
self
.
course_id
,
self
.
created
)
...
@@ -667,12 +666,12 @@ class CourseEnrollmentAllowed(models.Model):
...
@@ -667,12 +666,12 @@ class CourseEnrollmentAllowed(models.Model):
created
=
models
.
DateTimeField
(
auto_now_add
=
True
,
null
=
True
,
db_index
=
True
)
created
=
models
.
DateTimeField
(
auto_now_add
=
True
,
null
=
True
,
db_index
=
True
)
class
Meta
:
class
Meta
:
unique_together
=
((
'email'
,
'course_id'
),
)
unique_together
=
((
'email'
,
'course_id'
),)
def
__unicode__
(
self
):
def
__unicode__
(
self
):
return
"[CourseEnrollmentAllowed]
%
s:
%
s (
%
s)"
%
(
self
.
email
,
self
.
course_id
,
self
.
created
)
return
"[CourseEnrollmentAllowed]
%
s:
%
s (
%
s)"
%
(
self
.
email
,
self
.
course_id
,
self
.
created
)
#cache_relation(User.profile)
#
cache_relation(User.profile)
#### Helper methods for use from python manage.py shell and other classes.
#### Helper methods for use from python manage.py shell and other classes.
...
...
common/djangoapps/xmodule_modifiers.py
View file @
d7194e6b
import
re
import
re
import
json
import
json
import
logging
import
logging
import
time
import
static_replace
import
static_replace
from
django.conf
import
settings
from
django.conf
import
settings
...
@@ -9,6 +8,8 @@ from functools import wraps
...
@@ -9,6 +8,8 @@ from functools import wraps
from
mitxmako.shortcuts
import
render_to_string
from
mitxmako.shortcuts
import
render_to_string
from
xmodule.seq_module
import
SequenceModule
from
xmodule.seq_module
import
SequenceModule
from
xmodule.vertical_module
import
VerticalModule
from
xmodule.vertical_module
import
VerticalModule
import
datetime
from
django.utils.timezone
import
UTC
log
=
logging
.
getLogger
(
"mitx.xmodule_modifiers"
)
log
=
logging
.
getLogger
(
"mitx.xmodule_modifiers"
)
...
@@ -83,7 +84,7 @@ def grade_histogram(module_id):
...
@@ -83,7 +84,7 @@ def grade_histogram(module_id):
cursor
.
execute
(
q
,
[
module_id
])
cursor
.
execute
(
q
,
[
module_id
])
grades
=
list
(
cursor
.
fetchall
())
grades
=
list
(
cursor
.
fetchall
())
grades
.
sort
(
key
=
lambda
x
:
x
[
0
])
# Add ORDER BY to sql query?
grades
.
sort
(
key
=
lambda
x
:
x
[
0
])
# Add ORDER BY to sql query?
if
len
(
grades
)
>=
1
and
grades
[
0
][
0
]
is
None
:
if
len
(
grades
)
>=
1
and
grades
[
0
][
0
]
is
None
:
return
[]
return
[]
return
grades
return
grades
...
@@ -101,7 +102,7 @@ def add_histogram(get_html, module, user):
...
@@ -101,7 +102,7 @@ def add_histogram(get_html, module, user):
@wraps
(
get_html
)
@wraps
(
get_html
)
def
_get_html
():
def
_get_html
():
if
type
(
module
)
in
[
SequenceModule
,
VerticalModule
]:
# TODO: make this more general, eg use an XModule attribute instead
if
type
(
module
)
in
[
SequenceModule
,
VerticalModule
]:
# TODO: make this more general, eg use an XModule attribute instead
return
get_html
()
return
get_html
()
module_id
=
module
.
id
module_id
=
module
.
id
...
@@ -132,7 +133,7 @@ def add_histogram(get_html, module, user):
...
@@ -132,7 +133,7 @@ def add_histogram(get_html, module, user):
# useful to indicate to staff if problem has been released or not
# useful to indicate to staff if problem has been released or not
# TODO (ichuang): use _has_access_descriptor.can_load in lms.courseware.access, instead of now>mstart comparison here
# TODO (ichuang): use _has_access_descriptor.can_load in lms.courseware.access, instead of now>mstart comparison here
now
=
time
.
gmtime
(
)
now
=
datetime
.
datetime
.
now
(
UTC
()
)
is_released
=
"unknown"
is_released
=
"unknown"
mstart
=
module
.
descriptor
.
lms
.
start
mstart
=
module
.
descriptor
.
lms
.
start
...
...
common/lib/capa/capa/inputtypes.py
View file @
d7194e6b
...
@@ -144,11 +144,11 @@ class InputTypeBase(object):
...
@@ -144,11 +144,11 @@ class InputTypeBase(object):
self
.
tag
=
xml
.
tag
self
.
tag
=
xml
.
tag
self
.
system
=
system
self
.
system
=
system
## NOTE: ID should only come from one place. If it comes from multiple,
#
# NOTE: ID should only come from one place. If it comes from multiple,
## we use state first, XML second (in case the xml changed, but we have
#
# we use state first, XML second (in case the xml changed, but we have
## existing state with an old id). Since we don't make this guarantee,
#
# existing state with an old id). Since we don't make this guarantee,
## we can swap this around in the future if there's a more logical
#
# we can swap this around in the future if there's a more logical
## order.
#
# order.
self
.
input_id
=
state
.
get
(
'id'
,
xml
.
get
(
'id'
))
self
.
input_id
=
state
.
get
(
'id'
,
xml
.
get
(
'id'
))
if
self
.
input_id
is
None
:
if
self
.
input_id
is
None
:
...
@@ -769,7 +769,7 @@ class MatlabInput(CodeInput):
...
@@ -769,7 +769,7 @@ class MatlabInput(CodeInput):
# construct xqueue headers
# construct xqueue headers
qinterface
=
self
.
system
.
xqueue
[
'interface'
]
qinterface
=
self
.
system
.
xqueue
[
'interface'
]
qtime
=
datetime
.
strftime
(
datetime
.
utcnow
(),
xqueue_interface
.
dateformat
)
qtime
=
datetime
.
utcnow
()
.
strftime
(
xqueue_interface
.
dateformat
)
callback_url
=
self
.
system
.
xqueue
[
'construct_callback'
](
'ungraded_response'
)
callback_url
=
self
.
system
.
xqueue
[
'construct_callback'
](
'ungraded_response'
)
anonymous_student_id
=
self
.
system
.
anonymous_student_id
anonymous_student_id
=
self
.
system
.
anonymous_student_id
queuekey
=
xqueue_interface
.
make_hashkey
(
str
(
self
.
system
.
seed
)
+
qtime
+
queuekey
=
xqueue_interface
.
make_hashkey
(
str
(
self
.
system
.
seed
)
+
qtime
+
...
...
common/lib/xmodule/xmodule/capa_module.py
View file @
d7194e6b
...
@@ -11,7 +11,7 @@ import sys
...
@@ -11,7 +11,7 @@ import sys
from
pkg_resources
import
resource_string
from
pkg_resources
import
resource_string
from
capa.capa_problem
import
LoncapaProblem
from
capa.capa_problem
import
LoncapaProblem
from
capa.responsetypes
import
StudentInputError
,
\
from
capa.responsetypes
import
StudentInputError
,
\
ResponseError
,
LoncapaProblemError
ResponseError
,
LoncapaProblemError
from
capa.util
import
convert_files_to_filenames
from
capa.util
import
convert_files_to_filenames
from
.progress
import
Progress
from
.progress
import
Progress
...
@@ -20,7 +20,7 @@ from xmodule.raw_module import RawDescriptor
...
@@ -20,7 +20,7 @@ from xmodule.raw_module import RawDescriptor
from
xmodule.exceptions
import
NotFoundError
,
ProcessingError
from
xmodule.exceptions
import
NotFoundError
,
ProcessingError
from
xblock.core
import
Scope
,
String
,
Boolean
,
Object
from
xblock.core
import
Scope
,
String
,
Boolean
,
Object
from
.fields
import
Timedelta
,
Date
,
StringyInteger
,
StringyFloat
from
.fields
import
Timedelta
,
Date
,
StringyInteger
,
StringyFloat
from
xmodule.util.date_utils
import
time_to_datetime
from
django.utils.timezone
import
UTC
log
=
logging
.
getLogger
(
"mitx.courseware"
)
log
=
logging
.
getLogger
(
"mitx.courseware"
)
...
@@ -134,7 +134,7 @@ class CapaModule(CapaFields, XModule):
...
@@ -134,7 +134,7 @@ class CapaModule(CapaFields, XModule):
def
__init__
(
self
,
system
,
location
,
descriptor
,
model_data
):
def
__init__
(
self
,
system
,
location
,
descriptor
,
model_data
):
XModule
.
__init__
(
self
,
system
,
location
,
descriptor
,
model_data
)
XModule
.
__init__
(
self
,
system
,
location
,
descriptor
,
model_data
)
due_date
=
time_to_datetime
(
self
.
due
)
due_date
=
self
.
due
if
self
.
graceperiod
is
not
None
and
due_date
:
if
self
.
graceperiod
is
not
None
and
due_date
:
self
.
close_date
=
due_date
+
self
.
graceperiod
self
.
close_date
=
due_date
+
self
.
graceperiod
...
@@ -502,7 +502,7 @@ class CapaModule(CapaFields, XModule):
...
@@ -502,7 +502,7 @@ class CapaModule(CapaFields, XModule):
Is it now past this problem's due date, including grace period?
Is it now past this problem's due date, including grace period?
"""
"""
return
(
self
.
close_date
is
not
None
and
return
(
self
.
close_date
is
not
None
and
datetime
.
datetime
.
utcnow
(
)
>
self
.
close_date
)
datetime
.
datetime
.
now
(
UTC
()
)
>
self
.
close_date
)
def
closed
(
self
):
def
closed
(
self
):
''' Is the student still allowed to submit answers? '''
''' Is the student still allowed to submit answers? '''
...
...
common/lib/xmodule/xmodule/course_module.py
View file @
d7194e6b
...
@@ -4,7 +4,6 @@ from math import exp
...
@@ -4,7 +4,6 @@ from math import exp
from
lxml
import
etree
from
lxml
import
etree
from
path
import
path
# NOTE (THK): Only used for detecting presence of syllabus
from
path
import
path
# NOTE (THK): Only used for detecting presence of syllabus
import
requests
import
requests
import
time
from
datetime
import
datetime
from
datetime
import
datetime
import
dateutil.parser
import
dateutil.parser
...
@@ -14,11 +13,11 @@ from xmodule.seq_module import SequenceDescriptor, SequenceModule
...
@@ -14,11 +13,11 @@ from xmodule.seq_module import SequenceDescriptor, SequenceModule
from
xmodule.timeparse
import
parse_time
from
xmodule.timeparse
import
parse_time
from
xmodule.util.decorators
import
lazyproperty
from
xmodule.util.decorators
import
lazyproperty
from
xmodule.graders
import
grader_from_conf
from
xmodule.graders
import
grader_from_conf
from
xmodule.util.date_utils
import
time_to_datetime
import
json
import
json
from
xblock.core
import
Scope
,
List
,
String
,
Object
,
Boolean
from
xblock.core
import
Scope
,
List
,
String
,
Object
,
Boolean
from
.fields
import
Date
from
.fields
import
Date
from
django.utils.timezone
import
UTC
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -219,8 +218,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
...
@@ -219,8 +218,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
msg
=
None
msg
=
None
if
self
.
start
is
None
:
if
self
.
start
is
None
:
msg
=
"Course loaded without a valid start date. id =
%
s"
%
self
.
id
msg
=
"Course loaded without a valid start date. id =
%
s"
%
self
.
id
# hack it -- start in 1970
self
.
start
=
datetime
.
now
(
UTC
())
self
.
start
=
time
.
gmtime
(
0
)
log
.
critical
(
msg
)
log
.
critical
(
msg
)
self
.
system
.
error_tracker
(
msg
)
self
.
system
.
error_tracker
(
msg
)
...
@@ -392,7 +390,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
...
@@ -392,7 +390,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
textbook_xml_object
.
set
(
'book_url'
,
textbook
.
book_url
)
textbook_xml_object
.
set
(
'book_url'
,
textbook
.
book_url
)
xml_object
.
append
(
textbook_xml_object
)
xml_object
.
append
(
textbook_xml_object
)
return
xml_object
return
xml_object
def
has_ended
(
self
):
def
has_ended
(
self
):
...
@@ -403,10 +401,10 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
...
@@ -403,10 +401,10 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
if
self
.
end
is
None
:
if
self
.
end
is
None
:
return
False
return
False
return
time
.
gmtime
(
)
>
self
.
end
return
datetime
.
now
(
UTC
()
)
>
self
.
end
def
has_started
(
self
):
def
has_started
(
self
):
return
time
.
gmtime
(
)
>
self
.
start
return
datetime
.
now
(
UTC
()
)
>
self
.
start
@property
@property
def
grader
(
self
):
def
grader
(
self
):
...
@@ -547,14 +545,16 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
...
@@ -547,14 +545,16 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
announcement
=
self
.
announcement
announcement
=
self
.
announcement
if
announcement
is
not
None
:
if
announcement
is
not
None
:
announcement
=
time_to_datetime
(
announcement
)
announcement
=
announcement
try
:
try
:
start
=
dateutil
.
parser
.
parse
(
self
.
advertised_start
)
start
=
dateutil
.
parser
.
parse
(
self
.
advertised_start
)
if
start
.
tzinfo
is
None
:
start
=
start
.
replace
(
tzinfo
=
UTC
())
except
(
ValueError
,
AttributeError
):
except
(
ValueError
,
AttributeError
):
start
=
time_to_datetime
(
self
.
start
)
start
=
self
.
start
now
=
datetime
.
utcnow
(
)
now
=
datetime
.
now
(
UTC
()
)
return
announcement
,
start
,
now
return
announcement
,
start
,
now
...
@@ -656,7 +656,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
...
@@ -656,7 +656,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
elif
self
.
advertised_start
is
None
and
self
.
start
is
None
:
elif
self
.
advertised_start
is
None
and
self
.
start
is
None
:
return
'TBD'
return
'TBD'
else
:
else
:
return
time
.
strftime
(
"
%
b
%
d,
%
Y"
,
self
.
advertised_start
or
self
.
start
)
return
(
self
.
advertised_start
or
self
.
start
)
.
strftime
(
"
%
b
%
d,
%
Y"
)
@property
@property
def
end_date_text
(
self
):
def
end_date_text
(
self
):
...
@@ -665,7 +665,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
...
@@ -665,7 +665,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
If the course does not have an end date set (course.end is None), an empty string will be returned.
If the course does not have an end date set (course.end is None), an empty string will be returned.
"""
"""
return
''
if
self
.
end
is
None
else
time
.
strftime
(
"
%
b
%
d,
%
Y"
,
self
.
end
)
return
''
if
self
.
end
is
None
else
self
.
end
.
strftime
(
"
%
b
%
d,
%
Y"
)
@property
@property
def
forum_posts_allowed
(
self
):
def
forum_posts_allowed
(
self
):
...
@@ -673,7 +673,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
...
@@ -673,7 +673,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
blackout_periods
=
[(
parse_time
(
start
),
parse_time
(
end
))
blackout_periods
=
[(
parse_time
(
start
),
parse_time
(
end
))
for
start
,
end
for
start
,
end
in
self
.
discussion_blackouts
]
in
self
.
discussion_blackouts
]
now
=
time
.
gmtime
(
)
now
=
datetime
.
now
(
UTC
()
)
for
start
,
end
in
blackout_periods
:
for
start
,
end
in
blackout_periods
:
if
start
<=
now
<=
end
:
if
start
<=
now
<=
end
:
return
False
return
False
...
@@ -699,7 +699,8 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
...
@@ -699,7 +699,8 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
self
.
last_eligible_appointment_date
=
self
.
_try_parse_time
(
'Last_Eligible_Appointment_Date'
)
# or self.first_eligible_appointment_date
self
.
last_eligible_appointment_date
=
self
.
_try_parse_time
(
'Last_Eligible_Appointment_Date'
)
# or self.first_eligible_appointment_date
if
self
.
last_eligible_appointment_date
is
None
:
if
self
.
last_eligible_appointment_date
is
None
:
raise
ValueError
(
"Last appointment date must be specified"
)
raise
ValueError
(
"Last appointment date must be specified"
)
self
.
registration_start_date
=
self
.
_try_parse_time
(
'Registration_Start_Date'
)
or
time
.
gmtime
(
0
)
self
.
registration_start_date
=
(
self
.
_try_parse_time
(
'Registration_Start_Date'
)
or
datetime
.
utcfromtimestamp
(
0
))
self
.
registration_end_date
=
self
.
_try_parse_time
(
'Registration_End_Date'
)
or
self
.
last_eligible_appointment_date
self
.
registration_end_date
=
self
.
_try_parse_time
(
'Registration_End_Date'
)
or
self
.
last_eligible_appointment_date
# do validation within the exam info:
# do validation within the exam info:
if
self
.
registration_start_date
>
self
.
registration_end_date
:
if
self
.
registration_start_date
>
self
.
registration_end_date
:
...
@@ -725,32 +726,32 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
...
@@ -725,32 +726,32 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
return
None
return
None
def
has_started
(
self
):
def
has_started
(
self
):
return
time
.
gmtime
(
)
>
self
.
first_eligible_appointment_date
return
datetime
.
now
(
UTC
()
)
>
self
.
first_eligible_appointment_date
def
has_ended
(
self
):
def
has_ended
(
self
):
return
time
.
gmtime
(
)
>
self
.
last_eligible_appointment_date
return
datetime
.
now
(
UTC
()
)
>
self
.
last_eligible_appointment_date
def
has_started_registration
(
self
):
def
has_started_registration
(
self
):
return
time
.
gmtime
(
)
>
self
.
registration_start_date
return
datetime
.
now
(
UTC
()
)
>
self
.
registration_start_date
def
has_ended_registration
(
self
):
def
has_ended_registration
(
self
):
return
time
.
gmtime
(
)
>
self
.
registration_end_date
return
datetime
.
now
(
UTC
()
)
>
self
.
registration_end_date
def
is_registering
(
self
):
def
is_registering
(
self
):
now
=
time
.
gmtime
(
)
now
=
datetime
.
now
(
UTC
()
)
return
now
>=
self
.
registration_start_date
and
now
<=
self
.
registration_end_date
return
now
>=
self
.
registration_start_date
and
now
<=
self
.
registration_end_date
@property
@property
def
first_eligible_appointment_date_text
(
self
):
def
first_eligible_appointment_date_text
(
self
):
return
time
.
strftime
(
"
%
b
%
d,
%
Y"
,
self
.
first_eligible_appointment_date
)
return
date
time
.
strftime
(
"
%
b
%
d,
%
Y"
,
self
.
first_eligible_appointment_date
)
@property
@property
def
last_eligible_appointment_date_text
(
self
):
def
last_eligible_appointment_date_text
(
self
):
return
time
.
strftime
(
"
%
b
%
d,
%
Y"
,
self
.
last_eligible_appointment_date
)
return
date
time
.
strftime
(
"
%
b
%
d,
%
Y"
,
self
.
last_eligible_appointment_date
)
@property
@property
def
registration_end_date_text
(
self
):
def
registration_end_date_text
(
self
):
return
time
.
strftime
(
"
%
b
%
d,
%
Y at
%
H:
%
M UTC"
,
self
.
registration_end_date
)
return
date
time
.
strftime
(
"
%
b
%
d,
%
Y at
%
H:
%
M UTC"
,
self
.
registration_end_date
)
@property
@property
def
current_test_center_exam
(
self
):
def
current_test_center_exam
(
self
):
...
...
common/lib/xmodule/xmodule/fields.py
View file @
d7194e6b
...
@@ -2,19 +2,19 @@ import time
...
@@ -2,19 +2,19 @@ import time
import
logging
import
logging
import
re
import
re
from
datetime
import
timedelta
from
xblock.core
import
ModelType
from
xblock.core
import
ModelType
import
datetime
import
datetime
import
dateutil.parser
import
dateutil.parser
from
xblock.core
import
Integer
,
Float
,
Boolean
from
xblock.core
import
Integer
,
Float
,
Boolean
from
django.utils.timezone
import
UTC
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
class
Date
(
ModelType
):
class
Date
(
ModelType
):
'''
'''
Date fields know how to parse and produce json (iso) compatible formats.
Date fields know how to parse and produce json (iso) compatible formats.
Converts to tz aware datetimes.
'''
'''
def
from_json
(
self
,
field
):
def
from_json
(
self
,
field
):
"""
"""
...
@@ -27,11 +27,15 @@ class Date(ModelType):
...
@@ -27,11 +27,15 @@ class Date(ModelType):
elif
field
is
""
:
elif
field
is
""
:
return
None
return
None
elif
isinstance
(
field
,
basestring
):
elif
isinstance
(
field
,
basestring
):
d
=
dateutil
.
parser
.
parse
(
field
)
result
=
dateutil
.
parser
.
parse
(
field
)
return
d
.
utctimetuple
()
if
result
.
tzinfo
is
None
:
result
=
result
.
replace
(
tzinfo
=
UTC
())
return
result
elif
isinstance
(
field
,
(
int
,
long
,
float
)):
elif
isinstance
(
field
,
(
int
,
long
,
float
)):
return
time
.
gmtime
(
field
/
1000
)
return
datetime
.
datetime
.
fromtimestamp
(
field
/
1000
,
UTC
()
)
elif
isinstance
(
field
,
time
.
struct_time
):
elif
isinstance
(
field
,
time
.
struct_time
):
return
datetime
.
datetime
.
fromtimestamp
(
time
.
mktime
(
field
),
UTC
())
elif
isinstance
(
field
,
datetime
.
datetime
):
return
field
return
field
else
:
else
:
msg
=
"Field {0} has bad value '{1}'"
.
format
(
msg
=
"Field {0} has bad value '{1}'"
.
format
(
...
@@ -49,7 +53,11 @@ class Date(ModelType):
...
@@ -49,7 +53,11 @@ class Date(ModelType):
# struct_times are always utc
# struct_times are always utc
return
time
.
strftime
(
'
%
Y-
%
m-
%
dT
%
H:
%
M:
%
SZ'
,
value
)
return
time
.
strftime
(
'
%
Y-
%
m-
%
dT
%
H:
%
M:
%
SZ'
,
value
)
elif
isinstance
(
value
,
datetime
.
datetime
):
elif
isinstance
(
value
,
datetime
.
datetime
):
return
value
.
isoformat
()
+
'Z'
if
value
.
tzinfo
is
None
or
value
.
utcoffset
()
.
total_seconds
()
==
0
:
# isoformat adds +00:00 rather than Z
return
value
.
strftime
(
'
%
Y-
%
m-
%
dT
%
H:
%
M:
%
SZ'
)
else
:
return
value
.
isoformat
()
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)?)?$'
)
...
@@ -74,7 +82,7 @@ class Timedelta(ModelType):
...
@@ -74,7 +82,7 @@ class Timedelta(ModelType):
for
(
name
,
param
)
in
parts
.
iteritems
():
for
(
name
,
param
)
in
parts
.
iteritems
():
if
param
:
if
param
:
time_params
[
name
]
=
int
(
param
)
time_params
[
name
]
=
int
(
param
)
return
timedelta
(
**
time_params
)
return
datetime
.
timedelta
(
**
time_params
)
def
to_json
(
self
,
value
):
def
to_json
(
self
,
value
):
values
=
[]
values
=
[]
...
@@ -93,7 +101,7 @@ class StringyInteger(Integer):
...
@@ -93,7 +101,7 @@ class StringyInteger(Integer):
def
from_json
(
self
,
value
):
def
from_json
(
self
,
value
):
try
:
try
:
return
int
(
value
)
return
int
(
value
)
except
:
except
Exception
:
return
None
return
None
...
...
common/lib/xmodule/xmodule/foldit_module.py
View file @
d7194e6b
...
@@ -8,7 +8,6 @@ from xmodule.x_module import XModule
...
@@ -8,7 +8,6 @@ from xmodule.x_module import XModule
from
xmodule.xml_module
import
XmlDescriptor
from
xmodule.xml_module
import
XmlDescriptor
from
xblock.core
import
Scope
,
Integer
,
String
from
xblock.core
import
Scope
,
Integer
,
String
from
.fields
import
Date
from
.fields
import
Date
from
xmodule.util.date_utils
import
time_to_datetime
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -31,9 +30,7 @@ class FolditModule(FolditFields, XModule):
...
@@ -31,9 +30,7 @@ class FolditModule(FolditFields, XModule):
css
=
{
'scss'
:
[
resource_string
(
__name__
,
'css/foldit/leaderboard.scss'
)]}
css
=
{
'scss'
:
[
resource_string
(
__name__
,
'css/foldit/leaderboard.scss'
)]}
def
__init__
(
self
,
*
args
,
**
kwargs
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
XModule
.
__init__
(
self
,
*
args
,
**
kwargs
)
"""
"""
Example:
Example:
<foldit show_basic_score="true"
<foldit show_basic_score="true"
required_level="4"
required_level="4"
...
@@ -42,8 +39,8 @@ class FolditModule(FolditFields, XModule):
...
@@ -42,8 +39,8 @@ class FolditModule(FolditFields, XModule):
required_sublevel_half_credit="3"
required_sublevel_half_credit="3"
show_leaderboard="false"/>
show_leaderboard="false"/>
"""
"""
XModule
.
__init__
(
self
,
*
args
,
**
kwargs
)
self
.
due_time
=
time_to_datetime
(
self
.
due
)
self
.
due_time
=
self
.
due
def
is_complete
(
self
):
def
is_complete
(
self
):
"""
"""
...
@@ -102,7 +99,7 @@ class FolditModule(FolditFields, XModule):
...
@@ -102,7 +99,7 @@ class FolditModule(FolditFields, XModule):
from
foldit.models
import
Score
from
foldit.models
import
Score
leaders
=
[(
e
[
'username'
],
e
[
'score'
])
for
e
in
Score
.
get_tops_n
(
10
)]
leaders
=
[(
e
[
'username'
],
e
[
'score'
])
for
e
in
Score
.
get_tops_n
(
10
)]
leaders
.
sort
(
key
=
lambda
x
:
-
x
[
1
])
leaders
.
sort
(
key
=
lambda
x
:
-
x
[
1
])
return
leaders
return
leaders
...
...
common/lib/xmodule/xmodule/modulestore/draft.py
View file @
d7194e6b
...
@@ -4,6 +4,7 @@ from . import ModuleStoreBase, Location, namedtuple_to_son
...
@@ -4,6 +4,7 @@ from . import ModuleStoreBase, Location, namedtuple_to_son
from
.exceptions
import
ItemNotFoundError
from
.exceptions
import
ItemNotFoundError
from
.inheritance
import
own_metadata
from
.inheritance
import
own_metadata
from
xmodule.exceptions
import
InvalidVersionError
from
xmodule.exceptions
import
InvalidVersionError
from
pytz
import
UTC
DRAFT
=
'draft'
DRAFT
=
'draft'
# Things w/ these categories should never be marked as version='draft'
# Things w/ these categories should never be marked as version='draft'
...
@@ -197,7 +198,7 @@ class DraftModuleStore(ModuleStoreBase):
...
@@ -197,7 +198,7 @@ class DraftModuleStore(ModuleStoreBase):
"""
"""
draft
=
self
.
get_item
(
location
)
draft
=
self
.
get_item
(
location
)
draft
.
cms
.
published_date
=
datetime
.
utcnow
(
)
draft
.
cms
.
published_date
=
datetime
.
now
(
UTC
)
draft
.
cms
.
published_by
=
published_by_id
draft
.
cms
.
published_by
=
published_by_id
super
(
DraftModuleStore
,
self
)
.
update_item
(
location
,
draft
.
_model_data
.
_kvs
.
_data
)
super
(
DraftModuleStore
,
self
)
.
update_item
(
location
,
draft
.
_model_data
.
_kvs
.
_data
)
super
(
DraftModuleStore
,
self
)
.
update_children
(
location
,
draft
.
_model_data
.
_kvs
.
_children
)
super
(
DraftModuleStore
,
self
)
.
update_children
(
location
,
draft
.
_model_data
.
_kvs
.
_children
)
...
...
common/lib/xmodule/xmodule/modulestore/xml.py
View file @
d7194e6b
...
@@ -52,7 +52,7 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
...
@@ -52,7 +52,7 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
xmlstore: the XMLModuleStore to store the loaded modules in
xmlstore: the XMLModuleStore to store the loaded modules in
"""
"""
self
.
unnamed
=
defaultdict
(
int
)
# category -> num of new url_names for that category
self
.
unnamed
=
defaultdict
(
int
)
# category -> num of new url_names for that category
self
.
used_names
=
defaultdict
(
set
)
# category -> set of used url_names
self
.
used_names
=
defaultdict
(
set
)
# category -> set of used url_names
self
.
org
,
self
.
course
,
self
.
url_name
=
course_id
.
split
(
'/'
)
self
.
org
,
self
.
course
,
self
.
url_name
=
course_id
.
split
(
'/'
)
# cdodge: adding the course_id as passed in for later reference rather than having to recomine the org/course/url_name
# cdodge: adding the course_id as passed in for later reference rather than having to recomine the org/course/url_name
...
@@ -124,7 +124,7 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
...
@@ -124,7 +124,7 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
else
:
else
:
# TODO (vshnayder): We may want to enable this once course repos are cleaned up.
# TODO (vshnayder): We may want to enable this once course repos are cleaned up.
# (or we may want to give up on the requirement for non-state-relevant issues...)
# (or we may want to give up on the requirement for non-state-relevant issues...)
#error_tracker("WARNING: no name specified for module. xml='{0}...'".format(xml[:100]))
#
error_tracker("WARNING: no name specified for module. xml='{0}...'".format(xml[:100]))
pass
pass
# Make sure everything is unique
# Make sure everything is unique
...
@@ -447,7 +447,7 @@ class XMLModuleStore(ModuleStoreBase):
...
@@ -447,7 +447,7 @@ class XMLModuleStore(ModuleStoreBase):
def
load_extra_content
(
self
,
system
,
course_descriptor
,
category
,
base_dir
,
course_dir
,
url_name
):
def
load_extra_content
(
self
,
system
,
course_descriptor
,
category
,
base_dir
,
course_dir
,
url_name
):
self
.
_load_extra_content
(
system
,
course_descriptor
,
category
,
base_dir
,
course_dir
)
self
.
_load_extra_content
(
system
,
course_descriptor
,
category
,
base_dir
,
course_dir
)
# then look in a override folder based on the course run
# then look in a override folder based on the course run
if
os
.
path
.
isdir
(
base_dir
/
url_name
):
if
os
.
path
.
isdir
(
base_dir
/
url_name
):
self
.
_load_extra_content
(
system
,
course_descriptor
,
category
,
base_dir
/
url_name
,
course_dir
)
self
.
_load_extra_content
(
system
,
course_descriptor
,
category
,
base_dir
/
url_name
,
course_dir
)
...
...
common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py
View file @
d7194e6b
...
@@ -16,6 +16,7 @@ from .peer_grading_service import PeerGradingService, MockPeerGradingService
...
@@ -16,6 +16,7 @@ from .peer_grading_service import PeerGradingService, MockPeerGradingService
import
controller_query_service
import
controller_query_service
from
datetime
import
datetime
from
datetime
import
datetime
from
django.utils.timezone
import
UTC
log
=
logging
.
getLogger
(
"mitx.courseware"
)
log
=
logging
.
getLogger
(
"mitx.courseware"
)
...
@@ -56,7 +57,7 @@ class OpenEndedChild(object):
...
@@ -56,7 +57,7 @@ class OpenEndedChild(object):
POST_ASSESSMENT
=
'post_assessment'
POST_ASSESSMENT
=
'post_assessment'
DONE
=
'done'
DONE
=
'done'
#This is used to tell students where they are at in the module
#
This is used to tell students where they are at in the module
HUMAN_NAMES
=
{
HUMAN_NAMES
=
{
'initial'
:
'Not started'
,
'initial'
:
'Not started'
,
'assessing'
:
'In progress'
,
'assessing'
:
'In progress'
,
...
@@ -102,7 +103,7 @@ class OpenEndedChild(object):
...
@@ -102,7 +103,7 @@ class OpenEndedChild(object):
if
system
.
open_ended_grading_interface
:
if
system
.
open_ended_grading_interface
:
self
.
peer_gs
=
PeerGradingService
(
system
.
open_ended_grading_interface
,
system
)
self
.
peer_gs
=
PeerGradingService
(
system
.
open_ended_grading_interface
,
system
)
self
.
controller_qs
=
controller_query_service
.
ControllerQueryService
(
self
.
controller_qs
=
controller_query_service
.
ControllerQueryService
(
system
.
open_ended_grading_interface
,
system
system
.
open_ended_grading_interface
,
system
)
)
else
:
else
:
self
.
peer_gs
=
MockPeerGradingService
()
self
.
peer_gs
=
MockPeerGradingService
()
...
@@ -130,7 +131,7 @@ class OpenEndedChild(object):
...
@@ -130,7 +131,7 @@ class OpenEndedChild(object):
pass
pass
def
closed
(
self
):
def
closed
(
self
):
if
self
.
close_date
is
not
None
and
datetime
.
utcnow
(
)
>
self
.
close_date
:
if
self
.
close_date
is
not
None
and
datetime
.
now
(
UTC
()
)
>
self
.
close_date
:
return
True
return
True
return
False
return
False
...
@@ -138,13 +139,13 @@ class OpenEndedChild(object):
...
@@ -138,13 +139,13 @@ class OpenEndedChild(object):
if
self
.
closed
():
if
self
.
closed
():
return
True
,
{
return
True
,
{
'success'
:
False
,
'success'
:
False
,
#This is a student_facing_error
#
This is a student_facing_error
'error'
:
'The problem close date has passed, and this problem is now closed.'
'error'
:
'The problem close date has passed, and this problem is now closed.'
}
}
elif
self
.
child_attempts
>
self
.
max_attempts
:
elif
self
.
child_attempts
>
self
.
max_attempts
:
return
True
,
{
return
True
,
{
'success'
:
False
,
'success'
:
False
,
#This is a student_facing_error
#
This is a student_facing_error
'error'
:
'You have attempted this problem {0} times. You are allowed {1} attempts.'
.
format
(
'error'
:
'You have attempted this problem {0} times. You are allowed {1} attempts.'
.
format
(
self
.
child_attempts
,
self
.
max_attempts
self
.
child_attempts
,
self
.
max_attempts
)
)
...
@@ -272,7 +273,7 @@ class OpenEndedChild(object):
...
@@ -272,7 +273,7 @@ class OpenEndedChild(object):
try
:
try
:
return
Progress
(
int
(
self
.
get_score
()[
'score'
]),
int
(
self
.
_max_score
))
return
Progress
(
int
(
self
.
get_score
()[
'score'
]),
int
(
self
.
_max_score
))
except
Exception
as
err
:
except
Exception
as
err
:
#This is a dev_facing_error
#
This is a dev_facing_error
log
.
exception
(
"Got bad progress from open ended child module. Max Score: {0}"
.
format
(
self
.
_max_score
))
log
.
exception
(
"Got bad progress from open ended child module. Max Score: {0}"
.
format
(
self
.
_max_score
))
return
None
return
None
return
None
return
None
...
@@ -281,10 +282,10 @@ class OpenEndedChild(object):
...
@@ -281,10 +282,10 @@ class OpenEndedChild(object):
"""
"""
return dict out-of-sync error message, and also log.
return dict out-of-sync error message, and also log.
"""
"""
#This is a dev_facing_error
#
This is a dev_facing_error
log
.
warning
(
"Open ended child state out sync. state:
%
r, get:
%
r.
%
s"
,
log
.
warning
(
"Open ended child state out sync. state:
%
r, get:
%
r.
%
s"
,
self
.
child_state
,
get
,
msg
)
self
.
child_state
,
get
,
msg
)
#This is a student_facing_error
#
This is a student_facing_error
return
{
'success'
:
False
,
return
{
'success'
:
False
,
'error'
:
'The problem state got out-of-sync. Please try reloading the page.'
}
'error'
:
'The problem state got out-of-sync. Please try reloading the page.'
}
...
@@ -391,7 +392,7 @@ class OpenEndedChild(object):
...
@@ -391,7 +392,7 @@ class OpenEndedChild(object):
"""
"""
overall_success
=
False
overall_success
=
False
if
not
self
.
accept_file_upload
:
if
not
self
.
accept_file_upload
:
#If the question does not accept file uploads, do not do anything
#
If the question does not accept file uploads, do not do anything
return
True
,
get_data
return
True
,
get_data
has_file_to_upload
,
uploaded_to_s3
,
image_ok
,
image_tag
=
self
.
check_for_image_and_upload
(
get_data
)
has_file_to_upload
,
uploaded_to_s3
,
image_ok
,
image_tag
=
self
.
check_for_image_and_upload
(
get_data
)
...
@@ -399,19 +400,19 @@ class OpenEndedChild(object):
...
@@ -399,19 +400,19 @@ class OpenEndedChild(object):
get_data
[
'student_answer'
]
+=
image_tag
get_data
[
'student_answer'
]
+=
image_tag
overall_success
=
True
overall_success
=
True
elif
has_file_to_upload
and
not
uploaded_to_s3
and
image_ok
:
elif
has_file_to_upload
and
not
uploaded_to_s3
and
image_ok
:
#In this case, an image was submitted by the student, but the image could not be uploaded to S3. Likely
#
In this case, an image was submitted by the student, but the image could not be uploaded to S3. Likely
#a config issue (development vs deployment). For now, just treat this as a "success"
#
a config issue (development vs deployment). For now, just treat this as a "success"
log
.
exception
(
"Student AJAX post to combined open ended xmodule indicated that it contained an image, "
log
.
exception
(
"Student AJAX post to combined open ended xmodule indicated that it contained an image, "
"but the image was not able to be uploaded to S3. This could indicate a config"
"but the image was not able to be uploaded to S3. This could indicate a config"
"issue with this deployment, but it could also indicate a problem with S3 or with the"
"issue with this deployment, but it could also indicate a problem with S3 or with the"
"student image itself."
)
"student image itself."
)
overall_success
=
True
overall_success
=
True
elif
not
has_file_to_upload
:
elif
not
has_file_to_upload
:
#If there is no file to upload, probably the student has embedded the link in the answer text
#
If there is no file to upload, probably the student has embedded the link in the answer text
success
,
get_data
[
'student_answer'
]
=
self
.
check_for_url_in_text
(
get_data
[
'student_answer'
])
success
,
get_data
[
'student_answer'
]
=
self
.
check_for_url_in_text
(
get_data
[
'student_answer'
])
overall_success
=
success
overall_success
=
success
#log.debug("Has file: {0} Uploaded: {1} Image Ok: {2}".format(has_file_to_upload, uploaded_to_s3, image_ok))
#
log.debug("Has file: {0} Uploaded: {1} Image Ok: {2}".format(has_file_to_upload, uploaded_to_s3, image_ok))
return
overall_success
,
get_data
return
overall_success
,
get_data
...
@@ -441,7 +442,7 @@ class OpenEndedChild(object):
...
@@ -441,7 +442,7 @@ class OpenEndedChild(object):
success
=
False
success
=
False
allowed_to_submit
=
True
allowed_to_submit
=
True
response
=
{}
response
=
{}
#This is a student_facing_error
#
This is a student_facing_error
error_string
=
(
"You need to peer grade {0} more in order to make another submission. "
error_string
=
(
"You need to peer grade {0} more in order to make another submission. "
"You have graded {1}, and {2} are required. You have made {3} successful peer grading submissions."
)
"You have graded {1}, and {2} are required. You have made {3} successful peer grading submissions."
)
try
:
try
:
...
@@ -451,17 +452,17 @@ class OpenEndedChild(object):
...
@@ -451,17 +452,17 @@ class OpenEndedChild(object):
student_sub_count
=
response
[
'student_sub_count'
]
student_sub_count
=
response
[
'student_sub_count'
]
success
=
True
success
=
True
except
:
except
:
#This is a dev_facing_error
#
This is a dev_facing_error
log
.
error
(
"Could not contact external open ended graders for location {0} and student {1}"
.
format
(
log
.
error
(
"Could not contact external open ended graders for location {0} and student {1}"
.
format
(
self
.
location_string
,
student_id
))
self
.
location_string
,
student_id
))
#This is a student_facing_error
#
This is a student_facing_error
error_message
=
"Could not contact the graders. Please notify course staff."
error_message
=
"Could not contact the graders. Please notify course staff."
return
success
,
allowed_to_submit
,
error_message
return
success
,
allowed_to_submit
,
error_message
if
count_graded
>=
count_required
:
if
count_graded
>=
count_required
:
return
success
,
allowed_to_submit
,
""
return
success
,
allowed_to_submit
,
""
else
:
else
:
allowed_to_submit
=
False
allowed_to_submit
=
False
#This is a student_facing_error
#
This is a student_facing_error
error_message
=
error_string
.
format
(
count_required
-
count_graded
,
count_graded
,
count_required
,
error_message
=
error_string
.
format
(
count_required
-
count_graded
,
count_graded
,
count_required
,
student_sub_count
)
student_sub_count
)
return
success
,
allowed_to_submit
,
error_message
return
success
,
allowed_to_submit
,
error_message
...
...
common/lib/xmodule/xmodule/peer_grading_module.py
View file @
d7194e6b
...
@@ -15,6 +15,7 @@ from xmodule.fields import Date, StringyFloat, StringyInteger, StringyBoolean
...
@@ -15,6 +15,7 @@ from xmodule.fields import Date, StringyFloat, StringyInteger, StringyBoolean
from
xmodule.open_ended_grading_classes.peer_grading_service
import
PeerGradingService
,
GradingServiceError
,
MockPeerGradingService
from
xmodule.open_ended_grading_classes.peer_grading_service
import
PeerGradingService
,
GradingServiceError
,
MockPeerGradingService
from
open_ended_grading_classes
import
combined_open_ended_rubric
from
open_ended_grading_classes
import
combined_open_ended_rubric
from
django.utils.timezone
import
UTC
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -76,7 +77,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
...
@@ -76,7 +77,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
def
__init__
(
self
,
system
,
location
,
descriptor
,
model_data
):
def
__init__
(
self
,
system
,
location
,
descriptor
,
model_data
):
XModule
.
__init__
(
self
,
system
,
location
,
descriptor
,
model_data
)
XModule
.
__init__
(
self
,
system
,
location
,
descriptor
,
model_data
)
#We need to set the location here so the child modules can use it
#
We need to set the location here so the child modules can use it
system
.
set
(
'location'
,
location
)
system
.
set
(
'location'
,
location
)
self
.
system
=
system
self
.
system
=
system
if
(
self
.
system
.
open_ended_grading_interface
):
if
(
self
.
system
.
open_ended_grading_interface
):
...
@@ -112,7 +113,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
...
@@ -112,7 +113,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
if
not
self
.
ajax_url
.
endswith
(
"/"
):
if
not
self
.
ajax_url
.
endswith
(
"/"
):
self
.
ajax_url
=
self
.
ajax_url
+
"/"
self
.
ajax_url
=
self
.
ajax_url
+
"/"
#StringyInteger could return None, so keep this check.
#
StringyInteger could return None, so keep this check.
if
not
isinstance
(
self
.
max_grade
,
int
):
if
not
isinstance
(
self
.
max_grade
,
int
):
raise
TypeError
(
"max_grade needs to be an integer."
)
raise
TypeError
(
"max_grade needs to be an integer."
)
...
@@ -120,7 +121,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
...
@@ -120,7 +121,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
return
self
.
_closed
(
self
.
timeinfo
)
return
self
.
_closed
(
self
.
timeinfo
)
def
_closed
(
self
,
timeinfo
):
def
_closed
(
self
,
timeinfo
):
if
timeinfo
.
close_date
is
not
None
and
datetime
.
utcnow
(
)
>
timeinfo
.
close_date
:
if
timeinfo
.
close_date
is
not
None
and
datetime
.
now
(
UTC
()
)
>
timeinfo
.
close_date
:
return
True
return
True
return
False
return
False
...
@@ -166,9 +167,9 @@ class PeerGradingModule(PeerGradingFields, XModule):
...
@@ -166,9 +167,9 @@ class PeerGradingModule(PeerGradingFields, XModule):
}
}
if
dispatch
not
in
handlers
:
if
dispatch
not
in
handlers
:
#This is a dev_facing_error
#
This is a dev_facing_error
log
.
error
(
"Cannot find {0} in handlers in handle_ajax function for open_ended_module.py"
.
format
(
dispatch
))
log
.
error
(
"Cannot find {0} in handlers in handle_ajax function for open_ended_module.py"
.
format
(
dispatch
))
#This is a dev_facing_error
#
This is a dev_facing_error
return
json
.
dumps
({
'error'
:
'Error handling action. Please try again.'
,
'success'
:
False
})
return
json
.
dumps
({
'error'
:
'Error handling action. Please try again.'
,
'success'
:
False
})
d
=
handlers
[
dispatch
](
get
)
d
=
handlers
[
dispatch
](
get
)
...
@@ -187,7 +188,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
...
@@ -187,7 +188,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
count_required
=
response
[
'count_required'
]
count_required
=
response
[
'count_required'
]
success
=
True
success
=
True
except
GradingServiceError
:
except
GradingServiceError
:
#This is a dev_facing_error
#
This is a dev_facing_error
log
.
exception
(
"Error getting location data from controller for location {0}, student {1}"
log
.
exception
(
"Error getting location data from controller for location {0}, student {1}"
.
format
(
location
,
student_id
))
.
format
(
location
,
student_id
))
...
@@ -220,7 +221,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
...
@@ -220,7 +221,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
count_graded
=
response
[
'count_graded'
]
count_graded
=
response
[
'count_graded'
]
count_required
=
response
[
'count_required'
]
count_required
=
response
[
'count_required'
]
if
count_required
>
0
and
count_graded
>=
count_required
:
if
count_required
>
0
and
count_graded
>=
count_required
:
#Ensures that once a student receives a final score for peer grading, that it does not change.
#
Ensures that once a student receives a final score for peer grading, that it does not change.
self
.
student_data_for_location
=
response
self
.
student_data_for_location
=
response
if
self
.
weight
is
not
None
:
if
self
.
weight
is
not
None
:
...
@@ -271,10 +272,10 @@ class PeerGradingModule(PeerGradingFields, XModule):
...
@@ -271,10 +272,10 @@ class PeerGradingModule(PeerGradingFields, XModule):
response
=
self
.
peer_gs
.
get_next_submission
(
location
,
grader_id
)
response
=
self
.
peer_gs
.
get_next_submission
(
location
,
grader_id
)
return
response
return
response
except
GradingServiceError
:
except
GradingServiceError
:
#This is a dev_facing_error
#
This is a dev_facing_error
log
.
exception
(
"Error getting next submission. server url: {0} location: {1}, grader_id: {2}"
log
.
exception
(
"Error getting next submission. server url: {0} location: {1}, grader_id: {2}"
.
format
(
self
.
peer_gs
.
url
,
location
,
grader_id
))
.
format
(
self
.
peer_gs
.
url
,
location
,
grader_id
))
#This is a student_facing_error
#
This is a student_facing_error
return
{
'success'
:
False
,
return
{
'success'
:
False
,
'error'
:
EXTERNAL_GRADER_NO_CONTACT_ERROR
}
'error'
:
EXTERNAL_GRADER_NO_CONTACT_ERROR
}
...
@@ -314,13 +315,13 @@ class PeerGradingModule(PeerGradingFields, XModule):
...
@@ -314,13 +315,13 @@ class PeerGradingModule(PeerGradingFields, XModule):
score
,
feedback
,
submission_key
,
rubric_scores
,
submission_flagged
)
score
,
feedback
,
submission_key
,
rubric_scores
,
submission_flagged
)
return
response
return
response
except
GradingServiceError
:
except
GradingServiceError
:
#This is a dev_facing_error
#
This is a dev_facing_error
log
.
exception
(
"""Error saving grade to open ended grading service. server url: {0}, location: {1}, submission_id:{2},
log
.
exception
(
"""Error saving grade to open ended grading service. server url: {0}, location: {1}, submission_id:{2},
submission_key: {3}, score: {4}"""
submission_key: {3}, score: {4}"""
.
format
(
self
.
peer_gs
.
url
,
.
format
(
self
.
peer_gs
.
url
,
location
,
submission_id
,
submission_key
,
score
)
location
,
submission_id
,
submission_key
,
score
)
)
)
#This is a student_facing_error
#
This is a student_facing_error
return
{
return
{
'success'
:
False
,
'success'
:
False
,
'error'
:
EXTERNAL_GRADER_NO_CONTACT_ERROR
'error'
:
EXTERNAL_GRADER_NO_CONTACT_ERROR
...
@@ -356,10 +357,10 @@ class PeerGradingModule(PeerGradingFields, XModule):
...
@@ -356,10 +357,10 @@ class PeerGradingModule(PeerGradingFields, XModule):
response
=
self
.
peer_gs
.
is_student_calibrated
(
location
,
grader_id
)
response
=
self
.
peer_gs
.
is_student_calibrated
(
location
,
grader_id
)
return
response
return
response
except
GradingServiceError
:
except
GradingServiceError
:
#This is a dev_facing_error
#
This is a dev_facing_error
log
.
exception
(
"Error from open ended grading service. server url: {0}, grader_id: {0}, location: {1}"
log
.
exception
(
"Error from open ended grading service. server url: {0}, grader_id: {0}, location: {1}"
.
format
(
self
.
peer_gs
.
url
,
grader_id
,
location
))
.
format
(
self
.
peer_gs
.
url
,
grader_id
,
location
))
#This is a student_facing_error
#
This is a student_facing_error
return
{
return
{
'success'
:
False
,
'success'
:
False
,
'error'
:
EXTERNAL_GRADER_NO_CONTACT_ERROR
'error'
:
EXTERNAL_GRADER_NO_CONTACT_ERROR
...
@@ -401,17 +402,17 @@ class PeerGradingModule(PeerGradingFields, XModule):
...
@@ -401,17 +402,17 @@ class PeerGradingModule(PeerGradingFields, XModule):
response
=
self
.
peer_gs
.
show_calibration_essay
(
location
,
grader_id
)
response
=
self
.
peer_gs
.
show_calibration_essay
(
location
,
grader_id
)
return
response
return
response
except
GradingServiceError
:
except
GradingServiceError
:
#This is a dev_facing_error
#
This is a dev_facing_error
log
.
exception
(
"Error from open ended grading service. server url: {0}, location: {0}"
log
.
exception
(
"Error from open ended grading service. server url: {0}, location: {0}"
.
format
(
self
.
peer_gs
.
url
,
location
))
.
format
(
self
.
peer_gs
.
url
,
location
))
#This is a student_facing_error
#
This is a student_facing_error
return
{
'success'
:
False
,
return
{
'success'
:
False
,
'error'
:
EXTERNAL_GRADER_NO_CONTACT_ERROR
}
'error'
:
EXTERNAL_GRADER_NO_CONTACT_ERROR
}
# if we can't parse the rubric into HTML,
# if we can't parse the rubric into HTML,
except
etree
.
XMLSyntaxError
:
except
etree
.
XMLSyntaxError
:
#This is a dev_facing_error
#
This is a dev_facing_error
log
.
exception
(
"Cannot parse rubric string."
)
log
.
exception
(
"Cannot parse rubric string."
)
#This is a student_facing_error
#
This is a student_facing_error
return
{
'success'
:
False
,
return
{
'success'
:
False
,
'error'
:
'Error displaying submission. Please notify course staff.'
}
'error'
:
'Error displaying submission. Please notify course staff.'
}
...
@@ -455,11 +456,11 @@ class PeerGradingModule(PeerGradingFields, XModule):
...
@@ -455,11 +456,11 @@ class PeerGradingModule(PeerGradingFields, XModule):
response
[
'actual_rubric'
]
=
rubric_renderer
.
render_rubric
(
response
[
'actual_rubric'
])[
'html'
]
response
[
'actual_rubric'
]
=
rubric_renderer
.
render_rubric
(
response
[
'actual_rubric'
])[
'html'
]
return
response
return
response
except
GradingServiceError
:
except
GradingServiceError
:
#This is a dev_facing_error
#
This is a dev_facing_error
log
.
exception
(
log
.
exception
(
"Error saving calibration grade, location: {0}, submission_key: {1}, grader_id: {2}"
.
format
(
"Error saving calibration grade, location: {0}, submission_key: {1}, grader_id: {2}"
.
format
(
location
,
submission_key
,
grader_id
))
location
,
submission_key
,
grader_id
))
#This is a student_facing_error
#
This is a student_facing_error
return
self
.
_err_response
(
'There was an error saving your score. Please notify course staff.'
)
return
self
.
_err_response
(
'There was an error saving your score. Please notify course staff.'
)
def
peer_grading_closed
(
self
):
def
peer_grading_closed
(
self
):
...
@@ -491,13 +492,13 @@ class PeerGradingModule(PeerGradingFields, XModule):
...
@@ -491,13 +492,13 @@ class PeerGradingModule(PeerGradingFields, XModule):
problem_list
=
problem_list_dict
[
'problem_list'
]
problem_list
=
problem_list_dict
[
'problem_list'
]
except
GradingServiceError
:
except
GradingServiceError
:
#This is a student_facing_error
#
This is a student_facing_error
error_text
=
EXTERNAL_GRADER_NO_CONTACT_ERROR
error_text
=
EXTERNAL_GRADER_NO_CONTACT_ERROR
log
.
error
(
error_text
)
log
.
error
(
error_text
)
success
=
False
success
=
False
# catch error if if the json loads fails
# catch error if if the json loads fails
except
ValueError
:
except
ValueError
:
#This is a student_facing_error
#
This is a student_facing_error
error_text
=
"Could not get list of problems to peer grade. Please notify course staff."
error_text
=
"Could not get list of problems to peer grade. Please notify course staff."
log
.
error
(
error_text
)
log
.
error
(
error_text
)
success
=
False
success
=
False
...
@@ -557,8 +558,8 @@ class PeerGradingModule(PeerGradingFields, XModule):
...
@@ -557,8 +558,8 @@ class PeerGradingModule(PeerGradingFields, XModule):
'''
'''
if
get
is
None
or
get
.
get
(
'location'
)
is
None
:
if
get
is
None
or
get
.
get
(
'location'
)
is
None
:
if
self
.
use_for_single_location
not
in
TRUE_DICT
:
if
self
.
use_for_single_location
not
in
TRUE_DICT
:
#This is an error case, because it must be set to use a single location to be called without get parameters
#
This is an error case, because it must be set to use a single location to be called without get parameters
#This is a dev_facing_error
#
This is a dev_facing_error
log
.
error
(
log
.
error
(
"Peer grading problem in peer_grading_module called with no get parameters, but use_for_single_location is False."
)
"Peer grading problem in peer_grading_module called with no get parameters, but use_for_single_location is False."
)
return
{
'html'
:
""
,
'success'
:
False
}
return
{
'html'
:
""
,
'success'
:
False
}
...
...
common/lib/xmodule/xmodule/tests/test_course_module.py
View file @
d7194e6b
import
unittest
import
unittest
from
time
import
strptime
import
datetime
import
datetime
from
fs.memoryfs
import
MemoryFS
from
fs.memoryfs
import
MemoryFS
...
@@ -8,13 +7,13 @@ from mock import Mock, patch
...
@@ -8,13 +7,13 @@ from mock import Mock, patch
from
xmodule.modulestore.xml
import
ImportSystem
,
XMLModuleStore
from
xmodule.modulestore.xml
import
ImportSystem
,
XMLModuleStore
import
xmodule.course_module
import
xmodule.course_module
from
xmodule.util.date_utils
import
time_to_datetime
from
django.utils.timezone
import
UTC
ORG
=
'test_org'
ORG
=
'test_org'
COURSE
=
'test_course'
COURSE
=
'test_course'
NOW
=
strptime
(
'2013-01-01T01:00:00'
,
'
%
Y-
%
m-
%
dT
%
H:
%
M:00'
)
NOW
=
datetime
.
datetime
.
strptime
(
'2013-01-01T01:00:00'
,
'
%
Y-
%
m-
%
dT
%
H:
%
M:00'
)
.
replace
(
tzinfo
=
UTC
()
)
class
DummySystem
(
ImportSystem
):
class
DummySystem
(
ImportSystem
):
...
@@ -81,10 +80,10 @@ class IsNewCourseTestCase(unittest.TestCase):
...
@@ -81,10 +80,10 @@ class IsNewCourseTestCase(unittest.TestCase):
Mock
(
wraps
=
datetime
.
datetime
)
Mock
(
wraps
=
datetime
.
datetime
)
)
)
mocked_datetime
=
datetime_patcher
.
start
()
mocked_datetime
=
datetime_patcher
.
start
()
mocked_datetime
.
utcnow
.
return_value
=
time_to_datetime
(
NOW
)
mocked_datetime
.
now
.
return_value
=
NOW
self
.
addCleanup
(
datetime_patcher
.
stop
)
self
.
addCleanup
(
datetime_patcher
.
stop
)
@patch
(
'xmodule.course_module.
time.gmtime
'
)
@patch
(
'xmodule.course_module.
datetime.now
'
)
def
test_sorting_score
(
self
,
gmtime_mock
):
def
test_sorting_score
(
self
,
gmtime_mock
):
gmtime_mock
.
return_value
=
NOW
gmtime_mock
.
return_value
=
NOW
...
@@ -125,7 +124,7 @@ class IsNewCourseTestCase(unittest.TestCase):
...
@@ -125,7 +124,7 @@ class IsNewCourseTestCase(unittest.TestCase):
print
"Comparing
%
s to
%
s"
%
(
a
,
b
)
print
"Comparing
%
s to
%
s"
%
(
a
,
b
)
assertion
(
a_score
,
b_score
)
assertion
(
a_score
,
b_score
)
@patch
(
'xmodule.course_module.
time.gmtime
'
)
@patch
(
'xmodule.course_module.
datetime.now
'
)
def
test_start_date_text
(
self
,
gmtime_mock
):
def
test_start_date_text
(
self
,
gmtime_mock
):
gmtime_mock
.
return_value
=
NOW
gmtime_mock
.
return_value
=
NOW
...
...
common/lib/xmodule/xmodule/tests/test_date_utils.py
deleted
100644 → 0
View file @
673c015e
# Tests for xmodule.util.date_utils
from
nose.tools
import
assert_equals
from
xmodule.util
import
date_utils
import
datetime
import
time
def
test_get_time_struct_display
():
assert_equals
(
""
,
date_utils
.
get_time_struct_display
(
None
,
""
))
test_time
=
time
.
struct_time
((
1992
,
3
,
12
,
15
,
3
,
30
,
1
,
71
,
0
))
assert_equals
(
"03/12/1992"
,
date_utils
.
get_time_struct_display
(
test_time
,
'
%
m/
%
d/
%
Y'
))
assert_equals
(
"15:03"
,
date_utils
.
get_time_struct_display
(
test_time
,
'
%
H:
%
M'
))
def
test_get_default_time_display
():
assert_equals
(
""
,
date_utils
.
get_default_time_display
(
None
))
test_time
=
time
.
struct_time
((
1992
,
3
,
12
,
15
,
3
,
30
,
1
,
71
,
0
))
assert_equals
(
"Mar 12, 1992 at 15:03 UTC"
,
date_utils
.
get_default_time_display
(
test_time
))
assert_equals
(
"Mar 12, 1992 at 15:03 UTC"
,
date_utils
.
get_default_time_display
(
test_time
,
True
))
assert_equals
(
"Mar 12, 1992 at 15:03"
,
date_utils
.
get_default_time_display
(
test_time
,
False
))
def
test_time_to_datetime
():
assert_equals
(
None
,
date_utils
.
time_to_datetime
(
None
))
test_time
=
time
.
struct_time
((
1992
,
3
,
12
,
15
,
3
,
30
,
1
,
71
,
0
))
assert_equals
(
datetime
.
datetime
(
1992
,
3
,
12
,
15
,
3
,
30
),
date_utils
.
time_to_datetime
(
test_time
))
common/lib/xmodule/xmodule/tests/test_fields.py
View file @
d7194e6b
"""Tests for classes defined in fields.py."""
"""Tests for classes defined in fields.py."""
import
datetime
import
datetime
import
unittest
import
unittest
from
django.utils.timezone
import
UTC
from
xmodule.fields
import
Date
,
StringyFloat
,
StringyInteger
,
StringyBoolean
from
xmodule.fields
import
Date
,
StringyFloat
,
StringyInteger
,
StringyBoolean
import
time
from
django.utils.timezone
import
UTC
class
DateTest
(
unittest
.
TestCase
):
class
DateTest
(
unittest
.
TestCase
):
date
=
Date
()
date
=
Date
()
@staticmethod
def
compare_dates
(
self
,
dt1
,
dt2
,
expected_delta
):
def
struct_to_datetime
(
struct_time
):
self
.
assertEqual
(
dt1
-
dt2
,
expected_delta
,
str
(
dt1
)
+
"-"
return
datetime
.
datetime
(
struct_time
.
tm_year
,
struct_time
.
tm_mon
,
+
str
(
dt2
)
+
"!="
+
str
(
expected_delta
))
struct_time
.
tm_mday
,
struct_time
.
tm_hour
,
struct_time
.
tm_min
,
struct_time
.
tm_sec
,
tzinfo
=
UTC
())
def
compare_dates
(
self
,
date1
,
date2
,
expected_delta
):
dt1
=
DateTest
.
struct_to_datetime
(
date1
)
dt2
=
DateTest
.
struct_to_datetime
(
date2
)
self
.
assertEqual
(
dt1
-
dt2
,
expected_delta
,
str
(
date1
)
+
"-"
+
str
(
date2
)
+
"!="
+
str
(
expected_delta
))
def
test_from_json
(
self
):
def
test_from_json
(
self
):
'''Test conversion from iso compatible date strings to struct_time'''
'''Test conversion from iso compatible date strings to struct_time'''
...
@@ -55,10 +46,10 @@ class DateTest(unittest.TestCase):
...
@@ -55,10 +46,10 @@ class DateTest(unittest.TestCase):
def
test_old_due_date_format
(
self
):
def
test_old_due_date_format
(
self
):
current
=
datetime
.
datetime
.
today
()
current
=
datetime
.
datetime
.
today
()
self
.
assertEqual
(
self
.
assertEqual
(
time
.
struct_time
((
current
.
year
,
3
,
12
,
12
,
0
,
0
,
1
,
71
,
0
)),
datetime
.
datetime
(
current
.
year
,
3
,
12
,
12
,
tzinfo
=
UTC
(
)),
DateTest
.
date
.
from_json
(
"March 12 12:00"
))
DateTest
.
date
.
from_json
(
"March 12 12:00"
))
self
.
assertEqual
(
self
.
assertEqual
(
time
.
struct_time
((
current
.
year
,
12
,
4
,
16
,
30
,
0
,
2
,
338
,
0
)),
datetime
.
datetime
(
current
.
year
,
12
,
4
,
16
,
30
,
tzinfo
=
UTC
(
)),
DateTest
.
date
.
from_json
(
"December 4 16:30"
))
DateTest
.
date
.
from_json
(
"December 4 16:30"
))
def
test_to_json
(
self
):
def
test_to_json
(
self
):
...
@@ -67,7 +58,7 @@ class DateTest(unittest.TestCase):
...
@@ -67,7 +58,7 @@ class DateTest(unittest.TestCase):
'''
'''
self
.
assertEqual
(
self
.
assertEqual
(
DateTest
.
date
.
to_json
(
DateTest
.
date
.
to_json
(
time
.
strptime
(
"2012-12-31T23:59:59Z"
,
"
%
Y-
%
m-
%
dT
%
H:
%
M:
%
SZ"
)),
datetime
.
date
time
.
strptime
(
"2012-12-31T23:59:59Z"
,
"
%
Y-
%
m-
%
dT
%
H:
%
M:
%
SZ"
)),
"2012-12-31T23:59:59Z"
)
"2012-12-31T23:59:59Z"
)
self
.
assertEqual
(
self
.
assertEqual
(
DateTest
.
date
.
to_json
(
DateTest
.
date
.
to_json
(
...
@@ -76,7 +67,7 @@ class DateTest(unittest.TestCase):
...
@@ -76,7 +67,7 @@ class DateTest(unittest.TestCase):
self
.
assertEqual
(
self
.
assertEqual
(
DateTest
.
date
.
to_json
(
DateTest
.
date
.
to_json
(
DateTest
.
date
.
from_json
(
"2012-12-31T23:00:01-01:00"
)),
DateTest
.
date
.
from_json
(
"2012-12-31T23:00:01-01:00"
)),
"201
3-01-01T00:00:01Z
"
)
"201
2-12-31T23:00:01-01:00
"
)
class
StringyIntegerTest
(
unittest
.
TestCase
):
class
StringyIntegerTest
(
unittest
.
TestCase
):
...
...
common/lib/xmodule/xmodule/tests/test_import.py
View file @
d7194e6b
...
@@ -13,6 +13,8 @@ from xmodule.modulestore.inheritance import compute_inherited_metadata
...
@@ -13,6 +13,8 @@ from xmodule.modulestore.inheritance import compute_inherited_metadata
from
xmodule.fields
import
Date
from
xmodule.fields
import
Date
from
.test_export
import
DATA_DIR
from
.test_export
import
DATA_DIR
import
datetime
from
django.utils.timezone
import
UTC
ORG
=
'test_org'
ORG
=
'test_org'
COURSE
=
'test_course'
COURSE
=
'test_course'
...
@@ -40,7 +42,7 @@ class DummySystem(ImportSystem):
...
@@ -40,7 +42,7 @@ class DummySystem(ImportSystem):
load_error_modules
=
load_error_modules
,
load_error_modules
=
load_error_modules
,
)
)
def
render_template
(
self
,
template
,
context
):
def
render_template
(
self
,
_template
,
_
context
):
raise
Exception
(
"Shouldn't be called"
)
raise
Exception
(
"Shouldn't be called"
)
...
@@ -62,6 +64,7 @@ class BaseCourseTestCase(unittest.TestCase):
...
@@ -62,6 +64,7 @@ class BaseCourseTestCase(unittest.TestCase):
class
ImportTestCase
(
BaseCourseTestCase
):
class
ImportTestCase
(
BaseCourseTestCase
):
date
=
Date
()
def
test_fallback
(
self
):
def
test_fallback
(
self
):
'''Check that malformed xml loads as an ErrorDescriptor.'''
'''Check that malformed xml loads as an ErrorDescriptor.'''
...
@@ -145,15 +148,18 @@ class ImportTestCase(BaseCourseTestCase):
...
@@ -145,15 +148,18 @@ class ImportTestCase(BaseCourseTestCase):
descriptor
=
system
.
process_xml
(
start_xml
)
descriptor
=
system
.
process_xml
(
start_xml
)
compute_inherited_metadata
(
descriptor
)
compute_inherited_metadata
(
descriptor
)
# pylint: disable=W0212
print
(
descriptor
,
descriptor
.
_model_data
)
print
(
descriptor
,
descriptor
.
_model_data
)
self
.
assertEqual
(
descriptor
.
lms
.
due
,
Date
()
.
from_json
(
v
))
self
.
assertEqual
(
descriptor
.
lms
.
due
,
ImportTestCase
.
date
.
from_json
(
v
))
# Check that the child inherits due correctly
# Check that the child inherits due correctly
child
=
descriptor
.
get_children
()[
0
]
child
=
descriptor
.
get_children
()[
0
]
self
.
assertEqual
(
child
.
lms
.
due
,
Date
()
.
from_json
(
v
))
self
.
assertEqual
(
child
.
lms
.
due
,
ImportTestCase
.
date
.
from_json
(
v
))
self
.
assertEqual
(
child
.
_inheritable_metadata
,
child
.
_inherited_metadata
)
self
.
assertEqual
(
child
.
_inheritable_metadata
,
child
.
_inherited_metadata
)
self
.
assertEqual
(
2
,
len
(
child
.
_inherited_metadata
))
self
.
assertEqual
(
2
,
len
(
child
.
_inherited_metadata
))
self
.
assertEqual
(
'1970-01-01T00:00:00Z'
,
child
.
_inherited_metadata
[
'start'
])
self
.
assertLessEqual
(
ImportTestCase
.
date
.
from_json
(
child
.
_inherited_metadata
[
'start'
]),
datetime
.
datetime
.
now
(
UTC
()))
self
.
assertEqual
(
v
,
child
.
_inherited_metadata
[
'due'
])
self
.
assertEqual
(
v
,
child
.
_inherited_metadata
[
'due'
])
# Now export and check things
# Now export and check things
...
@@ -209,9 +215,13 @@ class ImportTestCase(BaseCourseTestCase):
...
@@ -209,9 +215,13 @@ class ImportTestCase(BaseCourseTestCase):
# Check that the child does not inherit a value for due
# Check that the child does not inherit a value for due
child
=
descriptor
.
get_children
()[
0
]
child
=
descriptor
.
get_children
()[
0
]
self
.
assertEqual
(
child
.
lms
.
due
,
None
)
self
.
assertEqual
(
child
.
lms
.
due
,
None
)
# pylint: disable=W0212
self
.
assertEqual
(
child
.
_inheritable_metadata
,
child
.
_inherited_metadata
)
self
.
assertEqual
(
child
.
_inheritable_metadata
,
child
.
_inherited_metadata
)
self
.
assertEqual
(
1
,
len
(
child
.
_inherited_metadata
))
self
.
assertEqual
(
1
,
len
(
child
.
_inherited_metadata
))
self
.
assertEqual
(
'1970-01-01T00:00:00Z'
,
child
.
_inherited_metadata
[
'start'
])
# why do these tests look in the internal structure v just calling child.start?
self
.
assertLessEqual
(
ImportTestCase
.
date
.
from_json
(
child
.
_inherited_metadata
[
'start'
]),
datetime
.
datetime
.
now
(
UTC
()))
def
test_metadata_override_default
(
self
):
def
test_metadata_override_default
(
self
):
"""
"""
...
@@ -230,14 +240,17 @@ class ImportTestCase(BaseCourseTestCase):
...
@@ -230,14 +240,17 @@ class ImportTestCase(BaseCourseTestCase):
</course>'''
.
format
(
due
=
course_due
,
org
=
ORG
,
course
=
COURSE
,
url_name
=
url_name
)
</course>'''
.
format
(
due
=
course_due
,
org
=
ORG
,
course
=
COURSE
,
url_name
=
url_name
)
descriptor
=
system
.
process_xml
(
start_xml
)
descriptor
=
system
.
process_xml
(
start_xml
)
child
=
descriptor
.
get_children
()[
0
]
child
=
descriptor
.
get_children
()[
0
]
# pylint: disable=W0212
child
.
_model_data
[
'due'
]
=
child_due
child
.
_model_data
[
'due'
]
=
child_due
compute_inherited_metadata
(
descriptor
)
compute_inherited_metadata
(
descriptor
)
self
.
assertEqual
(
descriptor
.
lms
.
due
,
Date
()
.
from_json
(
course_due
))
self
.
assertEqual
(
descriptor
.
lms
.
due
,
ImportTestCase
.
date
.
from_json
(
course_due
))
self
.
assertEqual
(
child
.
lms
.
due
,
Date
()
.
from_json
(
child_due
))
self
.
assertEqual
(
child
.
lms
.
due
,
ImportTestCase
.
date
.
from_json
(
child_due
))
# Test inherited metadata. Due does not appear here (because explicitly set on child).
# Test inherited metadata. Due does not appear here (because explicitly set on child).
self
.
assertEqual
(
1
,
len
(
child
.
_inherited_metadata
))
self
.
assertEqual
(
1
,
len
(
child
.
_inherited_metadata
))
self
.
assertEqual
(
'1970-01-01T00:00:00Z'
,
child
.
_inherited_metadata
[
'start'
])
self
.
assertLessEqual
(
ImportTestCase
.
date
.
from_json
(
child
.
_inherited_metadata
[
'start'
]),
datetime
.
datetime
.
now
(
UTC
()))
# Test inheritable metadata. This has the course inheritable value for due.
# Test inheritable metadata. This has the course inheritable value for due.
self
.
assertEqual
(
2
,
len
(
child
.
_inheritable_metadata
))
self
.
assertEqual
(
2
,
len
(
child
.
_inheritable_metadata
))
self
.
assertEqual
(
course_due
,
child
.
_inheritable_metadata
[
'due'
])
self
.
assertEqual
(
course_due
,
child
.
_inheritable_metadata
[
'due'
])
...
...
common/lib/xmodule/xmodule/timeinfo.py
View file @
d7194e6b
from
.timeparse
import
parse_timedelta
from
.timeparse
import
parse_timedelta
from
xmodule.util.date_utils
import
time_to_datetime
import
logging
import
logging
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -17,7 +16,7 @@ class TimeInfo(object):
...
@@ -17,7 +16,7 @@ class TimeInfo(object):
"""
"""
def
__init__
(
self
,
due_date
,
grace_period_string
):
def
__init__
(
self
,
due_date
,
grace_period_string
):
if
due_date
is
not
None
:
if
due_date
is
not
None
:
self
.
display_due_date
=
time_to_datetime
(
due_date
)
self
.
display_due_date
=
due_date
else
:
else
:
self
.
display_due_date
=
None
self
.
display_due_date
=
None
...
...
common/lib/xmodule/xmodule/timeparse.py
View file @
d7194e6b
"""
"""
Helper functions for handling time in the format we like.
Helper functions for handling time in the format we like.
"""
"""
import
time
import
re
import
re
from
datetime
import
timedelta
from
datetime
import
timedelta
,
datetime
TIME_FORMAT
=
"
%
Y-
%
m-
%
dT
%
H:
%
M"
TIME_FORMAT
=
"
%
Y-
%
m-
%
dT
%
H:
%
M"
...
@@ -17,14 +16,14 @@ def parse_time(time_str):
...
@@ -17,14 +16,14 @@ def parse_time(time_str):
Raises ValueError if the string is not in the right format.
Raises ValueError if the string is not in the right format.
"""
"""
return
time
.
strptime
(
time_str
,
TIME_FORMAT
)
return
date
time
.
strptime
(
time_str
,
TIME_FORMAT
)
def
stringify_time
(
time_struc
t
):
def
stringify_time
(
d
t
):
"""
"""
Convert a time struct to a string
Convert a
date
time struct to a string
"""
"""
return
time
.
strftime
(
TIME_FORMAT
,
time_struct
)
return
dt
.
isoformat
(
)
def
parse_timedelta
(
time_str
):
def
parse_timedelta
(
time_str
):
"""
"""
...
...
common/lib/xmodule/xmodule/util/date_utils.py
View file @
d7194e6b
import
time
def
get_default_time_display
(
dt
,
show_timezone
=
True
):
import
datetime
def
get_default_time_display
(
time_struct
,
show_timezone
=
True
):
"""
"""
Converts a
time struct
to a string representation. This is the default
Converts a
datetime
to a string representation. This is the default
representation used in Studio and LMS.
representation used in Studio and LMS.
It is of the form "Apr 09, 2013 at 16:00" or "Apr 09, 2013 at 16:00 UTC",
It is of the form "Apr 09, 2013 at 16:00" or "Apr 09, 2013 at 16:00 UTC",
depending on the value of show_timezone.
depending on the value of show_timezone.
If None is passed in for
time_struc
t, an empty string will be returned.
If None is passed in for
d
t, an empty string will be returned.
The default value of show_timezone is True.
The default value of show_timezone is True.
"""
"""
timezone
=
""
if
time_struct
is
None
or
not
show_timezone
else
" UTC"
timezone
=
""
return
get_time_struct_display
(
time_struct
,
"
%
b
%
d,
%
Y at
%
H:
%
M"
)
+
timezone
if
dt
is
not
None
and
show_timezone
:
if
dt
.
tzinfo
is
not
None
:
try
:
def
get_time_struct_display
(
time_struct
,
format
):
timezone
=
dt
.
tzinfo
.
tzname
(
dt
)
"""
except
NotImplementedError
:
Converts a time struct to a string based on the given format.
timezone
=
" UTC"
else
:
If None is passed in, an empty string will be returned.
timezone
=
" UTC"
"""
return
dt
.
strftime
(
"
%
b
%
d,
%
Y at
%
H:
%
M"
)
+
timezone
return
''
if
time_struct
is
None
else
time
.
strftime
(
format
,
time_struct
)
def
time_to_datetime
(
time_struct
):
"""
Convert a time struct to a datetime.
If None is passed in, None will be returned.
"""
return
datetime
.
datetime
(
*
time_struct
[:
6
])
if
time_struct
else
None
common/test/data/full/sequential/Administrivia_and_Circuit_Elements.xml
View file @
d7194e6b
<sequential>
<sequential>
<vertical
filename=
"vertical_58"
slug=
"vertical_58"
graceperiod=
"1 day 12 hours 59 minutes 59 seconds"
showanswer=
"attempted"
rerandomize=
"never"
/>
<vertical
filename=
"vertical_58"
slug=
"vertical_58"
<vertical
slug=
"vertical_66"
graceperiod=
"1 day 12 hours 59 minutes 59 seconds"
showanswer=
"attempted"
rerandomize=
"never"
>
graceperiod=
"1 day 12 hours 59 minutes 59 seconds"
showanswer=
"attempted"
<problem
filename=
"S1E3_AC_power"
slug=
"S1E3_AC_power"
name=
"S1E3: AC power"
/>
rerandomize=
"never"
/>
<customtag
tag=
"S1E3"
slug=
"discuss_67"
impl=
"discuss"
/>
<vertical
slug=
"vertical_66"
graceperiod=
"1 day 12 hours 59 minutes 59 seconds"
<!-- utf-8 characters acceptable, but not HTML entities -->
showanswer=
"attempted"
rerandomize=
"never"
>
<html
slug=
"html_68"
>
S1E4 has been removed…
</html>
<problem
filename=
"S1E3_AC_power"
slug=
"S1E3_AC_power"
</vertical>
name=
"S1E3: AC power"
/>
<vertical
filename=
"vertical_89"
slug=
"vertical_89"
graceperiod=
"1 day 12 hours 59 minutes 59 seconds"
showanswer=
"attempted"
rerandomize=
"never"
/>
<customtag
tag=
"S1E3"
slug=
"discuss_67"
impl=
"discuss"
/>
<vertical
slug=
"vertical_94"
graceperiod=
"1 day 12 hours 59 minutes 59 seconds"
showanswer=
"attempted"
rerandomize=
"never"
>
<!-- utf-8 characters acceptable, but not HTML entities -->
<video
youtube=
"0.75:XNh13VZhThQ,1.0:XbDRmF6J0K0,1.25:JDty12WEQWk,1.50:wELKGj-5iyM"
slug=
"What_s_next"
name=
"What's next"
/>
<html
slug=
"html_68"
>
S1E4 has been removed…
</html>
<html
slug=
"html_95"
>
Minor correction: Six elements (five resistors)…
</html>
</vertical>
<customtag
tag=
"S1"
slug=
"discuss_96"
impl=
"discuss"
/>
<vertical
filename=
"vertical_89"
slug=
"vertical_89"
</vertical>
graceperiod=
"1 day 12 hours 59 minutes 59 seconds"
showanswer=
"attempted"
rerandomize=
"never"
/>
<vertical
slug=
"vertical_94"
graceperiod=
"1 day 12 hours 59 minutes 59 seconds"
showanswer=
"attempted"
rerandomize=
"never"
>
<video
youtube=
"0.75:XNh13VZhThQ,1.0:XbDRmF6J0K0,1.25:JDty12WEQWk,1.50:wELKGj-5iyM"
slug=
"What_s_next"
name=
"What's next"
/>
<html
slug=
"html_95"
>
Minor correction: Six elements (five resistors)…
</html>
<customtag
tag=
"S1"
slug=
"discuss_96"
impl=
"discuss"
/>
</vertical>
<randomize
url_name=
"PS1_Q4"
display_name=
"Problem 4: Read a Molecule"
>
<randomize
url_name=
"PS1_Q4"
display_name=
"Problem 4: Read a Molecule"
>
<vertical>
<vertical>
<html
slug=
"html_900"
>
<html
slug=
"html_900"
>
<!-- UTF-8 characters are acceptable… HTML entities are not -->
<!-- UTF-8 characters are acceptable… HTML entities are not -->
<h1>
Inline content…
</h1>
<h1>
Inline content…
</h1>
</html>
</html>
</vertical>
</vertical>
</randomize>
</randomize>
</sequential>
</sequential>
lms/djangoapps/courseware/access.py
View file @
d7194e6b
...
@@ -16,6 +16,7 @@ from xmodule.x_module import XModule, XModuleDescriptor
...
@@ -16,6 +16,7 @@ from xmodule.x_module import XModule, XModuleDescriptor
from
student.models
import
CourseEnrollmentAllowed
from
student.models
import
CourseEnrollmentAllowed
from
courseware.masquerade
import
is_masquerading_as_student
from
courseware.masquerade
import
is_masquerading_as_student
from
django.utils.timezone
import
UTC
DEBUG_ACCESS
=
False
DEBUG_ACCESS
=
False
...
@@ -133,7 +134,7 @@ def _has_access_course_desc(user, course, action):
...
@@ -133,7 +134,7 @@ def _has_access_course_desc(user, course, action):
(staff can always enroll)
(staff can always enroll)
"""
"""
now
=
time
.
gmtime
(
)
now
=
datetime
.
now
(
UTC
()
)
start
=
course
.
enrollment_start
start
=
course
.
enrollment_start
end
=
course
.
enrollment_end
end
=
course
.
enrollment_end
...
@@ -242,7 +243,7 @@ def _has_access_descriptor(user, descriptor, action, course_context=None):
...
@@ -242,7 +243,7 @@ def _has_access_descriptor(user, descriptor, action, course_context=None):
# Check start date
# Check start date
if
descriptor
.
lms
.
start
is
not
None
:
if
descriptor
.
lms
.
start
is
not
None
:
now
=
time
.
gmtime
(
)
now
=
datetime
.
now
(
UTC
()
)
effective_start
=
_adjust_start_date_for_beta_testers
(
user
,
descriptor
)
effective_start
=
_adjust_start_date_for_beta_testers
(
user
,
descriptor
)
if
now
>
effective_start
:
if
now
>
effective_start
:
# after start date, everyone can see it
# after start date, everyone can see it
...
@@ -365,7 +366,7 @@ def _course_org_staff_group_name(location, course_context=None):
...
@@ -365,7 +366,7 @@ def _course_org_staff_group_name(location, course_context=None):
def
group_names_for
(
role
,
location
,
course_context
=
None
):
def
group_names_for
(
role
,
location
,
course_context
=
None
):
"""Returns the group names for a given role with this location. Plural
"""Returns the group names for a given role with this location. Plural
because it will return both the name we expect now as well as the legacy
because it will return both the name we expect now as well as the legacy
group name we support for backwards compatibility. This should not check
group name we support for backwards compatibility. This should not check
the DB for existence of a group (like some of its callers do) because that's
the DB for existence of a group (like some of its callers do) because that's
...
@@ -483,8 +484,7 @@ def _adjust_start_date_for_beta_testers(user, descriptor):
...
@@ -483,8 +484,7 @@ def _adjust_start_date_for_beta_testers(user, descriptor):
non-None start date.
non-None start date.
Returns:
Returns:
A time, in the same format as returned by time.gmtime(). Either the same as
A datetime. Either the same as start, or earlier for beta testers.
start, or earlier for beta testers.
NOTE: number of days to adjust should be cached to avoid looking it up thousands of
NOTE: number of days to adjust should be cached to avoid looking it up thousands of
times per query.
times per query.
...
@@ -505,15 +505,11 @@ def _adjust_start_date_for_beta_testers(user, descriptor):
...
@@ -505,15 +505,11 @@ def _adjust_start_date_for_beta_testers(user, descriptor):
beta_group
=
course_beta_test_group_name
(
descriptor
.
location
)
beta_group
=
course_beta_test_group_name
(
descriptor
.
location
)
if
beta_group
in
user_groups
:
if
beta_group
in
user_groups
:
debug
(
"Adjust start time: user in group
%
s"
,
beta_group
)
debug
(
"Adjust start time: user in group
%
s"
,
beta_group
)
# time_structs don't support subtraction, so convert to datetimes,
start_as_datetime
=
descriptor
.
lms
.
start
# subtract, convert back.
# (fun fact: datetime(*a_time_struct[:6]) is the beautiful syntax for
# converting time_structs into datetimes)
start_as_datetime
=
datetime
(
*
descriptor
.
lms
.
start
[:
6
])
delta
=
timedelta
(
descriptor
.
lms
.
days_early_for_beta
)
delta
=
timedelta
(
descriptor
.
lms
.
days_early_for_beta
)
effective
=
start_as_datetime
-
delta
effective
=
start_as_datetime
-
delta
# ...and back to time_struct
# ...and back to time_struct
return
effective
.
timetuple
()
return
effective
return
descriptor
.
lms
.
start
return
descriptor
.
lms
.
start
...
@@ -564,7 +560,7 @@ def _has_access_to_location(user, location, access_level, course_context):
...
@@ -564,7 +560,7 @@ def _has_access_to_location(user, location, access_level, course_context):
return
True
return
True
debug
(
"Deny: user not in groups
%
s"
,
staff_groups
)
debug
(
"Deny: user not in groups
%
s"
,
staff_groups
)
if
access_level
==
'instructor'
or
access_level
==
'staff'
:
# instructors get staff privileges
if
access_level
==
'instructor'
or
access_level
==
'staff'
:
# instructors get staff privileges
instructor_groups
=
group_names_for_instructor
(
location
,
course_context
)
+
\
instructor_groups
=
group_names_for_instructor
(
location
,
course_context
)
+
\
[
_course_org_instructor_group_name
(
location
,
course_context
)]
[
_course_org_instructor_group_name
(
location
,
course_context
)]
for
instructor_group
in
instructor_groups
:
for
instructor_group
in
instructor_groups
:
...
...
lms/djangoapps/courseware/tests/test_access.py
View file @
d7194e6b
import
unittest
from
mock
import
Mock
,
patch
import
logging
import
time
from
mock
import
Mock
,
MagicMock
,
patch
from
django.conf
import
settings
from
django.test
import
TestCase
from
django.test
import
TestCase
from
xmodule.course_module
import
CourseDescriptor
from
xmodule.error_module
import
ErrorDescriptor
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.timeparse
import
parse_time
from
xmodule.x_module
import
XModule
,
XModuleDescriptor
import
courseware.access
as
access
import
courseware.access
as
access
from
.factories
import
CourseEnrollmentAllowedFactory
from
.factories
import
CourseEnrollmentAllowedFactory
import
datetime
from
django.utils.timezone
import
UTC
class
AccessTestCase
(
TestCase
):
class
AccessTestCase
(
TestCase
):
...
@@ -77,7 +71,7 @@ class AccessTestCase(TestCase):
...
@@ -77,7 +71,7 @@ class AccessTestCase(TestCase):
# TODO: override DISABLE_START_DATES and test the start date branch of the method
# TODO: override DISABLE_START_DATES and test the start date branch of the method
u
=
Mock
()
u
=
Mock
()
d
=
Mock
()
d
=
Mock
()
d
.
start
=
time
.
gmtime
(
time
.
time
()
-
86400
)
# make sure the start time is in the past
d
.
start
=
datetime
.
datetime
.
now
(
UTC
())
-
datetime
.
timedelta
(
days
=
1
)
# make sure the start time is in the past
# Always returns true because DISABLE_START_DATES is set in test.py
# Always returns true because DISABLE_START_DATES is set in test.py
self
.
assertTrue
(
access
.
_has_access_descriptor
(
u
,
d
,
'load'
))
self
.
assertTrue
(
access
.
_has_access_descriptor
(
u
,
d
,
'load'
))
...
@@ -85,8 +79,8 @@ class AccessTestCase(TestCase):
...
@@ -85,8 +79,8 @@ class AccessTestCase(TestCase):
def
test__has_access_course_desc_can_enroll
(
self
):
def
test__has_access_course_desc_can_enroll
(
self
):
u
=
Mock
()
u
=
Mock
()
yesterday
=
time
.
gmtime
(
time
.
time
()
-
86400
)
yesterday
=
datetime
.
datetime
.
now
(
UTC
())
-
datetime
.
timedelta
(
days
=
1
)
tomorrow
=
time
.
gmtime
(
time
.
time
()
+
86400
)
tomorrow
=
datetime
.
datetime
.
now
(
UTC
())
+
datetime
.
timedelta
(
days
=
1
)
c
=
Mock
(
enrollment_start
=
yesterday
,
enrollment_end
=
tomorrow
)
c
=
Mock
(
enrollment_start
=
yesterday
,
enrollment_end
=
tomorrow
)
# User can enroll if it is between the start and end dates
# User can enroll if it is between the start and end dates
...
...
lms/djangoapps/courseware/tests/tests.py
View file @
d7194e6b
...
@@ -3,7 +3,6 @@ Test for lms courseware app
...
@@ -3,7 +3,6 @@ Test for lms courseware app
'''
'''
import
logging
import
logging
import
json
import
json
import
time
import
random
import
random
from
urlparse
import
urlsplit
,
urlunsplit
from
urlparse
import
urlsplit
,
urlunsplit
...
@@ -30,6 +29,8 @@ from xmodule.modulestore.django import modulestore
...
@@ -30,6 +29,8 @@ from xmodule.modulestore.django import modulestore
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.xml_importer
import
import_from_xml
from
xmodule.modulestore.xml_importer
import
import_from_xml
from
xmodule.modulestore.xml
import
XMLModuleStore
from
xmodule.modulestore.xml
import
XMLModuleStore
import
datetime
from
django.utils.timezone
import
UTC
log
=
logging
.
getLogger
(
"mitx."
+
__name__
)
log
=
logging
.
getLogger
(
"mitx."
+
__name__
)
...
@@ -603,9 +604,9 @@ class TestViewAuth(LoginEnrollmentTestCase):
...
@@ -603,9 +604,9 @@ class TestViewAuth(LoginEnrollmentTestCase):
"""Actually do the test, relying on settings to be right."""
"""Actually do the test, relying on settings to be right."""
# Make courses start in the future
# Make courses start in the future
tomorrow
=
time
.
time
()
+
24
*
3600
tomorrow
=
datetime
.
datetime
.
now
(
UTC
())
+
datetime
.
timedelta
(
days
=
1
)
self
.
toy
.
lms
.
start
=
t
ime
.
gmtime
(
tomorrow
)
self
.
toy
.
lms
.
start
=
t
omorrow
self
.
full
.
lms
.
start
=
t
ime
.
gmtime
(
tomorrow
)
self
.
full
.
lms
.
start
=
t
omorrow
self
.
assertFalse
(
self
.
toy
.
has_started
())
self
.
assertFalse
(
self
.
toy
.
has_started
())
self
.
assertFalse
(
self
.
full
.
has_started
())
self
.
assertFalse
(
self
.
full
.
has_started
())
...
@@ -728,18 +729,18 @@ class TestViewAuth(LoginEnrollmentTestCase):
...
@@ -728,18 +729,18 @@ class TestViewAuth(LoginEnrollmentTestCase):
"""Actually do the test, relying on settings to be right."""
"""Actually do the test, relying on settings to be right."""
# Make courses start in the future
# Make courses start in the future
tomorrow
=
time
.
time
()
+
24
*
3600
tomorrow
=
datetime
.
datetime
.
now
(
UTC
())
+
datetime
.
timedelta
(
days
=
1
)
nextday
=
tomorrow
+
24
*
3600
nextday
=
tomorrow
+
datetime
.
timedelta
(
days
=
1
)
yesterday
=
time
.
time
()
-
24
*
3600
yesterday
=
datetime
.
datetime
.
now
(
UTC
())
-
datetime
.
timedelta
(
days
=
1
)
print
"changing"
print
"changing"
# toy course's enrollment period hasn't started
# toy course's enrollment period hasn't started
self
.
toy
.
enrollment_start
=
t
ime
.
gmtime
(
tomorrow
)
self
.
toy
.
enrollment_start
=
t
omorrow
self
.
toy
.
enrollment_end
=
time
.
gmtime
(
nextday
)
self
.
toy
.
enrollment_end
=
nextday
# full course's has
# full course's has
self
.
full
.
enrollment_start
=
time
.
gmtime
(
yesterday
)
self
.
full
.
enrollment_start
=
yesterday
self
.
full
.
enrollment_end
=
t
ime
.
gmtime
(
tomorrow
)
self
.
full
.
enrollment_end
=
t
omorrow
print
"login"
print
"login"
# First, try with an enrolled student
# First, try with an enrolled student
...
@@ -778,12 +779,10 @@ class TestViewAuth(LoginEnrollmentTestCase):
...
@@ -778,12 +779,10 @@ class TestViewAuth(LoginEnrollmentTestCase):
self
.
assertFalse
(
settings
.
MITX_FEATURES
[
'DISABLE_START_DATES'
])
self
.
assertFalse
(
settings
.
MITX_FEATURES
[
'DISABLE_START_DATES'
])
# Make courses start in the future
# Make courses start in the future
tomorrow
=
time
.
time
()
+
24
*
3600
tomorrow
=
datetime
.
datetime
.
now
(
UTC
())
+
datetime
.
timedelta
(
days
=
1
)
# nextday = tomorrow + 24 * 3600
# yesterday = time.time() - 24 * 3600
# toy course's hasn't started
# toy course's hasn't started
self
.
toy
.
lms
.
start
=
t
ime
.
gmtime
(
tomorrow
)
self
.
toy
.
lms
.
start
=
t
omorrow
self
.
assertFalse
(
self
.
toy
.
has_started
())
self
.
assertFalse
(
self
.
toy
.
has_started
())
# but should be accessible for beta testers
# but should be accessible for beta testers
...
@@ -854,7 +853,7 @@ class TestSubmittingProblems(LoginEnrollmentTestCase):
...
@@ -854,7 +853,7 @@ class TestSubmittingProblems(LoginEnrollmentTestCase):
modx_url
=
self
.
modx_url
(
problem_location
,
'problem_check'
)
modx_url
=
self
.
modx_url
(
problem_location
,
'problem_check'
)
answer_key_prefix
=
'input_i4x-edX-{}-problem-{}_'
.
format
(
self
.
course_slug
,
problem_url_name
)
answer_key_prefix
=
'input_i4x-edX-{}-problem-{}_'
.
format
(
self
.
course_slug
,
problem_url_name
)
resp
=
self
.
client
.
post
(
modx_url
,
resp
=
self
.
client
.
post
(
modx_url
,
{
(
answer_key_prefix
+
k
):
v
for
k
,
v
in
responses
.
items
()
}
{
(
answer_key_prefix
+
k
):
v
for
k
,
v
in
responses
.
items
()
}
)
)
return
resp
return
resp
...
@@ -925,7 +924,7 @@ class TestCourseGrader(TestSubmittingProblems):
...
@@ -925,7 +924,7 @@ class TestCourseGrader(TestSubmittingProblems):
# Only get half of the first problem correct
# Only get half of the first problem correct
self
.
submit_question_answer
(
'H1P1'
,
{
'2_1'
:
'Correct'
,
'2_2'
:
'Incorrect'
})
self
.
submit_question_answer
(
'H1P1'
,
{
'2_1'
:
'Correct'
,
'2_2'
:
'Incorrect'
})
self
.
check_grade_percent
(
0.06
)
self
.
check_grade_percent
(
0.06
)
self
.
assertEqual
(
earned_hw_scores
(),
[
1.0
,
0
,
0
])
# Order matters
self
.
assertEqual
(
earned_hw_scores
(),
[
1.0
,
0
,
0
])
# Order matters
self
.
assertEqual
(
score_for_hw
(
'Homework1'
),
[
1.0
,
0.0
])
self
.
assertEqual
(
score_for_hw
(
'Homework1'
),
[
1.0
,
0.0
])
# Get both parts of the first problem correct
# Get both parts of the first problem correct
...
@@ -958,16 +957,16 @@ class TestCourseGrader(TestSubmittingProblems):
...
@@ -958,16 +957,16 @@ class TestCourseGrader(TestSubmittingProblems):
# Third homework
# Third homework
self
.
submit_question_answer
(
'H3P1'
,
{
'2_1'
:
'Correct'
,
'2_2'
:
'Correct'
})
self
.
submit_question_answer
(
'H3P1'
,
{
'2_1'
:
'Correct'
,
'2_2'
:
'Correct'
})
self
.
check_grade_percent
(
0.42
)
# Score didn't change
self
.
check_grade_percent
(
0.42
)
# Score didn't change
self
.
assertEqual
(
earned_hw_scores
(),
[
4.0
,
4.0
,
2.0
])
self
.
assertEqual
(
earned_hw_scores
(),
[
4.0
,
4.0
,
2.0
])
self
.
submit_question_answer
(
'H3P2'
,
{
'2_1'
:
'Correct'
,
'2_2'
:
'Correct'
})
self
.
submit_question_answer
(
'H3P2'
,
{
'2_1'
:
'Correct'
,
'2_2'
:
'Correct'
})
self
.
check_grade_percent
(
0.5
)
# Now homework2 dropped. Score changes
self
.
check_grade_percent
(
0.5
)
# Now homework2 dropped. Score changes
self
.
assertEqual
(
earned_hw_scores
(),
[
4.0
,
4.0
,
4.0
])
self
.
assertEqual
(
earned_hw_scores
(),
[
4.0
,
4.0
,
4.0
])
# Now we answer the final question (worth half of the grade)
# Now we answer the final question (worth half of the grade)
self
.
submit_question_answer
(
'FinalQuestion'
,
{
'2_1'
:
'Correct'
,
'2_2'
:
'Correct'
})
self
.
submit_question_answer
(
'FinalQuestion'
,
{
'2_1'
:
'Correct'
,
'2_2'
:
'Correct'
})
self
.
check_grade_percent
(
1.0
)
# Hooray! We got 100%
self
.
check_grade_percent
(
1.0
)
# Hooray! We got 100%
@override_settings
(
MODULESTORE
=
TEST_DATA_XML_MODULESTORE
)
@override_settings
(
MODULESTORE
=
TEST_DATA_XML_MODULESTORE
)
...
@@ -1000,7 +999,7 @@ class TestSchematicResponse(TestSubmittingProblems):
...
@@ -1000,7 +999,7 @@ class TestSchematicResponse(TestSubmittingProblems):
{
'2_1'
:
json
.
dumps
(
{
'2_1'
:
json
.
dumps
(
[[
'transient'
,
{
'Z'
:
[
[[
'transient'
,
{
'Z'
:
[
[
0.0000004
,
2.8
],
[
0.0000004
,
2.8
],
[
0.0000009
,
0.0
],
# wrong.
[
0.0000009
,
0.0
],
# wrong.
[
0.0000014
,
2.8
],
[
0.0000014
,
2.8
],
[
0.0000019
,
2.8
],
[
0.0000019
,
2.8
],
[
0.0000024
,
2.8
],
[
0.0000024
,
2.8
],
...
...
lms/djangoapps/django_comment_client/utils.py
View file @
d7194e6b
import
time
from
collections
import
defaultdict
from
collections
import
defaultdict
import
logging
import
logging
import
time
import
urllib
import
urllib
from
datetime
import
datetime
from
datetime
import
datetime
from
courseware.module_render
import
get_module
from
courseware.module_render
import
get_module
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.search
import
path_to_location
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.db
import
connection
from
django.db
import
connection
...
@@ -16,13 +11,12 @@ from django.http import HttpResponse
...
@@ -16,13 +11,12 @@ from django.http import HttpResponse
from
django.utils
import
simplejson
from
django.utils
import
simplejson
from
django_comment_common.models
import
Role
from
django_comment_common.models
import
Role
from
django_comment_client.permissions
import
check_permissions_by_view
from
django_comment_client.permissions
import
check_permissions_by_view
from
xmodule.modulestore.exceptions
import
NoPathToItem
from
mitxmako
import
middleware
from
mitxmako
import
middleware
import
pystache_custom
as
pystache
import
pystache_custom
as
pystache
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
from
django.utils.timezone
import
UTC
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -100,7 +94,7 @@ def get_discussion_category_map(course):
...
@@ -100,7 +94,7 @@ def get_discussion_category_map(course):
def
filter_unstarted_categories
(
category_map
):
def
filter_unstarted_categories
(
category_map
):
now
=
time
.
gmtime
(
)
now
=
datetime
.
now
(
UTC
()
)
result_map
=
{}
result_map
=
{}
...
@@ -220,7 +214,7 @@ def initialize_discussion_info(course):
...
@@ -220,7 +214,7 @@ def initialize_discussion_info(course):
for
topic
,
entry
in
course
.
discussion_topics
.
items
():
for
topic
,
entry
in
course
.
discussion_topics
.
items
():
category_map
[
'entries'
][
topic
]
=
{
"id"
:
entry
[
"id"
],
category_map
[
'entries'
][
topic
]
=
{
"id"
:
entry
[
"id"
],
"sort_key"
:
entry
.
get
(
"sort_key"
,
topic
),
"sort_key"
:
entry
.
get
(
"sort_key"
,
topic
),
"start_date"
:
time
.
gmtime
(
)}
"start_date"
:
datetime
.
now
(
UTC
()
)}
sort_map_entries
(
category_map
)
sort_map_entries
(
category_map
)
_DISCUSSIONINFO
[
course
.
id
][
'id_map'
]
=
discussion_id_map
_DISCUSSIONINFO
[
course
.
id
][
'id_map'
]
=
discussion_id_map
...
@@ -292,7 +286,7 @@ def get_ability(course_id, content, user):
...
@@ -292,7 +286,7 @@ def get_ability(course_id, content, user):
'can_vote'
:
check_permissions_by_view
(
user
,
course_id
,
content
,
"vote_for_thread"
if
content
[
'type'
]
==
'thread'
else
"vote_for_comment"
),
'can_vote'
:
check_permissions_by_view
(
user
,
course_id
,
content
,
"vote_for_thread"
if
content
[
'type'
]
==
'thread'
else
"vote_for_comment"
),
}
}
#TODO: RENAME
#
TODO: RENAME
def
get_annotated_content_info
(
course_id
,
content
,
user
,
user_info
):
def
get_annotated_content_info
(
course_id
,
content
,
user
,
user_info
):
...
@@ -310,7 +304,7 @@ def get_annotated_content_info(course_id, content, user, user_info):
...
@@ -310,7 +304,7 @@ def get_annotated_content_info(course_id, content, user, user_info):
'ability'
:
get_ability
(
course_id
,
content
,
user
),
'ability'
:
get_ability
(
course_id
,
content
,
user
),
}
}
#TODO: RENAME
#
TODO: RENAME
def
get_annotated_content_infos
(
course_id
,
thread
,
user
,
user_info
):
def
get_annotated_content_infos
(
course_id
,
thread
,
user
,
user_info
):
...
...
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