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
8ba41635
Commit
8ba41635
authored
Dec 07, 2012
by
Calen Pennington
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
WIP. Data loads, but not all of it
parent
3d6cbf47
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
297 additions
and
382 deletions
+297
-382
cms/djangoapps/contentstore/views.py
+1
-1
common/lib/capa/capa/capa_problem.py
+7
-11
common/lib/xmodule/xmodule/abtest_module.py
+2
-31
common/lib/xmodule/xmodule/capa_module.py
+59
-95
common/lib/xmodule/xmodule/course_module.py
+45
-37
common/lib/xmodule/xmodule/error_module.py
+3
-4
common/lib/xmodule/xmodule/mako_module.py
+4
-5
common/lib/xmodule/xmodule/model.py
+79
-0
common/lib/xmodule/xmodule/modulestore/xml.py
+13
-12
common/lib/xmodule/xmodule/template_module.py
+0
-4
common/lib/xmodule/xmodule/x_module.py
+53
-169
common/lib/xmodule/xmodule/xml_module.py
+10
-7
jenkins/base.sh
+13
-0
lms/djangoapps/courseware/courses.py
+5
-3
lms/djangoapps/courseware/module_render.py
+2
-2
rakefile
+1
-1
No files found.
cms/djangoapps/contentstore/views.py
View file @
8ba41635
...
...
@@ -499,7 +499,7 @@ def load_preview_module(request, preview_id, descriptor, instance_state, shared_
"""
system
=
preview_module_system
(
request
,
preview_id
,
descriptor
)
try
:
module
=
descriptor
.
xmodule
_constructor
(
system
)(
instance_state
,
shared_state
)
module
=
descriptor
.
xmodule
(
system
)
except
:
module
=
ErrorDescriptor
.
from_descriptor
(
descriptor
,
...
...
common/lib/capa/capa/capa_problem.py
View file @
8ba41635
...
...
@@ -83,7 +83,7 @@ class LoncapaProblem(object):
Main class for capa Problems.
'''
def
__init__
(
self
,
problem_text
,
id
,
stat
e
=
None
,
seed
=
None
,
system
=
None
):
def
__init__
(
self
,
problem_text
,
id
,
correct_map
=
None
,
don
e
=
None
,
seed
=
None
,
system
=
None
):
'''
Initializes capa Problem.
...
...
@@ -91,7 +91,8 @@ class LoncapaProblem(object):
- problem_text (string): xml defining the problem
- id (string): identifier for this problem; often a filename (no spaces)
- state (dict): student state
- correct_map (dict): data specifying whether the student has completed the problem
- done (bool): Whether the student has answered the problem
- seed (int): random number generator seed (int)
- system (ModuleSystem): ModuleSystem instance which provides OS,
rendering, and user context
...
...
@@ -103,16 +104,11 @@ class LoncapaProblem(object):
self
.
problem_id
=
id
self
.
system
=
system
self
.
seed
=
seed
self
.
done
=
done
self
.
correct_map
=
CorrectMap
()
if
state
:
if
'seed'
in
state
:
self
.
seed
=
state
[
'seed'
]
if
'student_answers'
in
state
:
self
.
student_answers
=
state
[
'student_answers'
]
if
'correct_map'
in
state
:
self
.
correct_map
.
set_dict
(
state
[
'correct_map'
])
if
'done'
in
state
:
self
.
done
=
state
[
'done'
]
if
correct_map
is
not
None
:
self
.
correct_map
.
set_dict
(
correct_map
)
# TODO: Does this deplete the Linux entropy pool? Is this fast enough?
if
not
self
.
seed
:
...
...
common/lib/xmodule/xmodule/abtest_module.py
View file @
8ba41635
...
...
@@ -7,6 +7,7 @@ from xmodule.x_module import XModule
from
xmodule.raw_module
import
RawDescriptor
from
xmodule.xml_module
import
XmlDescriptor
from
xmodule.exceptions
import
InvalidDefinitionError
from
.model
import
String
,
Scope
DEFAULT
=
"_DEFAULT_GROUP"
...
...
@@ -68,37 +69,7 @@ class ABTestDescriptor(RawDescriptor, XmlDescriptor):
template_dir_name
=
"abtest"
def
__init__
(
self
,
system
,
definition
=
None
,
**
kwargs
):
"""
definition is a dictionary with the following layout:
{'data': {
'experiment': 'the name of the experiment',
'group_portions': {
'group_a': 0.1,
'group_b': 0.2
},
'group_contents': {
'group_a': [
'url://for/content/module/1',
'url://for/content/module/2',
],
'group_b': [
'url://for/content/module/3',
],
DEFAULT: [
'url://for/default/content/1'
]
}
},
'children': [
'url://for/content/module/1',
'url://for/content/module/2',
'url://for/content/module/3',
'url://for/default/content/1',
]}
"""
kwargs
[
'shared_state_key'
]
=
definition
[
'data'
][
'experiment'
]
RawDescriptor
.
__init__
(
self
,
system
,
definition
,
**
kwargs
)
experiment
=
String
(
help
=
"Experiment that this A/B test belongs to"
,
scope
=
Scope
.
content
)
@classmethod
def
definition_from_xml
(
cls
,
xml_object
,
system
):
...
...
common/lib/xmodule/xmodule/capa_module.py
View file @
8ba41635
...
...
@@ -19,6 +19,9 @@ from progress import Progress
from
xmodule.x_module
import
XModule
from
xmodule.raw_module
import
RawDescriptor
from
xmodule.exceptions
import
NotFoundError
from
.model
import
Int
,
Scope
,
ModuleScope
,
ModelType
,
String
,
Boolean
,
Object
,
Float
Date
=
Timedelta
=
ModelType
log
=
logging
.
getLogger
(
"mitx.courseware"
)
...
...
@@ -77,6 +80,17 @@ class CapaModule(XModule):
'''
icon_class
=
'problem'
attempts
=
Int
(
help
=
"Number of attempts taken by the student on this problem"
,
default
=
0
,
scope
=
Scope
.
student_state
)
max_attempts
=
Int
(
help
=
"Maximum number of attempts that a student is allowed"
,
scope
=
Scope
.
settings
)
due
=
Date
(
help
=
"Date that this problem is due by"
,
scope
=
Scope
.
settings
)
graceperiod
=
Timedelta
(
help
=
"Amount of time after the due date that submissions will be accepted"
,
scope
=
Scope
.
settings
)
show_answer
=
String
(
help
=
"When to show the problem answer to the student"
,
scope
=
Scope
.
settings
,
default
=
"closed"
)
force_save_button
=
Boolean
(
help
=
"Whether to force the save button to appear on the page"
,
scope
=
Scope
.
settings
)
rerandomize
=
String
(
help
=
"When to rerandomize the problem"
,
default
=
"always"
)
data
=
String
(
help
=
"XML data for the problem"
,
scope
=
Scope
.
content
)
correct_map
=
Object
(
help
=
"Dictionary with the correctness of current student answers"
,
scope
=
Scope
.
student_state
)
done
=
Boolean
(
help
=
"Whether the student has answered the problem"
,
scope
=
Scope
.
student_state
)
js
=
{
'coffee'
:
[
resource_string
(
__name__
,
'js/src/capa/display.coffee'
),
resource_string
(
__name__
,
'js/src/collapsible.coffee'
),
resource_string
(
__name__
,
'js/src/javascript_loader.coffee'
),
...
...
@@ -87,51 +101,15 @@ class CapaModule(XModule):
js_module_name
=
"Problem"
css
=
{
'scss'
:
[
resource_string
(
__name__
,
'css/capa/display.scss'
)]}
def
__init__
(
self
,
system
,
location
,
definition
,
descriptor
,
instance_state
=
None
,
shared_state
=
None
,
**
kwargs
):
XModule
.
__init__
(
self
,
system
,
location
,
definition
,
descriptor
,
instance_state
,
shared_state
,
**
kwargs
)
self
.
attempts
=
0
self
.
max_attempts
=
None
dom2
=
etree
.
fromstring
(
definition
[
'data'
])
display_due_date_string
=
self
.
metadata
.
get
(
'due'
,
None
)
if
display_due_date_string
is
not
None
:
self
.
display_due_date
=
dateutil
.
parser
.
parse
(
display_due_date_string
)
#log.debug("Parsed " + display_due_date_string +
# " to " + str(self.display_due_date))
else
:
self
.
display_due_date
=
None
def
__init__
(
self
,
system
,
location
,
descriptor
,
model_data
):
XModule
.
__init__
(
self
,
system
,
location
,
descriptor
,
model_data
)
grace_period_string
=
self
.
metadata
.
get
(
'graceperiod'
,
None
)
if
grace_period_string
is
not
None
and
self
.
display_due_date
:
self
.
grace_period
=
parse_timedelta
(
grace_period_string
)
self
.
close_date
=
self
.
display_due_date
+
self
.
grace_period
if
self
.
graceperiod
is
not
None
and
self
.
due
:
self
.
close_date
=
self
.
due
+
self
.
graceperiod
#log.debug("Then parsed " + grace_period_string +
# " to closing date" + str(self.close_date))
else
:
self
.
grace_period
=
None
self
.
close_date
=
self
.
display_due_date
self
.
max_attempts
=
self
.
metadata
.
get
(
'attempts'
,
None
)
if
self
.
max_attempts
is
not
None
:
self
.
max_attempts
=
int
(
self
.
max_attempts
)
self
.
show_answer
=
self
.
metadata
.
get
(
'showanswer'
,
'closed'
)
self
.
force_save_button
=
self
.
metadata
.
get
(
'force_save_button'
,
'false'
)
if
self
.
show_answer
==
""
:
self
.
show_answer
=
"closed"
if
instance_state
is
not
None
:
instance_state
=
json
.
loads
(
instance_state
)
if
instance_state
is
not
None
and
'attempts'
in
instance_state
:
self
.
attempts
=
instance_state
[
'attempts'
]
self
.
name
=
only_one
(
dom2
.
xpath
(
'/problem/@name'
))
self
.
close_date
=
self
.
due
if
self
.
rerandomize
==
'never'
:
self
.
seed
=
1
...
...
@@ -148,8 +126,8 @@ class CapaModule(XModule):
try
:
# TODO (vshnayder): move as much as possible of this work and error
# checking to descriptor load time
self
.
lcp
=
LoncapaProblem
(
self
.
d
efinition
[
'data'
]
,
self
.
location
.
html_id
(),
instance_state
,
seed
=
self
.
seed
,
system
=
self
.
system
)
self
.
lcp
=
LoncapaProblem
(
self
.
d
ata
,
self
.
location
.
html_id
(),
self
.
correct_map
,
self
.
done
,
self
.
seed
,
self
.
system
)
except
Exception
as
err
:
msg
=
'cannot create LoncapaProblem {loc}: {err}'
.
format
(
loc
=
self
.
location
.
url
(),
err
=
err
)
...
...
@@ -168,33 +146,21 @@ class CapaModule(XModule):
(
self
.
location
.
url
(),
msg
))
self
.
lcp
=
LoncapaProblem
(
problem_text
,
self
.
location
.
html_id
(),
instance_state
,
seed
=
self
.
seed
,
system
=
self
.
system
)
self
.
correct_map
,
self
.
done
,
self
.
seed
,
self
.
system
)
else
:
# add extra info and raise
raise
Exception
(
msg
),
None
,
sys
.
exc_info
()[
2
]
@property
def
rerandomize
(
self
):
"""
Property accessor that returns self.metadata['rerandomize'] in a
canonical form
"""
rerandomize
=
self
.
metadata
.
get
(
'rerandomize'
,
'always'
)
if
rerandomize
in
(
""
,
"always"
,
"true"
):
return
"always"
elif
rerandomize
in
(
"false"
,
"per_student"
):
return
"per_student"
elif
rerandomize
==
"never"
:
return
"never"
elif
rerandomize
==
"onreset"
:
return
"onreset"
else
:
raise
Exception
(
"Invalid rerandomize attribute "
+
rerandomize
)
if
self
.
rerandomize
in
(
""
,
"true"
):
self
.
rerandomize
=
"always"
elif
self
.
rerandomize
==
"false"
:
self
.
rerandomize
=
"per_student"
def
get_instance_state
(
self
):
state
=
self
.
lcp
.
get_state
()
state
[
'attempts'
]
=
self
.
attempts
return
json
.
dumps
(
state
)
def
sync_lcp_state
(
self
):
lcp_state
=
self
.
lcp
.
get_state
()
self
.
done
=
lcp_state
[
'done'
]
self
.
correct_map
=
lcp_state
[
'correct_map'
]
self
.
seed
=
lcp_state
[
'seed'
]
def
get_score
(
self
):
return
self
.
lcp
.
get_score
()
...
...
@@ -211,7 +177,7 @@ class CapaModule(XModule):
if
total
>
0
:
try
:
return
Progress
(
score
,
total
)
except
Exception
as
err
:
except
Exception
:
log
.
exception
(
"Got bad progress"
)
return
None
return
None
...
...
@@ -261,8 +227,8 @@ class CapaModule(XModule):
# Next, generate a fresh LoncapaProblem
self
.
lcp
=
LoncapaProblem
(
self
.
definition
[
'data'
],
self
.
location
.
html_id
(),
state
=
None
,
# Tabula rasa
seed
=
self
.
seed
,
system
=
self
.
system
)
self
.
sync_lcp_state
()
# Prepend a scary warning to the student
warning
=
'<div class="capa_reset">'
\
...
...
@@ -280,8 +246,8 @@ class CapaModule(XModule):
html
=
warning
try
:
html
+=
self
.
lcp
.
get_html
()
except
Exception
,
err
:
# Couldn't do it. Give up
log
.
exception
(
err
)
except
Exception
:
# Couldn't do it. Give up
log
.
exception
(
"Unable to generate html from LoncapaProblem"
)
raise
content
=
{
'name'
:
self
.
display_name
,
...
...
@@ -311,7 +277,7 @@ class CapaModule(XModule):
# User submitted a problem, and hasn't reset. We don't want
# more submissions.
if
self
.
lcp
.
done
and
self
.
rerandomize
==
"always"
:
if
self
.
done
and
self
.
rerandomize
==
"always"
:
check_button
=
False
save_button
=
False
...
...
@@ -320,7 +286,7 @@ class CapaModule(XModule):
reset_button
=
False
# User hasn't submitted an answer yet -- we don't want resets
if
not
self
.
lcp
.
done
:
if
not
self
.
done
:
reset_button
=
False
# We may not need a "save" button if infinite number of attempts and
...
...
@@ -406,7 +372,7 @@ class CapaModule(XModule):
return
self
.
attempts
>
0
if
self
.
show_answer
==
'answered'
:
return
self
.
lcp
.
done
return
self
.
done
if
self
.
show_answer
==
'closed'
:
return
self
.
closed
()
...
...
@@ -429,6 +395,7 @@ class CapaModule(XModule):
queuekey
=
get
[
'queuekey'
]
score_msg
=
get
[
'xqueue_body'
]
self
.
lcp
.
update_score
(
score_msg
,
queuekey
)
self
.
sync_lcp_state
()
return
dict
()
# No AJAX return is needed
...
...
@@ -445,8 +412,9 @@ class CapaModule(XModule):
raise
NotFoundError
(
'Answer is not available'
)
else
:
answers
=
self
.
lcp
.
get_question_answers
()
self
.
sync_lcp_state
()
# answers (eg <solution>) may have embedded images
# answers (eg <solution>) may have embedded images
# but be careful, some problems are using non-string answer dicts
new_answers
=
dict
()
for
answer_id
in
answers
:
...
...
@@ -512,7 +480,7 @@ class CapaModule(XModule):
raise
NotFoundError
(
'Problem is closed'
)
# Problem submitted. Student should reset before checking again
if
self
.
lcp
.
done
and
self
.
rerandomize
==
"always"
:
if
self
.
done
and
self
.
rerandomize
==
"always"
:
event_info
[
'failure'
]
=
'unreset'
self
.
system
.
track_function
(
'save_problem_check_fail'
,
event_info
)
raise
NotFoundError
(
'Problem must be reset before it can be checked again'
)
...
...
@@ -522,14 +490,13 @@ class CapaModule(XModule):
current_time
=
datetime
.
datetime
.
now
()
prev_submit_time
=
self
.
lcp
.
get_recentmost_queuetime
()
waittime_between_requests
=
self
.
system
.
xqueue
[
'waittime'
]
if
(
current_time
-
prev_submit_time
)
.
total_seconds
()
<
waittime_between_requests
:
if
(
current_time
-
prev_submit_time
)
.
total_seconds
()
<
waittime_between_requests
:
msg
=
'You must wait at least
%
d seconds between submissions'
%
waittime_between_requests
return
{
'success'
:
msg
,
'html'
:
''
}
# Prompts a modal dialog in ajax callback
return
{
'success'
:
msg
,
'html'
:
''
}
# Prompts a modal dialog in ajax callback
try
:
old_state
=
self
.
lcp
.
get_state
()
lcp_id
=
self
.
lcp
.
problem_id
correct_map
=
self
.
lcp
.
grade_answers
(
answers
)
self
.
sync_lcp_state
()
except
StudentInputError
as
inst
:
log
.
exception
(
"StudentInputError in capa_module:problem_check"
)
return
{
'success'
:
inst
.
message
}
...
...
@@ -554,11 +521,11 @@ class CapaModule(XModule):
# 'success' will always be incorrect
event_info
[
'correct_map'
]
=
correct_map
.
get_dict
()
event_info
[
'success'
]
=
success
event_info
[
'attempts'
]
=
self
.
attempts
event_info
[
'attempts'
]
=
self
.
attempts
self
.
system
.
track_function
(
'save_problem_check'
,
event_info
)
if
hasattr
(
self
.
system
,
'psychometrics_handler'
):
# update PsychometricsData using callback
self
.
system
.
psychometrics_handler
(
self
.
get_instance_state
())
if
hasattr
(
self
.
system
,
'psychometrics_handler'
):
# update PsychometricsData using callback
self
.
system
.
psychometrics_handler
(
self
.
get_instance_state
())
# render problem into HTML
html
=
self
.
get_problem_html
(
encapsulate
=
False
)
...
...
@@ -589,7 +556,7 @@ class CapaModule(XModule):
# Problem submitted. Student should reset before saving
# again.
if
self
.
lcp
.
done
and
self
.
rerandomize
==
"always"
:
if
self
.
done
and
self
.
rerandomize
==
"always"
:
event_info
[
'failure'
]
=
'done'
self
.
system
.
track_function
(
'save_problem_fail'
,
event_info
)
return
{
'success'
:
False
,
...
...
@@ -617,7 +584,7 @@ class CapaModule(XModule):
return
{
'success'
:
False
,
'error'
:
"Problem is closed"
}
if
not
self
.
lcp
.
done
:
if
not
self
.
done
:
event_info
[
'failure'
]
=
'not_done'
self
.
system
.
track_function
(
'reset_problem_fail'
,
event_info
)
return
{
'success'
:
False
,
...
...
@@ -629,9 +596,13 @@ class CapaModule(XModule):
# in next line)
self
.
lcp
.
seed
=
None
self
.
lcp
=
LoncapaProblem
(
self
.
definition
[
'data'
],
self
.
location
.
html_id
(),
self
.
lcp
.
get_state
(),
system
=
self
.
system
)
self
.
lcp
=
LoncapaProblem
(
self
.
data
,
self
.
location
.
html_id
(),
self
.
lcp
.
correct_map
,
self
.
lcp
.
done
,
self
.
lcp
.
seed
,
self
.
system
)
self
.
sync_lcp_state
()
event_info
[
'new_state'
]
=
self
.
lcp
.
get_state
()
self
.
system
.
track_function
(
'reset_problem'
,
event_info
)
...
...
@@ -647,6 +618,8 @@ class CapaDescriptor(RawDescriptor):
module_class
=
CapaModule
weight
=
Float
(
help
=
"How much to weight this problem by"
,
scope
=
Scope
.
settings
)
stores_state
=
True
has_score
=
True
template_dir_name
=
'problem'
...
...
@@ -665,12 +638,3 @@ class CapaDescriptor(RawDescriptor):
'problems/'
+
path
[
8
:],
path
[
8
:],
]
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
CapaDescriptor
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
weight_string
=
self
.
metadata
.
get
(
'weight'
,
None
)
if
weight_string
:
self
.
weight
=
float
(
weight_string
)
else
:
self
.
weight
=
None
common/lib/xmodule/xmodule/course_module.py
View file @
8ba41635
...
...
@@ -12,6 +12,9 @@ import requests
import
time
import
copy
from
.model
import
Scope
,
ModelType
,
List
,
String
,
Object
,
Boolean
Date
=
ModelType
log
=
logging
.
getLogger
(
__name__
)
...
...
@@ -21,6 +24,39 @@ edx_xml_parser = etree.XMLParser(dtd_validation=False, load_dtd=False,
class
CourseDescriptor
(
SequenceDescriptor
):
module_class
=
SequenceModule
textbooks
=
List
(
help
=
"List of pairs of (title, url) for textbooks used in this course"
,
default
=
[],
scope
=
Scope
.
content
)
wiki_slug
=
String
(
help
=
"Slug that points to the wiki for this course"
,
scope
=
Scope
.
content
)
enrollment_start
=
Date
(
help
=
"Date that enrollment for this class is opened"
,
scope
=
Scope
.
settings
)
enrollment_end
=
Date
(
help
=
"Date that enrollment for this class is closed"
,
scope
=
Scope
.
settings
)
end
=
Date
(
help
=
"Date that this class ends"
,
scope
=
Scope
.
settings
)
advertised_start
=
Date
(
help
=
"Date that this course is advertised to start"
,
scope
=
Scope
.
settings
)
grading_policy
=
Object
(
help
=
"Grading policy definition for this class"
,
scope
=
Scope
.
content
)
info_sidebar_name
=
String
(
scope
=
Scope
.
settings
,
default
=
'Course Handouts'
)
# An extra property is used rather than the wiki_slug/number because
# there are courses that change the number for different runs. This allows
# courses to share the same css_class across runs even if they have
# different numbers.
#
# TODO get rid of this as soon as possible or potentially build in a robust
# way to add in course-specific styling. There needs to be a discussion
# about the right way to do this, but arjun will address this ASAP. Also
# note that the courseware template needs to change when this is removed.
css_class
=
String
(
help
=
"DO NOT USE THIS"
,
scope
=
Scope
.
settings
)
# TODO: This is a quick kludge to allow CS50 (and other courses) to
# specify their own discussion forums as external links by specifying a
# "discussion_link" in their policy JSON file. This should later get
# folded in with Syllabus, Course Info, and additional Custom tabs in a
# more sensible framework later.
discussion_link
=
String
(
help
=
"DO NOT USE THIS"
,
scope
=
Scope
.
settings
)
# TODO: same as above, intended to let internal CS50 hide the progress tab
# until we get grade integration set up.
# Explicit comparison to True because we always want to return a bool.
hide_progress_tab
=
Boolean
(
help
=
"DO NOT USE THIS"
,
scope
=
Scope
.
settings
)
template_dir_name
=
'course'
class
Textbook
:
...
...
@@ -69,10 +105,11 @@ class CourseDescriptor(SequenceDescriptor):
return
table_of_contents
def
__init__
(
self
,
system
,
definition
=
None
,
**
kwargs
):
super
(
CourseDescriptor
,
self
)
.
__init__
(
system
,
definition
,
**
kwargs
)
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
CourseDescriptor
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
textbooks
=
[]
for
title
,
book_url
in
self
.
definition
[
'data'
][
'textbooks'
]
:
for
title
,
book_url
in
self
.
textbooks
:
try
:
self
.
textbooks
.
append
(
self
.
Textbook
(
title
,
book_url
))
except
:
...
...
@@ -81,7 +118,8 @@ class CourseDescriptor(SequenceDescriptor):
log
.
exception
(
"Couldn't load textbook ({0}, {1})"
.
format
(
title
,
book_url
))
continue
self
.
wiki_slug
=
self
.
definition
[
'data'
][
'wiki_slug'
]
or
self
.
location
.
course
if
self
.
wiki_slug
is
None
:
self
.
wiki_slug
=
self
.
location
.
course
msg
=
None
if
self
.
start
is
None
:
...
...
@@ -98,7 +136,7 @@ class CourseDescriptor(SequenceDescriptor):
# disable the syllabus content for courses that do not provide a syllabus
self
.
syllabus_present
=
self
.
system
.
resources_fs
.
exists
(
path
(
'syllabus'
))
self
.
set_grading_policy
(
self
.
definition
[
'data'
]
.
get
(
'grading_policy'
,
None
)
)
self
.
set_grading_policy
(
self
.
grading_policy
)
def
defaut_grading_policy
(
self
):
"""
...
...
@@ -203,7 +241,7 @@ class CourseDescriptor(SequenceDescriptor):
# cdodge: import the grading policy information that is on disk and put into the
# descriptor 'definition' bucket as a dictionary so that it is persisted in the DB
instance
.
definition
[
'data'
][
'grading_policy'
]
=
policy
instance
.
grading_policy
=
policy
# now set the current instance. set_grading_policy() will apply some inheritance rules
instance
.
set_grading_policy
(
policy
)
...
...
@@ -395,38 +433,14 @@ class CourseDescriptor(SequenceDescriptor):
@property
def
start_date_text
(
self
):
displayed_start
=
self
.
_try_parse_time
(
'advertised_start'
)
or
self
.
start
return
time
.
strftime
(
"
%
b
%
d,
%
Y"
,
displayed_start
)
return
time
.
strftime
(
"
%
b
%
d,
%
Y"
,
self
.
advertised_start
or
self
.
start
)
@property
def
end_date_text
(
self
):
return
time
.
strftime
(
"
%
b
%
d,
%
Y"
,
self
.
end
)
# An extra property is used rather than the wiki_slug/number because
# there are courses that change the number for different runs. This allows
# courses to share the same css_class across runs even if they have
# different numbers.
#
# TODO get rid of this as soon as possible or potentially build in a robust
# way to add in course-specific styling. There needs to be a discussion
# about the right way to do this, but arjun will address this ASAP. Also
# note that the courseware template needs to change when this is removed.
@property
def
css_class
(
self
):
return
self
.
metadata
.
get
(
'css_class'
,
''
)
@property
def
info_sidebar_name
(
self
):
return
self
.
metadata
.
get
(
'info_sidebar_name'
,
'Course Handouts'
)
@property
def
discussion_link
(
self
):
"""TODO: This is a quick kludge to allow CS50 (and other courses) to
specify their own discussion forums as external links by specifying a
"discussion_link" in their policy JSON file. This should later get
folded in with Syllabus, Course Info, and additional Custom tabs in a
more sensible framework later."""
return
self
.
metadata
.
get
(
'discussion_link'
,
None
)
@property
def
forum_posts_allowed
(
self
):
...
...
@@ -443,12 +457,6 @@ class CourseDescriptor(SequenceDescriptor):
return
True
@property
def
hide_progress_tab
(
self
):
"""TODO: same as above, intended to let internal CS50 hide the progress tab
until we get grade integration set up."""
# Explicit comparison to True because we always want to return a bool.
return
self
.
metadata
.
get
(
'hide_progress_tab'
)
==
True
@property
def
end_of_course_survey_url
(
self
):
...
...
common/lib/xmodule/xmodule/error_module.py
View file @
8ba41635
...
...
@@ -74,12 +74,11 @@ class ErrorDescriptor(JSONEditingDescriptor):
}
# real metadata stays in the content, but add a display name
m
eta
data
=
{
'display_name'
:
'Error: '
+
location
.
name
}
m
odel_
data
=
{
'display_name'
:
'Error: '
+
location
.
name
}
return
ErrorDescriptor
(
system
,
definition
,
location
=
location
,
metadata
=
metadata
location
,
model_data
,
)
def
get_context
(
self
):
...
...
common/lib/xmodule/xmodule/mako_module.py
View file @
8ba41635
...
...
@@ -21,20 +21,19 @@ class MakoModuleDescriptor(XModuleDescriptor):
the descriptor as the `module` parameter to that template
"""
def
__init__
(
self
,
system
,
definition
=
None
,
**
kwargs
):
def
__init__
(
self
,
system
,
location
,
model_data
):
if
getattr
(
system
,
'render_template'
,
None
)
is
None
:
raise
TypeError
(
'{system} must have a render_template function'
' in order to use a MakoDescriptor'
.
format
(
system
=
system
))
super
(
MakoModuleDescriptor
,
self
)
.
__init__
(
system
,
definition
,
**
kwargs
)
super
(
MakoModuleDescriptor
,
self
)
.
__init__
(
system
,
location
,
model_data
)
def
get_context
(
self
):
"""
Return the context to render the mako template with
"""
return
{
'module'
:
self
,
'metadata'
:
self
.
metadata
,
'editable_metadata_fields'
:
self
.
editable_metadata_fields
'editable_metadata_fields'
:
self
.
editable_fields
}
def
get_html
(
self
):
...
...
@@ -44,6 +43,6 @@ class MakoModuleDescriptor(XModuleDescriptor):
# cdodge: encapsulate a means to expose "editable" metadata fields (i.e. not internal system metadata)
@property
def
editable_metadata_fields
(
self
):
subset
=
[
name
for
name
in
self
.
metadata
.
keys
()
if
name
not
in
self
.
system_metadata_fields
]
subset
=
[
field
.
name
for
field
in
self
.
fields
if
field
.
name
not
in
self
.
system_metadata_fields
]
return
subset
common/lib/xmodule/xmodule/model.py
0 → 100644
View file @
8ba41635
from
collections
import
namedtuple
class
ModuleScope
(
object
):
USAGE
,
DEFINITION
,
TYPE
,
ALL
=
xrange
(
4
)
class
Scope
(
namedtuple
(
'ScopeBase'
,
'student module'
)):
pass
Scope
.
content
=
Scope
(
student
=
False
,
module
=
ModuleScope
.
DEFINITION
)
Scope
.
student_state
=
Scope
(
student
=
True
,
module
=
ModuleScope
.
USAGE
)
Scope
.
settings
=
Scope
(
student
=
True
,
module
=
ModuleScope
.
USAGE
)
Scope
.
student_preferences
=
Scope
(
student
=
True
,
module
=
ModuleScope
.
TYPE
)
Scope
.
student_info
=
Scope
(
student
=
True
,
module
=
ModuleScope
.
ALL
)
class
ModelType
(
object
):
sequence
=
0
def
__init__
(
self
,
help
=
None
,
default
=
None
,
scope
=
Scope
.
content
):
self
.
_seq
=
self
.
sequence
self
.
_name
=
"unknown"
self
.
help
=
help
self
.
default
=
default
self
.
scope
=
scope
ModelType
.
sequence
+=
1
@property
def
name
(
self
):
return
self
.
_name
def
__get__
(
self
,
instance
,
owner
):
if
instance
is
None
:
return
self
return
instance
.
_model_data
.
get
(
self
.
name
,
self
.
default
)
def
__set__
(
self
,
instance
,
value
):
instance
.
_model_data
[
self
.
name
]
=
value
def
__delete__
(
self
,
instance
):
del
instance
.
_model_data
[
self
.
name
]
def
__repr__
(
self
):
return
"<{0.__class__.__name} {0.__name__}>"
.
format
(
self
)
def
__lt__
(
self
,
other
):
return
self
.
_seq
<
other
.
_seq
Int
=
Float
=
Boolean
=
Object
=
List
=
String
=
Any
=
ModelType
class
ModelMetaclass
(
type
):
def
__new__
(
cls
,
name
,
bases
,
attrs
):
# Find registered methods
reg_methods
=
{}
for
value
in
attrs
.
itervalues
():
for
reg_type
,
names
in
getattr
(
value
,
"_method_registrations"
,
{})
.
iteritems
():
for
n
in
names
:
reg_methods
[
reg_type
+
n
]
=
value
attrs
[
'registered_methods'
]
=
reg_methods
if
attrs
.
get
(
'has_children'
,
False
):
attrs
[
'children'
]
=
ModelType
(
help
=
'The children of this XModule'
,
default
=
[],
scope
=
None
)
@property
def
child_map
(
self
):
return
dict
((
child
.
name
,
child
)
for
child
in
self
.
children
)
attrs
[
'child_map'
]
=
child_map
fields
=
[]
for
n
,
v
in
attrs
.
items
():
if
isinstance
(
v
,
ModelType
):
v
.
_name
=
n
fields
.
append
(
v
)
fields
.
sort
()
attrs
[
'fields'
]
=
fields
return
super
(
ModelMetaclass
,
cls
)
.
__new__
(
cls
,
name
,
bases
,
attrs
)
common/lib/xmodule/xmodule/modulestore/xml.py
View file @
8ba41635
...
...
@@ -187,12 +187,13 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
err_msg
)
descriptor
.
metadata
[
'data_dir'
]
=
course_dir
setattr
(
descriptor
,
'data_dir'
,
course_dir
)
xmlstore
.
modules
[
course_id
][
descriptor
.
location
]
=
descriptor
for
child
in
descriptor
.
get_children
():
parent_tracker
.
add_parent
(
child
.
location
,
descriptor
.
location
)
if
hasattr
(
descriptor
,
'children'
):
for
child
in
descriptor
.
children
:
parent_tracker
.
add_parent
(
child
.
location
,
descriptor
.
location
)
return
descriptor
render_template
=
lambda
:
''
...
...
@@ -425,14 +426,14 @@ class XMLModuleStore(ModuleStoreBase):
# breaks metadata inheritance via get_children(). Instead
# (actually, in addition to, for now), we do a final inheritance pass
# after we have the course descriptor.
XModuleDescriptor
.
compute_inherited_metadata
(
course_descriptor
)
#
XModuleDescriptor.compute_inherited_metadata(course_descriptor)
# now import all pieces of course_info which is expected to be stored
# in <content_dir>/info or <content_dir>/info/<url_name>
self
.
load_extra_content
(
system
,
course_descriptor
,
'course_info'
,
self
.
data_dir
/
course_dir
/
'info'
,
course_dir
,
url_name
)
# now import all static tabs which are expected to be stored in
# in <content_dir>/tabs or <content_dir>/tabs/<url_name>
# in <content_dir>/tabs or <content_dir>/tabs/<url_name>
self
.
load_extra_content
(
system
,
course_descriptor
,
'static_tab'
,
self
.
data_dir
/
course_dir
/
'tabs'
,
course_dir
,
url_name
)
self
.
load_extra_content
(
system
,
course_descriptor
,
'custom_tag_template'
,
self
.
data_dir
/
course_dir
/
'custom_tags'
,
course_dir
,
url_name
)
...
...
@@ -444,30 +445,30 @@ class XMLModuleStore(ModuleStoreBase):
def
load_extra_content
(
self
,
system
,
course_descriptor
,
category
,
base_dir
,
course_dir
,
url_name
):
if
url_name
:
path
=
base_dir
/
url_name
path
=
base_dir
/
url_name
if
not
os
.
path
.
exists
(
path
):
path
=
base_dir
for
filepath
in
glob
.
glob
(
path
/
'*'
):
for
filepath
in
glob
.
glob
(
path
/
'*'
):
with
open
(
filepath
)
as
f
:
try
:
html
=
f
.
read
()
.
decode
(
'utf-8'
)
# tabs are referenced in policy.json through a 'slug' which is just the filename without the .html suffix
slug
=
os
.
path
.
splitext
(
os
.
path
.
basename
(
filepath
))[
0
]
loc
=
Location
(
'i4x'
,
course_descriptor
.
location
.
org
,
course_descriptor
.
location
.
course
,
category
,
slug
)
module
=
HtmlDescriptor
(
system
,
definition
=
{
'data'
:
html
},
**
{
'location'
:
loc
})
module
=
HtmlDescriptor
(
system
,
loc
,
{
'data'
:
html
})
# VS[compat]:
# Hack because we need to pull in the 'display_name' for static tabs (because we need to edit them)
# from the course policy
if
category
==
"static_tab"
:
for
tab
in
course_descriptor
.
tabs
or
[]:
if
tab
.
get
(
'url_slug'
)
==
slug
:
module
.
metadata
[
'display_name'
]
=
tab
[
'name'
]
module
.
metadata
[
'data_dir'
]
=
course_dir
self
.
modules
[
course_descriptor
.
id
][
module
.
location
]
=
module
module
.
display_name
=
tab
[
'name'
]
module
.
data_dir
=
course_dir
self
.
modules
[
course_descriptor
.
id
][
module
.
location
]
=
module
except
Exception
,
e
:
logging
.
exception
(
"Failed to load {0}. Skipping... Exception: {1}"
.
format
(
filepath
,
str
(
e
)))
logging
.
exception
(
"Failed to load {0}. Skipping... Exception: {1}"
.
format
(
filepath
,
str
(
e
)))
system
.
error_tracker
(
"ERROR: "
+
str
(
e
))
def
get_instance
(
self
,
course_id
,
location
,
depth
=
0
):
...
...
common/lib/xmodule/xmodule/template_module.py
View file @
8ba41635
...
...
@@ -67,10 +67,6 @@ class CustomTagDescriptor(RawDescriptor):
return
template
.
render
(
**
params
)
def
__init__
(
self
,
system
,
definition
,
**
kwargs
):
'''Render and save the template for this descriptor instance'''
super
(
CustomTagDescriptor
,
self
)
.
__init__
(
system
,
definition
,
**
kwargs
)
@property
def
rendered_html
(
self
):
return
self
.
render_template
(
self
.
system
,
self
.
definition
[
'data'
])
...
...
common/lib/xmodule/xmodule/x_module.py
View file @
8ba41635
...
...
@@ -2,19 +2,41 @@ import logging
import
pkg_resources
import
yaml
import
os
import
time
from
functools
import
partial
from
lxml
import
etree
from
pprint
import
pprint
from
collections
import
namedtuple
from
pkg_resources
import
resource_listdir
,
resource_string
,
resource_isdir
from
xmodule.modulestore
import
Location
from
xmodule.timeparse
import
parse_time
,
stringify_tim
e
from
.model
import
ModelMetaclass
,
String
,
Scope
,
ModuleScope
,
ModelTyp
e
from
xmodule.contentstore.content
import
StaticContent
,
XASSET_SRCREF_PREFIX
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
import
time
Date
=
ModelType
class
Date
(
ModelType
):
time_format
=
"
%
Y-
%
m-
%
dT
%
H:
%
M"
def
from_json
(
self
,
value
):
"""
Parse an optional metadata key containing a time: if present, complain
if it doesn't parse.
Return None if not present or invalid.
"""
try
:
return
time
.
strptime
(
value
,
self
.
time_format
)
except
ValueError
as
e
:
msg
=
"Field {0} has bad value '{1}': '{2}'"
.
format
(
self
.
_name
,
value
,
e
)
log
.
warning
(
msg
)
return
None
def
to_json
(
self
,
value
):
"""
Convert a time struct to a string
"""
return
time
.
strftime
(
self
.
time_format
,
value
)
log
=
logging
.
getLogger
(
'mitx.'
+
__name__
)
...
...
@@ -157,6 +179,10 @@ class XModule(HTMLSnippet):
See the HTML module for a simple example.
'''
__metaclass__
=
ModelMetaclass
display_name
=
String
(
help
=
"Display name for this module"
,
scope
=
Scope
(
student
=
False
,
module
=
ModuleScope
.
USAGE
))
# The default implementation of get_icon_class returns the icon_class
# attribute of the class
#
...
...
@@ -165,8 +191,7 @@ class XModule(HTMLSnippet):
# in the module
icon_class
=
'other'
def
__init__
(
self
,
system
,
location
,
definition
,
descriptor
,
instance_state
=
None
,
shared_state
=
None
,
**
kwargs
):
def
__init__
(
self
,
system
,
location
,
descriptor
,
model_data
):
'''
Construct a new xmodule
...
...
@@ -214,63 +239,25 @@ class XModule(HTMLSnippet):
'''
self
.
system
=
system
self
.
location
=
Location
(
location
)
self
.
definition
=
definition
self
.
descriptor
=
descriptor
self
.
instance_state
=
instance_state
self
.
shared_state
=
shared_state
self
.
id
=
self
.
location
.
url
()
self
.
url_name
=
self
.
location
.
name
self
.
category
=
self
.
location
.
category
self
.
metadata
=
kwargs
.
get
(
'metadata'
,
{})
self
.
_loaded_children
=
None
self
.
_model_data
=
model_data
@property
def
display_name
(
self
):
'''
Return a display name for the module: use display_name if defined in
metadata, otherwise convert the url name.
'''
return
self
.
metadata
.
get
(
'display_name'
,
self
.
url_name
.
replace
(
'_'
,
' '
))
if
self
.
display_name
is
None
:
self
.
display_name
=
self
.
url_name
.
replace
(
'_'
,
' '
)
def
__unicode__
(
self
):
return
'<x_module(id={0})>'
.
format
(
self
.
id
)
def
get_children
(
self
):
'''
Return module instances for all the children of this module.
'''
if
self
.
_loaded_children
is
None
:
child_locations
=
self
.
get_children_locations
()
children
=
[
self
.
system
.
get_module
(
loc
)
for
loc
in
child_locations
]
# get_module returns None if the current user doesn't have access
# to the location.
self
.
_loaded_children
=
[
c
for
c
in
children
if
c
is
not
None
]
return
self
.
_loaded_children
def
get_children_locations
(
self
):
'''
Returns the locations of each of child modules.
Overriding this changes the behavior of get_children and
anything that uses get_children, such as get_display_items.
This method will not instantiate the modules of the children
unless absolutely necessary, so it is cheaper to call than get_children
These children will be the same children returned by the
descriptor unless descriptor.has_dynamic_children() is true.
'''
return
self
.
definition
.
get
(
'children'
,
[])
def
get_display_items
(
self
):
'''
Returns a list of descendent module instances that will display
immediately inside this module.
'''
items
=
[]
for
child
in
self
.
get_
children
():
for
child
in
self
.
children
():
items
.
extend
(
child
.
displayable_items
())
return
items
...
...
@@ -290,18 +277,6 @@ class XModule(HTMLSnippet):
### Functions used in the LMS
def
get_instance_state
(
self
):
''' State of the object, as stored in the database
'''
return
'{}'
def
get_shared_state
(
self
):
'''
Get state that should be shared with other instances
using the same 'shared_state_key' attribute.
'''
return
'{}'
def
get_score
(
self
):
''' Score the student received on the problem.
'''
...
...
@@ -391,7 +366,10 @@ class XModuleDescriptor(Plugin, HTMLSnippet, ResourceTemplates):
"""
entry_point
=
"xmodule.v1"
module_class
=
XModule
__metaclass__
=
ModelMetaclass
display_name
=
String
(
help
=
"Display name for this module"
,
scope
=
Scope
(
student
=
False
,
module
=
ModuleScope
.
USAGE
))
start
=
Date
(
help
=
"Start time when this module is visible"
,
scope
=
Scope
(
student
=
False
,
module
=
ModuleScope
.
USAGE
))
# Attributes for inspection of the descriptor
stores_state
=
False
# Indicates whether the xmodule state should be
# stored in a database (independent of shared state)
...
...
@@ -424,8 +402,8 @@ class XModuleDescriptor(Plugin, HTMLSnippet, ResourceTemplates):
# ============================= STRUCTURAL MANIPULATION ===================
def
__init__
(
self
,
system
,
definition
=
None
,
**
kwargs
):
location
,
model_data
):
"""
Construct a new XModuleDescriptor. The only required arguments are the
system, used for interaction with external resources, and the
...
...
@@ -467,116 +445,36 @@ class XModuleDescriptor(Plugin, HTMLSnippet, ResourceTemplates):
instance of the module data
"""
self
.
system
=
system
self
.
metadata
=
kwargs
.
get
(
'metadata'
,
{})
self
.
definition
=
definition
if
definition
is
not
None
else
{}
self
.
location
=
Location
(
kwargs
.
get
(
'location'
))
self
.
location
=
Location
(
location
)
self
.
url_name
=
self
.
location
.
name
self
.
category
=
self
.
location
.
category
self
.
shared_state_key
=
kwargs
.
get
(
'shared_state_key'
)
self
.
_model_data
=
model_data
self
.
_child_instances
=
None
self
.
_inherited_metadata
=
set
()
@property
def
display_name
(
self
):
'''
Return a display name for the module: use display_name if defined in
metadata, otherwise convert the url name.
'''
return
self
.
metadata
.
get
(
'display_name'
,
self
.
url_name
.
replace
(
'_'
,
' '
))
@property
def
start
(
self
):
"""
If self.metadata contains start, return it. Else return None.
"""
if
'start'
not
in
self
.
metadata
:
return
None
return
self
.
_try_parse_time
(
'start'
)
@start.setter
def
start
(
self
,
value
):
if
isinstance
(
value
,
time
.
struct_time
):
self
.
metadata
[
'start'
]
=
stringify_time
(
value
)
@property
def
own_metadata
(
self
):
"""
Return the metadata that is not inherited, but was defined on this module.
"""
return
dict
((
k
,
v
)
for
k
,
v
in
self
.
metadata
.
items
()
if
k
not
in
self
.
_inherited_metadata
)
@staticmethod
def
compute_inherited_metadata
(
node
):
"""Given a descriptor, traverse all of its descendants and do metadata
inheritance. Should be called on a CourseDescriptor after importing a
course.
NOTE: This means that there is no such thing as lazy loading at the
moment--this accesses all the children."""
for
c
in
node
.
get_children
():
c
.
inherit_metadata
(
node
.
metadata
)
XModuleDescriptor
.
compute_inherited_metadata
(
c
)
def
inherit_metadata
(
self
,
metadata
):
"""
Updates this module with metadata inherited from a containing module.
Only metadata specified in self.inheritable_metadata will
be inherited
"""
# Set all inheritable metadata from kwargs that are
# in self.inheritable_metadata and aren't already set in metadata
for
attr
in
self
.
inheritable_metadata
:
if
attr
not
in
self
.
metadata
and
attr
in
metadata
:
self
.
_inherited_metadata
.
add
(
attr
)
self
.
metadata
[
attr
]
=
metadata
[
attr
]
def
get_children
(
self
):
"""Returns a list of XModuleDescriptor instances for the children of
this module"""
if
self
.
_child_instances
is
None
:
self
.
_child_instances
=
[]
for
child_loc
in
self
.
definition
.
get
(
'children'
,
[]):
try
:
child
=
self
.
system
.
load_item
(
child_loc
)
except
ItemNotFoundError
:
log
.
exception
(
'Unable to load item {loc}, skipping'
.
format
(
loc
=
child_loc
))
continue
# TODO (vshnayder): this should go away once we have
# proper inheritance support in mongo. The xml
# datastore does all inheritance on course load.
child
.
inherit_metadata
(
self
.
metadata
)
self
.
_child_instances
.
append
(
child
)
return
self
.
_child_instances
def
get_child_by_url_name
(
self
,
url_name
):
"""
Return a child XModuleDescriptor with the specified url_name, if it exists, and None otherwise.
"""
for
c
in
self
.
get_children
()
:
for
c
in
self
.
children
:
if
c
.
url_name
==
url_name
:
return
c
return
None
def
xmodule
_constructor
(
self
,
system
):
def
xmodule
(
self
,
system
):
"""
Returns a constructor for an XModule. This constructor takes two
arguments: instance_state and shared_state, and returns a fully
instantiated XModule
"""
return
partial
(
self
.
module_class
,
return
self
.
module_class
(
system
,
self
.
location
,
self
.
definition
,
self
,
metadata
=
self
.
metadata
system
.
xmodule_model_data
(
self
.
model_data
),
)
def
has_dynamic_children
(
self
):
"""
Returns True if this descriptor has dynamic children for a given
...
...
@@ -701,31 +599,14 @@ class XModuleDescriptor(Plugin, HTMLSnippet, ResourceTemplates):
return
eq
def
__repr__
(
self
):
return
(
"{class_}({system!r},
{definition!r},
location={location!r},"
" m
etadata={meta
data!r})"
.
format
(
return
(
"{class_}({system!r}, location={location!r},"
" m
odel_data={model_
data!r})"
.
format
(
class_
=
self
.
__class__
.
__name__
,
system
=
self
.
system
,
definition
=
self
.
definition
,
location
=
self
.
location
,
m
etadata
=
self
.
metadata
m
odel_data
=
self
.
_model_data
,
))
# ================================ Internal helpers =======================
def
_try_parse_time
(
self
,
key
):
"""
Parse an optional metadata key containing a time: if present, complain
if it doesn't parse.
Return None if not present or invalid.
"""
if
key
in
self
.
metadata
:
try
:
return
parse_time
(
self
.
metadata
[
key
])
except
ValueError
as
e
:
msg
=
"Descriptor {0} loaded with a bad metadata key '{1}': '{2}'"
.
format
(
self
.
location
.
url
(),
self
.
metadata
[
key
],
e
)
log
.
warning
(
msg
)
return
None
class
DescriptorSystem
(
object
):
...
...
@@ -867,6 +748,9 @@ class ModuleSystem(object):
'''provide uniform access to attributes (like etree)'''
self
.
__dict__
[
attr
]
=
val
def
xmodule_module_data
(
self
,
module_data
):
return
module_data
def
__repr__
(
self
):
return
repr
(
self
.
__dict__
)
...
...
common/lib/xmodule/xmodule/xml_module.py
View file @
8ba41635
...
...
@@ -287,9 +287,9 @@ class XmlDescriptor(XModuleDescriptor):
filepath
=
cls
.
_format_filepath
(
xml_object
.
tag
,
name_to_pathname
(
url_name
))
definition_xml
=
cls
.
load_file
(
filepath
,
system
.
resources_fs
,
location
)
else
:
definition_xml
=
xml_object
# this is just a pointer, not the real definition content
definition_xml
=
xml_object
# this is just a pointer, not the real definition content
definition
=
cls
.
load_definition
(
definition_xml
,
system
,
location
)
# note this removes metadata
definition
=
cls
.
load_definition
(
definition_xml
,
system
,
location
)
# note this removes metadata
# VS[compat] -- make Ike's github preview links work in both old and
# new file layouts
if
is_pointer_tag
(
xml_object
):
...
...
@@ -299,13 +299,13 @@ class XmlDescriptor(XModuleDescriptor):
metadata
=
cls
.
load_metadata
(
definition_xml
)
# move definition metadata into dict
dmdata
=
definition
.
get
(
'definition_metadata'
,
''
)
dmdata
=
definition
.
get
(
'definition_metadata'
,
''
)
if
dmdata
:
metadata
[
'definition_metadata_raw'
]
=
dmdata
try
:
metadata
.
update
(
json
.
loads
(
dmdata
))
except
Exception
as
err
:
log
.
debug
(
'Error
%
s in loading metadata
%
s'
%
(
err
,
dmdata
))
log
.
debug
(
'Error
%
s in loading metadata
%
s'
%
(
err
,
dmdata
))
metadata
[
'definition_metadata_err'
]
=
str
(
err
)
# Set/override any metadata specified by policy
...
...
@@ -313,11 +313,14 @@ class XmlDescriptor(XModuleDescriptor):
if
k
in
system
.
policy
:
cls
.
apply_policy
(
metadata
,
system
.
policy
[
k
])
model_data
=
{}
model_data
.
update
(
metadata
)
model_data
.
update
(
definition
)
return
cls
(
system
,
definition
,
location
=
location
,
metadata
=
metadata
,
location
,
model_data
,
)
@classmethod
...
...
jenkins/base.sh
0 → 100644
View file @
8ba41635
function
github_status
{
gcli status create mitx mitx
$GIT_COMMIT
\
--params
=
$1
\
target_url:
$BUILD_URL
\
description:
"Build #
$BUILD_NUMBER
is running"
\
-f
csv
}
function
github_mark_failed_on_exit
{
trap
'[ $? == "0" ] || github_status state:failed'
EXIT
}
\ No newline at end of file
lms/djangoapps/courseware/courses.py
View file @
8ba41635
...
...
@@ -85,7 +85,7 @@ def course_image_url(course):
"""Try to look up the image url for the course. If it's not found,
log an error and return the dead link"""
if
isinstance
(
modulestore
(),
XMLModuleStore
):
path
=
course
.
metadata
[
'data_dir'
]
+
"/images/course_image.jpg"
path
=
course
.
data_dir
+
"/images/course_image.jpg"
return
try_staticfiles_lookup
(
path
)
else
:
loc
=
course
.
location
.
_replace
(
tag
=
'c4x'
,
category
=
'asset'
,
name
=
'images_course_image.jpg'
)
...
...
@@ -162,7 +162,9 @@ def get_course_about_section(course, section_key):
key
=
section_key
,
url
=
course
.
location
.
url
()))
return
None
elif
section_key
==
"title"
:
return
course
.
metadata
.
get
(
'display_name'
,
course
.
url_name
)
if
course
.
display_name
is
None
:
return
course
.
url_name
return
course
.
display_name
elif
section_key
==
"university"
:
return
course
.
location
.
org
elif
section_key
==
"number"
:
...
...
@@ -220,7 +222,7 @@ def get_course_syllabus_section(course, section_key):
filepath
=
find_file
(
fs
,
dirs
,
section_key
+
".html"
)
with
fs
.
open
(
filepath
)
as
htmlFile
:
return
replace_urls
(
htmlFile
.
read
()
.
decode
(
'utf-8'
),
course
.
metadata
[
'data_dir'
]
,
course_namespace
=
course
.
location
)
course
.
data_dir
,
course_namespace
=
course
.
location
)
except
ResourceNotFoundError
:
log
.
exception
(
"Missing syllabus section {key} in course {url}"
.
format
(
key
=
section_key
,
url
=
course
.
location
.
url
()))
...
...
lms/djangoapps/courseware/module_render.py
View file @
8ba41635
...
...
@@ -245,7 +245,7 @@ def _get_module(user, request, location, student_module_cache, course_id, positi
make_psychometrics_data_update_handler
(
instance_module
))
try
:
module
=
descriptor
.
xmodule
_constructor
(
system
)(
instance_state
,
shared_state
)
module
=
descriptor
.
xmodule
(
system
)
except
:
log
.
exception
(
"Error creating module from descriptor {0}"
.
format
(
descriptor
))
...
...
@@ -259,7 +259,7 @@ def _get_module(user, request, location, student_module_cache, course_id, positi
error_msg
=
exc_info_to_str
(
sys
.
exc_info
()))
# Make an error module
return
err_descriptor
.
xmodule
_constructor
(
system
)(
None
,
None
)
return
err_descriptor
.
xmodule
(
system
)
_get_html
=
module
.
get_html
...
...
rakefile
View file @
8ba41635
...
...
@@ -40,7 +40,7 @@ end
def
django_admin
(
system
,
env
,
command
,
*
args
)
django_admin
=
ENV
[
'DJANGO_ADMIN_PATH'
]
||
select_executable
(
'django-admin.py'
,
'django-admin'
)
return
"
#{
django_admin
}
#{
command
}
--settings=
#{
system
}
.envs.
#{
env
}
--pythonpath=.
#{
args
.
join
(
' '
)
}
"
return
"
#{
django_admin
}
#{
command
}
--
traceback --
settings=
#{
system
}
.envs.
#{
env
}
--pythonpath=.
#{
args
.
join
(
' '
)
}
"
end
def
django_for_jasmine
(
system
,
django_reload
)
...
...
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