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
bd822b9d
Commit
bd822b9d
authored
Feb 13, 2013
by
Calen Pennington
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix tests post-merge
parent
793bbfd3
Show whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
82 additions
and
96 deletions
+82
-96
common/djangoapps/student/views.py
+2
-6
common/lib/xmodule/xmodule/combined_open_ended_module.py
+1
-1
common/lib/xmodule/xmodule/combined_open_ended_modulev1.py
+1
-1
common/lib/xmodule/xmodule/conditional_module.py
+6
-3
common/lib/xmodule/xmodule/course_module.py
+15
-27
common/lib/xmodule/xmodule/gst_module.py
+1
-1
common/lib/xmodule/xmodule/modulestore/xml.py
+3
-0
common/lib/xmodule/xmodule/modulestore/xml_importer.py
+2
-5
common/lib/xmodule/xmodule/open_ended_module.py
+1
-2
common/lib/xmodule/xmodule/peer_grading_module.py
+11
-18
common/lib/xmodule/xmodule/self_assessment_module.py
+4
-4
common/lib/xmodule/xmodule/tests/test_course_module.py
+7
-7
lms/djangoapps/courseware/access.py
+3
-3
lms/djangoapps/courseware/courses.py
+1
-1
lms/djangoapps/courseware/module_render.py
+5
-5
lms/djangoapps/courseware/tabs.py
+5
-2
lms/djangoapps/courseware/tests/tests.py
+2
-2
lms/djangoapps/courseware/views.py
+5
-1
lms/djangoapps/open_ended_grading/controller_query_service.py
+1
-1
lms/djangoapps/open_ended_grading/tests.py
+3
-3
lms/templates/course.html
+1
-1
lms/templates/courseware/progress.html
+2
-2
No files found.
common/djangoapps/student/views.py
View file @
bd822b9d
...
@@ -44,9 +44,8 @@ from collections import namedtuple
...
@@ -44,9 +44,8 @@ from collections import namedtuple
from
courseware.courses
import
get_courses
,
sort_by_announcement
from
courseware.courses
import
get_courses
,
sort_by_announcement
from
courseware.access
import
has_access
from
courseware.access
import
has_access
from
courseware.models
import
StudentModuleCache
from
courseware.views
import
get_module_for_descriptor
,
jump_to
from
courseware.views
import
get_module_for_descriptor
,
jump_to
from
courseware.mod
ule_render
import
get_instance_modul
e
from
courseware.mod
el_data
import
ModelDataCach
e
from
statsd
import
statsd
from
statsd
import
statsd
...
@@ -1150,7 +1149,7 @@ def test_center_login(request):
...
@@ -1150,7 +1149,7 @@ def test_center_login(request):
log
.
error
(
"cand {} on exam {} for course {}: descriptor not found for location {}"
.
format
(
client_candidate_id
,
exam_series_code
,
course_id
,
location
))
log
.
error
(
"cand {} on exam {} for course {}: descriptor not found for location {}"
.
format
(
client_candidate_id
,
exam_series_code
,
course_id
,
location
))
return
HttpResponseRedirect
(
makeErrorURL
(
error_url
,
"missingClientProgram"
));
return
HttpResponseRedirect
(
makeErrorURL
(
error_url
,
"missingClientProgram"
));
timelimit_module_cache
=
StudentModuleCache
.
cache_for_descriptor_descendents
(
course_id
,
testcenteruser
.
user
,
timelimit_module_cache
=
ModelDataCache
.
cache_for_descriptor_descendents
(
course_id
,
testcenteruser
.
user
,
timelimit_descriptor
,
depth
=
None
)
timelimit_descriptor
,
depth
=
None
)
timelimit_module
=
get_module_for_descriptor
(
request
.
user
,
request
,
timelimit_descriptor
,
timelimit_module
=
get_module_for_descriptor
(
request
.
user
,
request
,
timelimit_descriptor
,
timelimit_module_cache
,
course_id
,
position
=
None
)
timelimit_module_cache
,
course_id
,
position
=
None
)
...
@@ -1177,9 +1176,6 @@ def test_center_login(request):
...
@@ -1177,9 +1176,6 @@ def test_center_login(request):
if
time_accommodation_code
:
if
time_accommodation_code
:
timelimit_module
.
accommodation_code
=
time_accommodation_code
timelimit_module
.
accommodation_code
=
time_accommodation_code
instance_module
=
get_instance_module
(
course_id
,
testcenteruser
.
user
,
timelimit_module
,
timelimit_module_cache
)
instance_module
.
state
=
timelimit_module
.
get_instance_state
()
instance_module
.
save
()
log
.
info
(
"cand {} on exam {} for course {}: receiving accommodation {}"
.
format
(
client_candidate_id
,
exam_series_code
,
course_id
,
time_accommodation_code
))
log
.
info
(
"cand {} on exam {} for course {}: receiving accommodation {}"
.
format
(
client_candidate_id
,
exam_series_code
,
course_id
,
time_accommodation_code
))
# UGLY HACK!!!
# UGLY HACK!!!
...
...
common/lib/xmodule/xmodule/combined_open_ended_module.py
View file @
bd822b9d
...
@@ -190,7 +190,7 @@ class CombinedOpenEndedDescriptor(XmlDescriptor, EditingDescriptor):
...
@@ -190,7 +190,7 @@ class CombinedOpenEndedDescriptor(XmlDescriptor, EditingDescriptor):
}
}
"""
"""
return
{
'xml_string'
:
etree
.
tostring
(
xml_object
),
'metadata'
:
xml_object
.
attrib
}
return
{
'xml_string'
:
etree
.
tostring
(
xml_object
),
'metadata'
:
xml_object
.
attrib
}
,
[]
def
definition_to_xml
(
self
,
resource_fs
):
def
definition_to_xml
(
self
,
resource_fs
):
...
...
common/lib/xmodule/xmodule/combined_open_ended_modulev1.py
View file @
bd822b9d
...
@@ -696,7 +696,7 @@ class CombinedOpenEndedV1Descriptor(XmlDescriptor, EditingDescriptor):
...
@@ -696,7 +696,7 @@ class CombinedOpenEndedV1Descriptor(XmlDescriptor, EditingDescriptor):
"""Assumes that xml_object has child k"""
"""Assumes that xml_object has child k"""
return
xml_object
.
xpath
(
k
)[
0
]
return
xml_object
.
xpath
(
k
)[
0
]
return
{
'task_xml'
:
parse_task
(
'task'
),
'prompt'
:
parse
(
'prompt'
),
'rubric'
:
parse
(
'rubric'
)}
return
{
'task_xml'
:
parse_task
(
'task'
),
'prompt'
:
parse
(
'prompt'
),
'rubric'
:
parse
(
'rubric'
)}
,
[]
def
definition_to_xml
(
self
,
resource_fs
):
def
definition_to_xml
(
self
,
resource_fs
):
...
...
common/lib/xmodule/xmodule/conditional_module.py
View file @
bd822b9d
...
@@ -4,6 +4,7 @@ import logging
...
@@ -4,6 +4,7 @@ import logging
from
xmodule.x_module
import
XModule
from
xmodule.x_module
import
XModule
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.seq_module
import
SequenceDescriptor
from
xmodule.seq_module
import
SequenceDescriptor
from
xblock.core
import
String
,
Scope
from
pkg_resources
import
resource_string
from
pkg_resources
import
resource_string
...
@@ -34,6 +35,7 @@ class ConditionalModule(XModule):
...
@@ -34,6 +35,7 @@ class ConditionalModule(XModule):
js_module_name
=
"Conditional"
js_module_name
=
"Conditional"
css
=
{
'scss'
:
[
resource_string
(
__name__
,
'css/capa/display.scss'
)]}
css
=
{
'scss'
:
[
resource_string
(
__name__
,
'css/capa/display.scss'
)]}
condition
=
String
(
help
=
"Condition for this module"
,
default
=
''
,
scope
=
Scope
.
settings
)
def
__init__
(
self
,
system
,
location
,
definition
,
descriptor
,
instance_state
=
None
,
shared_state
=
None
,
**
kwargs
):
def
__init__
(
self
,
system
,
location
,
definition
,
descriptor
,
instance_state
=
None
,
shared_state
=
None
,
**
kwargs
):
"""
"""
...
@@ -44,7 +46,6 @@ class ConditionalModule(XModule):
...
@@ -44,7 +46,6 @@ class ConditionalModule(XModule):
"""
"""
XModule
.
__init__
(
self
,
system
,
location
,
definition
,
descriptor
,
instance_state
,
shared_state
,
**
kwargs
)
XModule
.
__init__
(
self
,
system
,
location
,
definition
,
descriptor
,
instance_state
,
shared_state
,
**
kwargs
)
self
.
contents
=
None
self
.
contents
=
None
self
.
condition
=
self
.
metadata
.
get
(
'condition'
,
''
)
self
.
_get_required_modules
()
self
.
_get_required_modules
()
children
=
self
.
get_display_items
()
children
=
self
.
get_display_items
()
if
children
:
if
children
:
...
@@ -128,16 +129,18 @@ class ConditionalDescriptor(SequenceDescriptor):
...
@@ -128,16 +129,18 @@ class ConditionalDescriptor(SequenceDescriptor):
stores_state
=
True
stores_state
=
True
has_score
=
False
has_score
=
False
required
=
String
(
help
=
"List of required xmodule locations, separated by &"
,
default
=
''
,
scope
=
Scope
.
settings
)
def
__init__
(
self
,
*
args
,
**
kwargs
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
ConditionalDescriptor
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
super
(
ConditionalDescriptor
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
required_module_list
=
[
tuple
(
x
.
split
(
'/'
,
1
))
for
x
in
self
.
metadata
.
get
(
'required'
,
''
)
.
split
(
'&'
)]
required_module_list
=
[
tuple
(
x
.
split
(
'/'
,
1
))
for
x
in
self
.
required
.
split
(
'&'
)]
self
.
required_module_locations
=
[]
self
.
required_module_locations
=
[]
for
rm
in
required_module_list
:
for
rm
in
required_module_list
:
try
:
try
:
(
tag
,
name
)
=
rm
(
tag
,
name
)
=
rm
except
Exception
as
err
:
except
Exception
as
err
:
msg
=
"Specification of required module in conditional is broken:
%
s"
%
self
.
metadata
.
get
(
'required'
)
msg
=
"Specification of required module in conditional is broken:
%
s"
%
self
.
required
log
.
warning
(
msg
)
log
.
warning
(
msg
)
self
.
system
.
error_tracker
(
msg
)
self
.
system
.
error_tracker
(
msg
)
continue
continue
...
...
common/lib/xmodule/xmodule/course_module.py
View file @
bd822b9d
...
@@ -169,6 +169,11 @@ class CourseDescriptor(SequenceDescriptor):
...
@@ -169,6 +169,11 @@ class CourseDescriptor(SequenceDescriptor):
computed_default
=
lambda
c
:
{
'General'
:
{
'id'
:
c
.
location
.
html_id
()}},
computed_default
=
lambda
c
:
{
'General'
:
{
'id'
:
c
.
location
.
html_id
()}},
)
)
testcenter_info
=
Object
(
help
=
"Dictionary of Test Center info"
,
scope
=
Scope
.
settings
)
testcenter_info
=
Object
(
help
=
"Dictionary of Test Center info"
,
scope
=
Scope
.
settings
)
announcement
=
Date
(
help
=
"Date this course is announced"
,
scope
=
Scope
.
settings
)
cohort_config
=
Object
(
help
=
"Dictionary defining cohort configuration"
,
scope
=
Scope
.
settings
)
is_new
=
Boolean
(
help
=
"Whether this course should be flagged as new"
,
scope
=
Scope
.
settings
)
no_grade
=
Boolean
(
help
=
"True if this course isn't graded"
,
default
=
False
,
scope
=
Scope
.
settings
)
disable_progress_graph
=
Boolean
(
help
=
"True if this course shouldn't display the progress graph"
,
default
=
False
,
scope
=
Scope
.
settings
)
has_children
=
True
has_children
=
True
info_sidebar_name
=
String
(
scope
=
Scope
.
settings
,
default
=
'Course Handouts'
)
info_sidebar_name
=
String
(
scope
=
Scope
.
settings
,
default
=
'Course Handouts'
)
...
@@ -410,26 +415,11 @@ class CourseDescriptor(SequenceDescriptor):
...
@@ -410,26 +415,11 @@ class CourseDescriptor(SequenceDescriptor):
return
min
(
self
.
_grading_policy
[
'GRADE_CUTOFFS'
]
.
values
())
return
min
(
self
.
_grading_policy
[
'GRADE_CUTOFFS'
]
.
values
())
@property
@property
def
tabs
(
self
):
"""
Return the tabs config, as a python object, or None if not specified.
"""
return
self
.
metadata
.
get
(
'tabs'
)
@tabs.setter
def
tabs
(
self
,
value
):
self
.
metadata
[
'tabs'
]
=
value
@property
def
show_calculator
(
self
):
return
self
.
metadata
.
get
(
"show_calculator"
,
None
)
==
"Yes"
@property
def
is_cohorted
(
self
):
def
is_cohorted
(
self
):
"""
"""
Return whether the course is cohorted.
Return whether the course is cohorted.
"""
"""
config
=
self
.
metadata
.
get
(
"cohort_config"
)
config
=
self
.
cohort_config
if
config
is
None
:
if
config
is
None
:
return
False
return
False
...
@@ -440,7 +430,7 @@ class CourseDescriptor(SequenceDescriptor):
...
@@ -440,7 +430,7 @@ class CourseDescriptor(SequenceDescriptor):
"""
"""
Return list of topic ids defined in course policy.
Return list of topic ids defined in course policy.
"""
"""
topics
=
self
.
metadata
.
get
(
"discussion_topics"
,
{})
topics
=
self
.
discussion_topics
return
[
d
[
"id"
]
for
d
in
topics
.
values
()]
return
[
d
[
"id"
]
for
d
in
topics
.
values
()]
...
@@ -451,7 +441,7 @@ class CourseDescriptor(SequenceDescriptor):
...
@@ -451,7 +441,7 @@ class CourseDescriptor(SequenceDescriptor):
the empty set. Note that all inline discussions are automatically
the empty set. Note that all inline discussions are automatically
cohorted based on the course's is_cohorted setting.
cohorted based on the course's is_cohorted setting.
"""
"""
config
=
self
.
metadata
.
get
(
"cohort_config"
)
config
=
self
.
cohort_config
if
config
is
None
:
if
config
is
None
:
return
set
()
return
set
()
...
@@ -460,13 +450,13 @@ class CourseDescriptor(SequenceDescriptor):
...
@@ -460,13 +450,13 @@ class CourseDescriptor(SequenceDescriptor):
@property
@property
def
is_new
(
self
):
def
is_new
ish
(
self
):
"""
"""
Returns if the course has been flagged as new
in the metadata
. If
Returns if the course has been flagged as new. If
there is no flag, return a heuristic value considering the
there is no flag, return a heuristic value considering the
announcement and the start dates.
announcement and the start dates.
"""
"""
flag
=
self
.
metadata
.
get
(
'is_new'
,
None
)
flag
=
self
.
is_new
if
flag
is
None
:
if
flag
is
None
:
# Use a heuristic if the course has not been flagged
# Use a heuristic if the course has not been flagged
announcement
,
start
,
now
=
self
.
_sorting_dates
()
announcement
,
start
,
now
=
self
.
_sorting_dates
()
...
@@ -512,12 +502,10 @@ class CourseDescriptor(SequenceDescriptor):
...
@@ -512,12 +502,10 @@ class CourseDescriptor(SequenceDescriptor):
def
to_datetime
(
timestamp
):
def
to_datetime
(
timestamp
):
return
datetime
(
*
timestamp
[:
6
])
return
datetime
(
*
timestamp
[:
6
])
def
get_date
(
field
):
announcement
=
self
.
announcement
timetuple
=
self
.
_try_parse_time
(
field
)
if
announcement
is
not
None
:
return
to_datetime
(
timetuple
)
if
timetuple
else
None
announcement
=
to_datetime
(
announcement
)
start
=
self
.
advertised_start
or
to_datetime
(
self
.
start
)
announcement
=
get_date
(
'announcement'
)
start
=
get_date
(
'advertised_start'
)
or
to_datetime
(
self
.
start
)
now
=
to_datetime
(
time
.
gmtime
())
now
=
to_datetime
(
time
.
gmtime
())
return
announcement
,
start
,
now
return
announcement
,
start
,
now
...
...
common/lib/xmodule/xmodule/gst_module.py
View file @
bd822b9d
...
@@ -177,7 +177,7 @@ class GraphicalSliderToolDescriptor(MakoModuleDescriptor, XmlDescriptor):
...
@@ -177,7 +177,7 @@ class GraphicalSliderToolDescriptor(MakoModuleDescriptor, XmlDescriptor):
return
{
return
{
'render'
:
parse
(
'render'
),
'render'
:
parse
(
'render'
),
'configuration'
:
parse
(
'configuration'
)
'configuration'
:
parse
(
'configuration'
)
}
}
,
[]
def
definition_to_xml
(
self
,
resource_fs
):
def
definition_to_xml
(
self
,
resource_fs
):
'''Return an xml element representing this definition.'''
'''Return an xml element representing this definition.'''
...
...
common/lib/xmodule/xmodule/modulestore/xml.py
View file @
bd822b9d
...
@@ -456,6 +456,9 @@ class XMLModuleStore(ModuleStoreBase):
...
@@ -456,6 +456,9 @@ class XMLModuleStore(ModuleStoreBase):
def
_load_extra_content
(
self
,
system
,
course_descriptor
,
category
,
path
,
course_dir
):
def
_load_extra_content
(
self
,
system
,
course_descriptor
,
category
,
path
,
course_dir
):
for
filepath
in
glob
.
glob
(
path
/
'*'
):
for
filepath
in
glob
.
glob
(
path
/
'*'
):
if
not
os
.
path
.
isfile
(
filepath
):
continue
with
open
(
filepath
)
as
f
:
with
open
(
filepath
)
as
f
:
try
:
try
:
html
=
f
.
read
()
.
decode
(
'utf-8'
)
html
=
f
.
read
()
.
decode
(
'utf-8'
)
...
...
common/lib/xmodule/xmodule/modulestore/xml_importer.py
View file @
bd822b9d
...
@@ -199,16 +199,13 @@ def import_from_xml(store, data_dir, course_dirs=None,
...
@@ -199,16 +199,13 @@ def import_from_xml(store, data_dir, course_dirs=None,
course_items
=
[]
course_items
=
[]
for
course_id
in
module_store
.
modules
.
keys
():
for
course_id
in
module_store
.
modules
.
keys
():
course_data_path
=
None
course_location
=
None
# Import course modules first, because importing some of the children requires the course to exist
# Import course modules first, because importing some of the children requires the course to exist
for
module
in
module_store
.
modules
[
course_id
]
.
itervalues
():
for
module
in
module_store
.
modules
[
course_id
]
.
itervalues
():
if
module
.
category
==
'course'
:
if
module
.
category
==
'course'
:
import_course_from_xml
(
import_course_from_xml
(
store
,
store
,
static_content_store
,
static_content_store
,
course_data_path
,
data_dir
/
module
.
data_dir
,
module
,
module
,
target_location_namespace
,
target_location_namespace
,
verbose
=
verbose
verbose
=
verbose
...
@@ -220,7 +217,7 @@ def import_from_xml(store, data_dir, course_dirs=None,
...
@@ -220,7 +217,7 @@ def import_from_xml(store, data_dir, course_dirs=None,
import_module_from_xml
(
import_module_from_xml
(
store
,
store
,
static_content_store
,
static_content_store
,
course_data_path
,
data_dir
/
module
.
data_dir
,
module
,
module
,
target_location_namespace
,
target_location_namespace
,
verbose
=
verbose
verbose
=
verbose
...
...
common/lib/xmodule/xmodule/open_ended_module.py
View file @
bd822b9d
...
@@ -20,7 +20,6 @@ import capa.xqueue_interface as xqueue_interface
...
@@ -20,7 +20,6 @@ import capa.xqueue_interface as xqueue_interface
from
pkg_resources
import
resource_string
from
pkg_resources
import
resource_string
from
.capa_module
import
only_one
,
ComplexEncoder
from
.editing_module
import
EditingDescriptor
from
.editing_module
import
EditingDescriptor
from
.html_checker
import
check_html
from
.html_checker
import
check_html
from
progress
import
Progress
from
progress
import
Progress
...
@@ -656,7 +655,7 @@ class OpenEndedDescriptor(XmlDescriptor, EditingDescriptor):
...
@@ -656,7 +655,7 @@ class OpenEndedDescriptor(XmlDescriptor, EditingDescriptor):
"""Assumes that xml_object has child k"""
"""Assumes that xml_object has child k"""
return
xml_object
.
xpath
(
k
)[
0
]
return
xml_object
.
xpath
(
k
)[
0
]
return
{
'oeparam'
:
parse
(
'openendedparam'
)
,
}
return
{
'oeparam'
:
parse
(
'openendedparam'
)
},
[]
def
definition_to_xml
(
self
,
resource_fs
):
def
definition_to_xml
(
self
,
resource_fs
):
...
...
common/lib/xmodule/xmodule/peer_grading_module.py
View file @
bd822b9d
...
@@ -32,6 +32,7 @@ from .stringify import stringify_children
...
@@ -32,6 +32,7 @@ from .stringify import stringify_children
from
.x_module
import
XModule
from
.x_module
import
XModule
from
.xml_module
import
XmlDescriptor
from
.xml_module
import
XmlDescriptor
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xblock.core
import
Scope
,
Object
,
Integer
,
Boolean
,
String
from
peer_grading_service
import
peer_grading_service
,
GradingServiceError
from
peer_grading_service
import
peer_grading_service
,
GradingServiceError
...
@@ -56,32 +57,26 @@ class PeerGradingModule(XModule):
...
@@ -56,32 +57,26 @@ class PeerGradingModule(XModule):
css
=
{
'scss'
:
[
resource_string
(
__name__
,
'css/combinedopenended/display.scss'
)]}
css
=
{
'scss'
:
[
resource_string
(
__name__
,
'css/combinedopenended/display.scss'
)]}
def
__init__
(
self
,
system
,
location
,
definition
,
descriptor
,
student_data_for_location
=
Object
(
scope
=
Scope
.
student_state
)
instance_state
=
None
,
shared_state
=
None
,
**
kwargs
):
max_grade
=
Integer
(
default
=
MAX_SCORE
,
scope
=
Scope
.
student_state
)
XModule
.
__init__
(
self
,
system
,
location
,
definition
,
descriptor
,
use_for_single_location
=
Boolean
(
default
=
USE_FOR_SINGLE_LOCATION
,
scope
=
Scope
.
settings
)
instance_state
,
shared_state
,
**
kwargs
)
is_graded
=
Boolean
(
default
=
IS_GRADED
,
scope
=
Scope
.
settings
)
link_to_location
=
String
(
default
=
LINK_TO_LOCATION
,
scope
=
Scope
.
settings
)
# Load instance state
def
__init__
(
self
,
*
args
,
**
kwargs
):
if
instance_state
is
not
None
:
super
(
PeerGradingModule
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
instance_state
=
json
.
loads
(
instance_state
)
else
:
instance_state
=
{}
#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
)
self
.
system
.
set
(
'location'
,
self
.
location
)
self
.
system
=
system
self
.
peer_gs
=
peer_grading_service
(
self
.
system
)
self
.
peer_gs
=
peer_grading_service
(
self
.
system
)
self
.
use_for_single_location
=
self
.
metadata
.
get
(
'use_for_single_location'
,
USE_FOR_SINGLE_LOCATION
)
if
isinstance
(
self
.
use_for_single_location
,
basestring
):
if
isinstance
(
self
.
use_for_single_location
,
basestring
):
self
.
use_for_single_location
=
(
self
.
use_for_single_location
in
TRUE_DICT
)
self
.
use_for_single_location
=
(
self
.
use_for_single_location
in
TRUE_DICT
)
self
.
is_graded
=
self
.
metadata
.
get
(
'is_graded'
,
IS_GRADED
)
if
isinstance
(
self
.
is_graded
,
basestring
):
if
isinstance
(
self
.
is_graded
,
basestring
):
self
.
is_graded
=
(
self
.
is_graded
in
TRUE_DICT
)
self
.
is_graded
=
(
self
.
is_graded
in
TRUE_DICT
)
self
.
link_to_location
=
self
.
metadata
.
get
(
'link_to_location'
,
USE_FOR_SINGLE_LOCATION
)
if
self
.
use_for_single_location
:
if
self
.
use_for_single_location
==
True
:
#This will raise an exception if the location is invalid
#This will raise an exception if the location is invalid
link_to_location_object
=
Location
(
self
.
link_to_location
)
link_to_location_object
=
Location
(
self
.
link_to_location
)
...
@@ -89,8 +84,6 @@ class PeerGradingModule(XModule):
...
@@ -89,8 +84,6 @@ class PeerGradingModule(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
+
"/"
self
.
student_data_for_location
=
instance_state
.
get
(
'student_data_for_location'
,
{})
self
.
max_grade
=
instance_state
.
get
(
'max_grade'
,
MAX_SCORE
)
if
not
isinstance
(
self
.
max_grade
,
(
int
,
long
)):
if
not
isinstance
(
self
.
max_grade
,
(
int
,
long
)):
#This could result in an exception, but not wrapping in a try catch block so it moves up the stack
#This could result in an exception, but not wrapping in a try catch block so it moves up the stack
self
.
max_grade
=
int
(
self
.
max_grade
)
self
.
max_grade
=
int
(
self
.
max_grade
)
...
@@ -521,7 +514,7 @@ class PeerGradingDescriptor(XmlDescriptor, EditingDescriptor):
...
@@ -521,7 +514,7 @@ class PeerGradingDescriptor(XmlDescriptor, EditingDescriptor):
"""Assumes that xml_object has child k"""
"""Assumes that xml_object has child k"""
return
xml_object
.
xpath
(
k
)[
0
]
return
xml_object
.
xpath
(
k
)[
0
]
return
{}
return
{}
,
[]
def
definition_to_xml
(
self
,
resource_fs
):
def
definition_to_xml
(
self
,
resource_fs
):
...
...
common/lib/xmodule/xmodule/self_assessment_module.py
View file @
bd822b9d
...
@@ -58,10 +58,10 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
...
@@ -58,10 +58,10 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
# Used for progress / grading. Currently get credit just for
# Used for progress / grading. Currently get credit just for
# completion (doesn't matter if you self-assessed correct/incorrect).
# completion (doesn't matter if you self-assessed correct/incorrect).
max_score
=
Integer
(
scope
=
Scope
.
settings
,
default
=
MAX_SCORE
)
max_score
=
Integer
(
scope
=
Scope
.
settings
,
default
=
openendedchild
.
MAX_SCORE
)
max_attempts
=
Integer
(
scope
=
Scope
.
settings
,
default
=
openendedchild
.
MAX_ATTEMPTS
)
attempts
=
Integer
(
scope
=
Scope
.
student_state
,
default
=
0
)
attempts
=
Integer
(
scope
=
Scope
.
student_state
,
default
=
0
)
max_attempts
=
Integer
(
scope
=
Scope
.
settings
,
default
=
MAX_ATTEMPTS
)
rubric
=
String
(
scope
=
Scope
.
content
)
rubric
=
String
(
scope
=
Scope
.
content
)
prompt
=
String
(
scope
=
Scope
.
content
)
prompt
=
String
(
scope
=
Scope
.
content
)
submitmessage
=
String
(
scope
=
Scope
.
content
)
submitmessage
=
String
(
scope
=
Scope
.
content
)
...
@@ -318,9 +318,9 @@ class SelfAssessmentDescriptor(XmlDescriptor, EditingDescriptor):
...
@@ -318,9 +318,9 @@ class SelfAssessmentDescriptor(XmlDescriptor, EditingDescriptor):
# Used for progress / grading. Currently get credit just for
# Used for progress / grading. Currently get credit just for
# completion (doesn't matter if you self-assessed correct/incorrect).
# completion (doesn't matter if you self-assessed correct/incorrect).
max_score
=
Integer
(
scope
=
Scope
.
settings
,
default
=
MAX_SCORE
)
max_score
=
Integer
(
scope
=
Scope
.
settings
,
default
=
openendedchild
.
MAX_SCORE
)
max_attempts
=
Integer
(
scope
=
Scope
.
settings
,
default
=
MAX_ATTEMPTS
)
max_attempts
=
Integer
(
scope
=
Scope
.
settings
,
default
=
openendedchild
.
MAX_ATTEMPTS
)
rubric
=
String
(
scope
=
Scope
.
content
)
rubric
=
String
(
scope
=
Scope
.
content
)
prompt
=
String
(
scope
=
Scope
.
content
)
prompt
=
String
(
scope
=
Scope
.
content
)
submitmessage
=
String
(
scope
=
Scope
.
content
)
submitmessage
=
String
(
scope
=
Scope
.
content
)
...
...
common/lib/xmodule/xmodule/tests/test_course_module.py
View file @
bd822b9d
...
@@ -94,26 +94,26 @@ class IsNewCourseTestCase(unittest.TestCase):
...
@@ -94,26 +94,26 @@ class IsNewCourseTestCase(unittest.TestCase):
@patch
(
'xmodule.course_module.time.gmtime'
)
@patch
(
'xmodule.course_module.time.gmtime'
)
def
test_is_new
(
self
,
gmtime_mock
):
def
test_is_new
ish
(
self
,
gmtime_mock
):
gmtime_mock
.
return_value
=
NOW
gmtime_mock
.
return_value
=
NOW
descriptor
=
self
.
get_dummy_course
(
start
=
'2012-12-02T12:00'
,
is_new
=
True
)
descriptor
=
self
.
get_dummy_course
(
start
=
'2012-12-02T12:00'
,
is_new
=
True
)
assert
(
descriptor
.
is_new
is
True
)
assert
(
descriptor
.
is_new
ish
is
True
)
descriptor
=
self
.
get_dummy_course
(
start
=
'2013-02-02T12:00'
,
is_new
=
False
)
descriptor
=
self
.
get_dummy_course
(
start
=
'2013-02-02T12:00'
,
is_new
=
False
)
assert
(
descriptor
.
is_new
is
False
)
assert
(
descriptor
.
is_new
is
False
)
descriptor
=
self
.
get_dummy_course
(
start
=
'2013-02-02T12:00'
,
is_new
=
True
)
descriptor
=
self
.
get_dummy_course
(
start
=
'2013-02-02T12:00'
,
is_new
=
True
)
assert
(
descriptor
.
is_new
is
True
)
assert
(
descriptor
.
is_new
ish
is
True
)
descriptor
=
self
.
get_dummy_course
(
start
=
'2013-01-15T12:00'
)
descriptor
=
self
.
get_dummy_course
(
start
=
'2013-01-15T12:00'
)
assert
(
descriptor
.
is_new
is
True
)
assert
(
descriptor
.
is_new
ish
is
True
)
descriptor
=
self
.
get_dummy_course
(
start
=
'2013-03-00T12:00'
)
descriptor
=
self
.
get_dummy_course
(
start
=
'2013-03-00T12:00'
)
assert
(
descriptor
.
is_new
is
True
)
assert
(
descriptor
.
is_new
ish
is
True
)
descriptor
=
self
.
get_dummy_course
(
start
=
'2012-10-15T12:00'
)
descriptor
=
self
.
get_dummy_course
(
start
=
'2012-10-15T12:00'
)
assert
(
descriptor
.
is_new
is
False
)
assert
(
descriptor
.
is_new
ish
is
False
)
descriptor
=
self
.
get_dummy_course
(
start
=
'2012-12-31T12:00'
)
descriptor
=
self
.
get_dummy_course
(
start
=
'2012-12-31T12:00'
)
assert
(
descriptor
.
is_new
is
True
)
assert
(
descriptor
.
is_new
ish
is
True
)
lms/djangoapps/courseware/access.py
View file @
bd822b9d
...
@@ -442,7 +442,7 @@ def _adjust_start_date_for_beta_testers(user, descriptor):
...
@@ -442,7 +442,7 @@ def _adjust_start_date_for_beta_testers(user, descriptor):
NOTE: If testing manually, make sure MITX_FEATURES['DISABLE_START_DATES'] = False
NOTE: If testing manually, make sure MITX_FEATURES['DISABLE_START_DATES'] = False
in envs/dev.py!
in envs/dev.py!
"""
"""
if
descriptor
.
days_early_for_beta
is
None
:
if
descriptor
.
lms
.
days_early_for_beta
is
None
:
# bail early if no beta testing is set up
# bail early if no beta testing is set up
return
descriptor
.
lms
.
start
return
descriptor
.
lms
.
start
...
@@ -456,12 +456,12 @@ def _adjust_start_date_for_beta_testers(user, descriptor):
...
@@ -456,12 +456,12 @@ def _adjust_start_date_for_beta_testers(user, descriptor):
# (fun fact: datetime(*a_time_struct[:6]) is the beautiful syntax for
# (fun fact: datetime(*a_time_struct[:6]) is the beautiful syntax for
# converting time_structs into datetimes)
# converting time_structs into datetimes)
start_as_datetime
=
datetime
(
*
descriptor
.
lms
.
start
[:
6
])
start_as_datetime
=
datetime
(
*
descriptor
.
lms
.
start
[:
6
])
delta
=
timedelta
(
descriptor
.
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
.
timetuple
()
return
descriptor
.
start
return
descriptor
.
lms
.
start
def
_has_instructor_access_to_location
(
user
,
location
,
course_context
=
None
):
def
_has_instructor_access_to_location
(
user
,
location
,
course_context
=
None
):
...
...
lms/djangoapps/courseware/courses.py
View file @
bd822b9d
...
@@ -89,7 +89,7 @@ def course_image_url(course):
...
@@ -89,7 +89,7 @@ def course_image_url(course):
"""Try to look up the image url for the course. If it's not found,
"""Try to look up the image url for the course. If it's not found,
log an error and return the dead link"""
log an error and return the dead link"""
if
isinstance
(
modulestore
(),
XMLModuleStore
):
if
isinstance
(
modulestore
(),
XMLModuleStore
):
return
'/static/'
+
course
.
metadata
[
'data_dir'
]
+
"/images/course_image.jpg"
return
'/static/'
+
course
.
data_dir
+
"/images/course_image.jpg"
else
:
else
:
loc
=
course
.
location
.
_replace
(
tag
=
'c4x'
,
category
=
'asset'
,
name
=
'images_course_image.jpg'
)
loc
=
course
.
location
.
_replace
(
tag
=
'c4x'
,
category
=
'asset'
,
name
=
'images_course_image.jpg'
)
path
=
StaticContent
.
get_url_path_from_location
(
loc
)
path
=
StaticContent
.
get_url_path_from_location
(
loc
)
...
...
lms/djangoapps/courseware/module_render.py
View file @
bd822b9d
...
@@ -145,7 +145,7 @@ def get_module(user, request, location, model_data_cache, course_id,
...
@@ -145,7 +145,7 @@ def get_module(user, request, location, model_data_cache, course_id,
location
=
Location
(
location
)
location
=
Location
(
location
)
descriptor
=
modulestore
()
.
get_instance
(
course_id
,
location
,
depth
=
depth
)
descriptor
=
modulestore
()
.
get_instance
(
course_id
,
location
,
depth
=
depth
)
return
get_module_for_descriptor
(
user
,
request
,
descriptor
,
model_data_cache
,
course_id
,
return
get_module_for_descriptor
(
user
,
request
,
descriptor
,
model_data_cache
,
course_id
,
position
=
position
,
not_found_ok
=
not_found_ok
,
position
=
position
,
wrap_xmodule_display
=
wrap_xmodule_display
,
wrap_xmodule_display
=
wrap_xmodule_display
,
grade_bucket_type
=
grade_bucket_type
)
grade_bucket_type
=
grade_bucket_type
)
except
ItemNotFoundError
:
except
ItemNotFoundError
:
...
@@ -205,14 +205,14 @@ def get_module_for_descriptor(user, request, descriptor, model_data_cache, cours
...
@@ -205,14 +205,14 @@ def get_module_for_descriptor(user, request, descriptor, model_data_cache, cours
Delegate to get_module. It does an access check, so may return None
Delegate to get_module. It does an access check, so may return None
"""
"""
return
get_module_for_descriptor
(
user
,
request
,
descriptor
,
return
get_module_for_descriptor
(
user
,
request
,
descriptor
,
student_module
_cache
,
course_id
,
position
)
model_data
_cache
,
course_id
,
position
)
def
xblock_model_data
(
descriptor
):
def
xblock_model_data
(
descriptor
):
return
DbModel
(
return
DbModel
(
LmsKeyValueStore
(
descriptor
.
_model_data
,
model_data_cache
),
LmsKeyValueStore
(
descriptor
.
_model_data
,
model_data_cache
),
descriptor
.
module_class
,
descriptor
.
module_class
,
user
.
id
,
user
.
id
,
LmsUsage
(
location
,
location
)
LmsUsage
(
descriptor
.
location
,
descriptor
.
location
)
)
)
def
publish
(
event
):
def
publish
(
event
):
...
@@ -260,7 +260,7 @@ def get_module_for_descriptor(user, request, descriptor, model_data_cache, cours
...
@@ -260,7 +260,7 @@ def get_module_for_descriptor(user, request, descriptor, model_data_cache, cours
# by the replace_static_urls code below
# by the replace_static_urls code below
replace_urls
=
partial
(
replace_urls
=
partial
(
static_replace
.
replace_static_urls
,
static_replace
.
replace_static_urls
,
data_directory
=
descriptor
.
metadata
.
get
(
'data_dir'
,
''
)
,
data_directory
=
descriptor
.
data_dir
,
course_namespace
=
descriptor
.
location
.
_replace
(
category
=
None
,
name
=
None
),
course_namespace
=
descriptor
.
location
.
_replace
(
category
=
None
,
name
=
None
),
),
),
node_path
=
settings
.
NODE_PATH
,
node_path
=
settings
.
NODE_PATH
,
...
@@ -282,7 +282,7 @@ def get_module_for_descriptor(user, request, descriptor, model_data_cache, cours
...
@@ -282,7 +282,7 @@ def get_module_for_descriptor(user, request, descriptor, model_data_cache, cours
log
.
exception
(
"Error creating module from descriptor {0}"
.
format
(
descriptor
))
log
.
exception
(
"Error creating module from descriptor {0}"
.
format
(
descriptor
))
# make an ErrorDescriptor -- assuming that the descriptor's system is ok
# make an ErrorDescriptor -- assuming that the descriptor's system is ok
if
has_access
(
user
,
location
,
'staff'
,
course_id
):
if
has_access
(
user
,
descriptor
.
location
,
'staff'
,
course_id
):
err_descriptor_class
=
ErrorDescriptor
err_descriptor_class
=
ErrorDescriptor
else
:
else
:
err_descriptor_class
=
NonStaffErrorDescriptor
err_descriptor_class
=
NonStaffErrorDescriptor
...
...
lms/djangoapps/courseware/tabs.py
View file @
bd822b9d
...
@@ -28,6 +28,7 @@ from xmodule.modulestore.django import modulestore
...
@@ -28,6 +28,7 @@ from xmodule.modulestore.django import modulestore
from
xmodule.modulestore.xml
import
XMLModuleStore
from
xmodule.modulestore.xml
import
XMLModuleStore
from
xmodule.x_module
import
XModule
from
xmodule.x_module
import
XModule
from
student.models
import
unique_id_for_user
from
student.models
import
unique_id_for_user
from
courseware.model_data
import
ModelDataCache
from
open_ended_grading
import
open_ended_notifications
from
open_ended_grading
import
open_ended_notifications
...
@@ -321,10 +322,12 @@ def get_static_tab_by_slug(course, tab_slug):
...
@@ -321,10 +322,12 @@ def get_static_tab_by_slug(course, tab_slug):
return
None
return
None
def
get_static_tab_contents
(
request
,
c
ache
,
c
ourse
,
tab
):
def
get_static_tab_contents
(
request
,
course
,
tab
):
loc
=
Location
(
course
.
location
.
tag
,
course
.
location
.
org
,
course
.
location
.
course
,
'static_tab'
,
tab
[
'url_slug'
])
loc
=
Location
(
course
.
location
.
tag
,
course
.
location
.
org
,
course
.
location
.
course
,
'static_tab'
,
tab
[
'url_slug'
])
tab_module
=
get_module
(
request
.
user
,
request
,
loc
,
cache
,
course
.
id
)
model_data_cache
=
ModelDataCache
.
cache_for_descriptor_descendents
(
course
.
id
,
request
.
user
,
modulestore
()
.
get_instance
(
course
.
id
,
loc
),
depth
=
0
)
tab_module
=
get_module
(
request
.
user
,
request
,
loc
,
model_data_cache
,
course
.
id
)
logging
.
debug
(
'course_module = {0}'
.
format
(
tab_module
))
logging
.
debug
(
'course_module = {0}'
.
format
(
tab_module
))
...
...
lms/djangoapps/courseware/tests/tests.py
View file @
bd822b9d
...
@@ -742,11 +742,11 @@ class TestViewAuth(PageLoader):
...
@@ -742,11 +742,11 @@ class TestViewAuth(PageLoader):
yesterday
=
time
.
time
()
-
24
*
3600
yesterday
=
time
.
time
()
-
24
*
3600
# toy course's hasn't started
# toy course's hasn't started
self
.
toy
.
metadata
[
'start'
]
=
stringify_time
(
time
.
gmtime
(
tomorrow
)
)
self
.
toy
.
lms
.
start
=
time
.
gmtime
(
tomorrow
)
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
self
.
toy
.
days_early_for_beta
=
2
self
.
toy
.
lms
.
days_early_for_beta
=
2
# student user shouldn't see it
# student user shouldn't see it
student_user
=
user
(
self
.
student
)
student_user
=
user
(
self
.
student
)
...
...
lms/djangoapps/courseware/views.py
View file @
bd822b9d
...
@@ -443,7 +443,11 @@ def static_tab(request, course_id, tab_slug):
...
@@ -443,7 +443,11 @@ def static_tab(request, course_id, tab_slug):
if
tab
is
None
:
if
tab
is
None
:
raise
Http404
raise
Http404
contents
=
tabs
.
get_static_tab_contents
(
request
,
None
,
course
,
tab
)
contents
=
tabs
.
get_static_tab_contents
(
request
,
course
,
tab
)
if
contents
is
None
:
if
contents
is
None
:
raise
Http404
raise
Http404
...
...
lms/djangoapps/open_ended_grading/controller_query_service.py
View file @
bd822b9d
...
@@ -18,7 +18,7 @@ class ControllerQueryService(GradingService):
...
@@ -18,7 +18,7 @@ class ControllerQueryService(GradingService):
Interface to staff grading backend.
Interface to staff grading backend.
"""
"""
def
__init__
(
self
,
config
):
def
__init__
(
self
,
config
):
config
[
'system'
]
=
ModuleSystem
(
None
,
None
,
None
,
render_to_string
,
None
)
config
[
'system'
]
=
ModuleSystem
(
None
,
None
,
None
,
render_to_string
,
None
,
None
)
super
(
ControllerQueryService
,
self
)
.
__init__
(
config
)
super
(
ControllerQueryService
,
self
)
.
__init__
(
config
)
self
.
check_eta_url
=
self
.
url
+
'/get_submission_eta/'
self
.
check_eta_url
=
self
.
url
+
'/get_submission_eta/'
self
.
is_unique_url
=
self
.
url
+
'/is_name_unique/'
self
.
is_unique_url
=
self
.
url
+
'/is_name_unique/'
...
...
lms/djangoapps/open_ended_grading/tests.py
View file @
bd822b9d
...
@@ -143,10 +143,10 @@ class TestPeerGradingService(ct.PageLoader):
...
@@ -143,10 +143,10 @@ class TestPeerGradingService(ct.PageLoader):
location
=
"i4x://edX/toy/peergrading/init"
location
=
"i4x://edX/toy/peergrading/init"
self
.
mock_service
=
peer_grading_service
.
MockPeerGradingService
()
self
.
mock_service
=
peer_grading_service
.
MockPeerGradingService
()
self
.
system
=
ModuleSystem
(
location
,
None
,
None
,
render_to_string
,
None
)
self
.
system
=
ModuleSystem
(
location
,
None
,
None
,
render_to_string
,
None
,
None
)
self
.
descriptor
=
peer_grading_module
.
PeerGradingDescriptor
(
self
.
system
)
self
.
descriptor
=
peer_grading_module
.
PeerGradingDescriptor
(
self
.
system
,
location
,
{}
)
self
.
peer_module
=
peer_grading_module
.
PeerGradingModule
(
self
.
system
,
location
,
"<peergrading/>"
,
self
.
descriptor
)
self
.
peer_module
=
peer_grading_module
.
PeerGradingModule
(
self
.
system
,
location
,
self
.
descriptor
,
{}
)
self
.
peer_module
.
peer_gs
=
self
.
mock_service
self
.
peer_module
.
peer_gs
=
self
.
mock_service
self
.
logout
()
self
.
logout
()
...
...
lms/templates/course.html
View file @
bd822b9d
...
@@ -5,7 +5,7 @@
...
@@ -5,7 +5,7 @@
%
>
%
>
<
%
page
args=
"course"
/>
<
%
page
args=
"course"
/>
<article
id=
"${course.id}"
class=
"course"
>
<article
id=
"${course.id}"
class=
"course"
>
%if course.is_new:
%if course.is_new
ish
:
<span
class=
"status"
>
New
</span>
<span
class=
"status"
>
New
</span>
%endif
%endif
<a
href=
"${reverse('about_course', args=[course.id])}"
>
<a
href=
"${reverse('about_course', args=[course.id])}"
>
...
...
lms/templates/courseware/progress.html
View file @
bd822b9d
...
@@ -18,7 +18,7 @@
...
@@ -18,7 +18,7 @@
<script
type=
"text/javascript"
src=
"${static.url('js/vendor/flot/jquery.flot.stack.js')}"
></script>
<script
type=
"text/javascript"
src=
"${static.url('js/vendor/flot/jquery.flot.stack.js')}"
></script>
<script
type=
"text/javascript"
src=
"${static.url('js/vendor/flot/jquery.flot.symbol.js')}"
></script>
<script
type=
"text/javascript"
src=
"${static.url('js/vendor/flot/jquery.flot.symbol.js')}"
></script>
<script>
<script>
$
{
progress_graph
.
body
(
grade_summary
,
course
.
grade_cutoffs
,
"grade-detail-graph"
,
not
course
.
metadata
.
get
(
"no_grade"
,
False
),
not
course
.
metadata
.
get
(
"no_grade"
,
False
)
)}
$
{
progress_graph
.
body
(
grade_summary
,
course
.
grade_cutoffs
,
"grade-detail-graph"
,
not
course
.
no_grade
,
not
course
.
no_grade
)}
</script>
</script>
</
%
block>
</
%
block>
...
@@ -32,7 +32,7 @@ ${progress_graph.body(grade_summary, course.grade_cutoffs, "grade-detail-graph",
...
@@ -32,7 +32,7 @@ ${progress_graph.body(grade_summary, course.grade_cutoffs, "grade-detail-graph",
<h1>
Course Progress
</h1>
<h1>
Course Progress
</h1>
</header>
</header>
%if not course.
metadata.get('disable_progress_graph',False)
:
%if not course.
disable_progress_graph
:
<div
id=
"grade-detail-graph"
></div>
<div
id=
"grade-detail-graph"
></div>
%endif
%endif
...
...
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