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
76339687
Commit
76339687
authored
Jun 18, 2013
by
Peter Fogg
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into peter-fogg/remove-video-xml
parents
a4032522
c5dc404b
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
89 additions
and
64 deletions
+89
-64
CHANGELOG.rst
+3
-0
cms/djangoapps/contentstore/tests/tests.py
+22
-0
cms/envs/dev.py
+1
-0
cms/templates/asset_index.html
+0
-1
common/lib/xmodule/xmodule/course_module.py
+10
-6
common/lib/xmodule/xmodule/fields.py
+26
-7
common/lib/xmodule/xmodule/tests/test_fields.py
+23
-0
common/lib/xmodule/xmodule/timeinfo.py
+3
-3
common/lib/xmodule/xmodule/timeparse.py
+0
-46
requirements/edx/github.txt
+1
-1
No files found.
CHANGELOG.rst
View file @
76339687
...
@@ -56,6 +56,9 @@ Blades: Staff debug info is now accessible for Graphical Slider Tool problems.
...
@@ -56,6 +56,9 @@ Blades: Staff debug info is now accessible for Graphical Slider Tool problems.
Blades: For Video Alpha the events ready, play, pause, seek, and speed change
Blades: For Video Alpha the events ready, play, pause, seek, and speed change
are logged on the server (in the logs).
are logged on the server (in the logs).
Common: all dates and times are not time zone aware datetimes. No code should create or use struct_times nor naive
datetimes.
Common: Developers can now have private Django settings files.
Common: Developers can now have private Django settings files.
Common: Safety code added to prevent anything above the vertical level in the
Common: Safety code added to prevent anything above the vertical level in the
...
...
cms/djangoapps/contentstore/tests/tests.py
View file @
76339687
...
@@ -3,6 +3,10 @@ from django.core.urlresolvers import reverse
...
@@ -3,6 +3,10 @@ from django.core.urlresolvers import reverse
from
.utils
import
parse_json
,
user
,
registration
from
.utils
import
parse_json
,
user
,
registration
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
contentstore.tests.test_course_settings
import
CourseTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
import
datetime
from
pytz
import
UTC
class
ContentStoreTestCase
(
ModuleStoreTestCase
):
class
ContentStoreTestCase
(
ModuleStoreTestCase
):
...
@@ -162,3 +166,21 @@ class AuthTestCase(ContentStoreTestCase):
...
@@ -162,3 +166,21 @@ class AuthTestCase(ContentStoreTestCase):
self
.
assertEqual
(
resp
.
status_code
,
302
)
self
.
assertEqual
(
resp
.
status_code
,
302
)
# Logged in should work.
# Logged in should work.
class
ForumTestCase
(
CourseTestCase
):
def
setUp
(
self
):
""" Creates the test course. """
super
(
ForumTestCase
,
self
)
.
setUp
()
self
.
course
=
CourseFactory
.
create
(
org
=
'testX'
,
number
=
'727'
,
display_name
=
'Forum Course'
)
def
test_blackouts
(
self
):
now
=
datetime
.
datetime
.
now
(
UTC
)
self
.
course
.
discussion_blackouts
=
[(
t
.
isoformat
(),
t2
.
isoformat
())
for
t
,
t2
in
[(
now
-
datetime
.
timedelta
(
days
=
14
),
now
-
datetime
.
timedelta
(
days
=
11
)),
(
now
+
datetime
.
timedelta
(
days
=
24
),
now
+
datetime
.
timedelta
(
days
=
30
))]]
self
.
assertTrue
(
self
.
course
.
forum_posts_allowed
)
self
.
course
.
discussion_blackouts
=
[(
t
.
isoformat
(),
t2
.
isoformat
())
for
t
,
t2
in
[(
now
-
datetime
.
timedelta
(
days
=
14
),
now
+
datetime
.
timedelta
(
days
=
2
)),
(
now
+
datetime
.
timedelta
(
days
=
24
),
now
+
datetime
.
timedelta
(
days
=
30
))]]
self
.
assertFalse
(
self
.
course
.
forum_posts_allowed
)
cms/envs/dev.py
View file @
76339687
...
@@ -172,6 +172,7 @@ MITX_FEATURES['ENABLE_SERVICE_STATUS'] = True
...
@@ -172,6 +172,7 @@ MITX_FEATURES['ENABLE_SERVICE_STATUS'] = True
# If there's an environment variable set, grab it and turn on Segment.io
# If there's an environment variable set, grab it and turn on Segment.io
# Note that this is the Studio key. There is a separate key for the LMS.
# Note that this is the Studio key. There is a separate key for the LMS.
import
os
SEGMENT_IO_KEY
=
os
.
environ
.
get
(
'SEGMENT_IO_KEY'
)
SEGMENT_IO_KEY
=
os
.
environ
.
get
(
'SEGMENT_IO_KEY'
)
if
SEGMENT_IO_KEY
:
if
SEGMENT_IO_KEY
:
MITX_FEATURES
[
'SEGMENT_IO'
]
=
True
MITX_FEATURES
[
'SEGMENT_IO'
]
=
True
...
...
cms/templates/asset_index.html
View file @
76339687
...
@@ -8,7 +8,6 @@
...
@@ -8,7 +8,6 @@
<
%
block
name=
"jsextra"
>
<
%
block
name=
"jsextra"
>
<script
src=
"${static.url('js/vendor/mustache.js')}"
></script>
<script
src=
"${static.url('js/vendor/mustache.js')}"
></script>
<script
type=
"text/javascript"
src=
"${static.url('js/views/assets.js')}"
></script>
<script
type=
'text/javascript'
>
<script
type=
'text/javascript'
>
// we just want a singleton
// we just want a singleton
...
...
common/lib/xmodule/xmodule/course_module.py
View file @
76339687
...
@@ -10,7 +10,6 @@ import dateutil.parser
...
@@ -10,7 +10,6 @@ import dateutil.parser
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.seq_module
import
SequenceDescriptor
,
SequenceModule
from
xmodule.seq_module
import
SequenceDescriptor
,
SequenceModule
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
import
json
import
json
...
@@ -645,8 +644,11 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
...
@@ -645,8 +644,11 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
def
start_date_text
(
self
):
def
start_date_text
(
self
):
def
try_parse_iso_8601
(
text
):
def
try_parse_iso_8601
(
text
):
try
:
try
:
result
=
datetime
.
strptime
(
text
,
"
%
Y-
%
m-
%
dT
%
H:
%
M"
)
result
=
Date
()
.
from_json
(
text
)
result
=
result
.
strftime
(
"
%
b
%
d,
%
Y"
)
if
result
is
None
:
result
=
text
.
title
()
else
:
result
=
result
.
strftime
(
"
%
b
%
d,
%
Y"
)
except
ValueError
:
except
ValueError
:
result
=
text
.
title
()
result
=
text
.
title
()
...
@@ -670,8 +672,10 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
...
@@ -670,8 +672,10 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
@property
@property
def
forum_posts_allowed
(
self
):
def
forum_posts_allowed
(
self
):
date_proxy
=
Date
()
try
:
try
:
blackout_periods
=
[(
parse_time
(
start
),
parse_time
(
end
))
blackout_periods
=
[(
date_proxy
.
from_json
(
start
),
date_proxy
.
from_json
(
end
))
for
start
,
end
for
start
,
end
in
self
.
discussion_blackouts
]
in
self
.
discussion_blackouts
]
now
=
datetime
.
now
(
UTC
())
now
=
datetime
.
now
(
UTC
())
...
@@ -701,7 +705,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
...
@@ -701,7 +705,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
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
self
.
registration_start_date
=
(
self
.
_try_parse_time
(
'Registration_Start_Date'
)
or
datetime
.
utcfromtimestamp
(
0
))
datetime
.
fromtimestamp
(
0
,
UTC
()
))
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
:
...
@@ -720,7 +724,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
...
@@ -720,7 +724,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
"""
"""
if
key
in
self
.
exam_info
:
if
key
in
self
.
exam_info
:
try
:
try
:
return
parse_time
(
self
.
exam_info
[
key
])
return
Date
()
.
from_json
(
self
.
exam_info
[
key
])
except
ValueError
as
e
:
except
ValueError
as
e
:
msg
=
"Exam {0} in course {1} loaded with a bad exam_info key '{2}': '{3}'"
.
format
(
self
.
exam_name
,
self
.
course_id
,
self
.
exam_info
[
key
],
e
)
msg
=
"Exam {0} in course {1} loaded with a bad exam_info key '{2}': '{3}'"
.
format
(
self
.
exam_name
,
self
.
course_id
,
self
.
exam_info
[
key
],
e
)
log
.
warning
(
msg
)
log
.
warning
(
msg
)
...
...
common/lib/xmodule/xmodule/fields.py
View file @
76339687
...
@@ -6,7 +6,7 @@ from xblock.core import ModelType
...
@@ -6,7 +6,7 @@ from xblock.core import ModelType
import
datetime
import
datetime
import
dateutil.parser
import
dateutil.parser
from
django.utils.timezone
import
UTC
from
pytz
import
UTC
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -15,6 +15,28 @@ class Date(ModelType):
...
@@ -15,6 +15,28 @@ class Date(ModelType):
'''
'''
Date fields know how to parse and produce json (iso) compatible formats. Converts to tz aware datetimes.
Date fields know how to parse and produce json (iso) compatible formats. Converts to tz aware datetimes.
'''
'''
# See note below about not defaulting these
CURRENT_YEAR
=
datetime
.
datetime
.
now
(
UTC
)
.
year
PREVENT_DEFAULT_DAY_MON_SEED1
=
datetime
.
datetime
(
CURRENT_YEAR
,
1
,
1
,
tzinfo
=
UTC
)
PREVENT_DEFAULT_DAY_MON_SEED2
=
datetime
.
datetime
(
CURRENT_YEAR
,
2
,
2
,
tzinfo
=
UTC
)
def
_parse_date_wo_default_month_day
(
self
,
field
):
"""
Parse the field as an iso string but prevent dateutils from defaulting the day or month while
allowing it to default the other fields.
"""
# It's not trivial to replace dateutil b/c parsing timezones as Z, +03:30, -400 is hard in python
# however, we don't want dateutil to default the month or day (but some tests at least expect
# us to default year); so, we'll see if dateutil uses the defaults for these the hard way
result
=
dateutil
.
parser
.
parse
(
field
,
default
=
self
.
PREVENT_DEFAULT_DAY_MON_SEED1
)
result_other
=
dateutil
.
parser
.
parse
(
field
,
default
=
self
.
PREVENT_DEFAULT_DAY_MON_SEED2
)
if
result
!=
result_other
:
log
.
warning
(
"Field {0} is missing month or day"
.
format
(
self
.
_name
,
field
))
return
None
if
result
.
tzinfo
is
None
:
result
=
result
.
replace
(
tzinfo
=
UTC
)
return
result
def
from_json
(
self
,
field
):
def
from_json
(
self
,
field
):
"""
"""
Parse an optional metadata key containing a time: if present, complain
Parse an optional metadata key containing a time: if present, complain
...
@@ -26,14 +48,11 @@ class Date(ModelType):
...
@@ -26,14 +48,11 @@ class Date(ModelType):
elif
field
is
""
:
elif
field
is
""
:
return
None
return
None
elif
isinstance
(
field
,
basestring
):
elif
isinstance
(
field
,
basestring
):
result
=
dateutil
.
parser
.
parse
(
field
)
return
self
.
_parse_date_wo_default_month_day
(
field
)
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
datetime
.
datetime
.
fromtimestamp
(
field
/
1000
,
UTC
()
)
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
()
)
return
datetime
.
datetime
.
fromtimestamp
(
time
.
mktime
(
field
),
UTC
)
elif
isinstance
(
field
,
datetime
.
datetime
):
elif
isinstance
(
field
,
datetime
.
datetime
):
return
field
return
field
else
:
else
:
...
...
common/lib/xmodule/xmodule/tests/test_fields.py
View file @
76339687
...
@@ -3,6 +3,8 @@ import datetime
...
@@ -3,6 +3,8 @@ import datetime
import
unittest
import
unittest
from
django.utils.timezone
import
UTC
from
django.utils.timezone
import
UTC
from
xmodule.fields
import
Date
,
Timedelta
from
xmodule.fields
import
Date
,
Timedelta
from
xmodule.timeinfo
import
TimeInfo
import
time
class
DateTest
(
unittest
.
TestCase
):
class
DateTest
(
unittest
.
TestCase
):
...
@@ -52,6 +54,18 @@ class DateTest(unittest.TestCase):
...
@@ -52,6 +54,18 @@ class DateTest(unittest.TestCase):
self
.
assertEqual
(
self
.
assertEqual
(
datetime
.
datetime
(
current
.
year
,
12
,
4
,
16
,
30
,
tzinfo
=
UTC
()),
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"
))
self
.
assertIsNone
(
DateTest
.
date
.
from_json
(
"12 12:00"
))
def
test_non_std_from_json
(
self
):
"""
Test the non-standard args being passed to from_json
"""
now
=
datetime
.
datetime
.
now
(
UTC
())
delta
=
now
-
datetime
.
datetime
.
fromtimestamp
(
0
,
UTC
())
self
.
assertEqual
(
DateTest
.
date
.
from_json
(
delta
.
total_seconds
()
*
1000
),
now
)
yesterday
=
datetime
.
datetime
.
now
(
UTC
())
-
datetime
.
timedelta
(
days
=-
1
)
self
.
assertEqual
(
DateTest
.
date
.
from_json
(
yesterday
),
yesterday
)
def
test_to_json
(
self
):
def
test_to_json
(
self
):
'''
'''
...
@@ -90,3 +104,12 @@ class TimedeltaTest(unittest.TestCase):
...
@@ -90,3 +104,12 @@ class TimedeltaTest(unittest.TestCase):
'1 days 46799 seconds'
,
'1 days 46799 seconds'
,
TimedeltaTest
.
delta
.
to_json
(
datetime
.
timedelta
(
days
=
1
,
hours
=
12
,
minutes
=
59
,
seconds
=
59
))
TimedeltaTest
.
delta
.
to_json
(
datetime
.
timedelta
(
days
=
1
,
hours
=
12
,
minutes
=
59
,
seconds
=
59
))
)
)
class
TimeInfoTest
(
unittest
.
TestCase
):
def
test_time_info
(
self
):
due_date
=
datetime
.
datetime
(
2000
,
4
,
14
,
10
,
tzinfo
=
UTC
())
grace_pd_string
=
'1 day 12 hours 59 minutes 59 seconds'
timeinfo
=
TimeInfo
(
due_date
,
grace_pd_string
)
self
.
assertEqual
(
timeinfo
.
close_date
,
due_date
+
Timedelta
()
.
from_json
(
grace_pd_string
))
common/lib/xmodule/xmodule/timeinfo.py
View file @
76339687
from
.timeparse
import
parse_timedelta
import
logging
import
logging
from
xmodule.fields
import
Timedelta
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
class
TimeInfo
(
object
):
class
TimeInfo
(
object
):
...
@@ -14,6 +13,7 @@ class TimeInfo(object):
...
@@ -14,6 +13,7 @@ class TimeInfo(object):
self.close_date - the real due date
self.close_date - the real due date
"""
"""
_delta_standin
=
Timedelta
()
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
=
due_date
self
.
display_due_date
=
due_date
...
@@ -23,7 +23,7 @@ class TimeInfo(object):
...
@@ -23,7 +23,7 @@ class TimeInfo(object):
if
grace_period_string
is
not
None
and
self
.
display_due_date
:
if
grace_period_string
is
not
None
and
self
.
display_due_date
:
try
:
try
:
self
.
grace_period
=
parse_timedelta
(
grace_period_string
)
self
.
grace_period
=
TimeInfo
.
_delta_standin
.
from_json
(
grace_period_string
)
self
.
close_date
=
self
.
display_due_date
+
self
.
grace_period
self
.
close_date
=
self
.
display_due_date
+
self
.
grace_period
except
:
except
:
log
.
error
(
"Error parsing the grace period {0}"
.
format
(
grace_period_string
))
log
.
error
(
"Error parsing the grace period {0}"
.
format
(
grace_period_string
))
...
...
common/lib/xmodule/xmodule/timeparse.py
deleted
100644 → 0
View file @
a4032522
"""
Helper functions for handling time in the format we like.
"""
import
re
from
datetime
import
timedelta
,
datetime
TIME_FORMAT
=
"
%
Y-
%
m-
%
dT
%
H:
%
M"
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)?)?$'
)
def
parse_time
(
time_str
):
"""
Takes a time string in TIME_FORMAT
Returns it as a time_struct.
Raises ValueError if the string is not in the right format.
"""
return
datetime
.
strptime
(
time_str
,
TIME_FORMAT
)
def
stringify_time
(
dt
):
"""
Convert a datetime struct to a string
"""
return
dt
.
isoformat
()
def
parse_timedelta
(
time_str
):
"""
time_str: A string with the following components:
<D> day[s] (optional)
<H> hour[s] (optional)
<M> minute[s] (optional)
<S> second[s] (optional)
Returns a datetime.timedelta parsed from the string
"""
parts
=
TIMEDELTA_REGEX
.
match
(
time_str
)
if
not
parts
:
return
parts
=
parts
.
groupdict
()
time_params
=
{}
for
(
name
,
param
)
in
parts
.
iteritems
():
if
param
:
time_params
[
name
]
=
int
(
param
)
return
timedelta
(
**
time_params
)
requirements/edx/github.txt
View file @
76339687
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
# Third-party:
# Third-party:
-e git://github.com/edx/django-staticfiles.git@6d2504e5c8#egg=django-staticfiles
-e git://github.com/edx/django-staticfiles.git@6d2504e5c8#egg=django-staticfiles
-e git://github.com/edx/django-pipeline.git#egg=django-pipeline
-e git://github.com/edx/django-pipeline.git#egg=django-pipeline
-e git://github.com/edx/django-wiki.git@
e2e84558
#egg=django-wiki
-e git://github.com/edx/django-wiki.git@
ac906abe
#egg=django-wiki
-e git://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev
-e git://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev
-e git://github.com/eventbrite/zendesk.git@d53fe0e81b623f084e91776bcf6369f8b7b63879#egg=zendesk
-e git://github.com/eventbrite/zendesk.git@d53fe0e81b623f084e91776bcf6369f8b7b63879#egg=zendesk
...
...
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