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
6dd14124
Commit
6dd14124
authored
Jul 10, 2012
by
Calen Pennington
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixing github_sync to work with multiple course data directories
parent
8589a1d9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
242 additions
and
240 deletions
+242
-240
cms/djangoapps/contentstore/__init__.py
+8
-3
cms/djangoapps/contentstore/management/commands/import.py
+8
-4
cms/djangoapps/contentstore/views.py
+4
-3
cms/djangoapps/github_sync/__init__.py
+6
-12
cms/djangoapps/github_sync/tests/__init__.py
+6
-18
common/lib/xmodule/tests/__init__.py
+184
-186
common/lib/xmodule/xmodule/capa_module.py
+1
-2
common/lib/xmodule/xmodule/modulestore/xml.py
+11
-5
lms/djangoapps/courseware/module_render.py
+14
-7
No files found.
cms/djangoapps/contentstore/__init__.py
View file @
6dd14124
...
...
@@ -5,12 +5,17 @@ import logging
log
=
logging
.
getLogger
(
__name__
)
def
import_from_xml
(
org
,
course
,
data_dir
):
def
import_from_xml
(
data_dir
,
course_dirs
=
None
):
"""
Import the specified xml data_dir into the django defined modulestore,
using org and course as the location org and course.
"""
module_store
=
XMLModuleStore
(
org
,
course
,
data_dir
,
'xmodule.raw_module.RawDescriptor'
,
eager
=
True
)
module_store
=
XMLModuleStore
(
data_dir
,
default_class
=
'xmodule.raw_module.RawDescriptor'
,
eager
=
True
,
course_dirs
=
course_dirs
)
for
module
in
module_store
.
modules
.
itervalues
():
# TODO (cpennington): This forces import to overrite the same items.
...
...
@@ -26,4 +31,4 @@ def import_from_xml(org, course, data_dir):
modulestore
()
.
update_children
(
module
.
location
,
module
.
definition
[
'children'
])
modulestore
()
.
update_metadata
(
module
.
location
,
dict
(
module
.
metadata
))
return
module_store
.
course
return
module_store
cms/djangoapps/contentstore/management/commands/import.py
View file @
6dd14124
...
...
@@ -13,8 +13,12 @@ class Command(BaseCommand):
'''Import the specified data directory into the default ModuleStore'''
def
handle
(
self
,
*
args
,
**
options
):
if
len
(
args
)
!=
3
:
raise
CommandError
(
"import requires
3 arguments: <org> <course> <data directory>
"
)
if
len
(
args
)
==
0
:
raise
CommandError
(
"import requires
at least one argument: <data directory> [<course dir>...]
"
)
org
,
course
,
data_dir
=
args
import_from_xml
(
org
,
course
,
data_dir
)
data_dir
=
args
[
0
]
if
len
(
args
)
>
1
:
course_dirs
=
args
[
1
:]
else
:
course_dirs
=
None
import_from_xml
(
data_dir
,
course_dirs
)
cms/djangoapps/contentstore/views.py
View file @
6dd14124
...
...
@@ -6,7 +6,7 @@ from django_future.csrf import ensure_csrf_cookie
from
fs.osfs
import
OSFS
from
django.core.urlresolvers
import
reverse
from
xmodule.modulestore
import
Location
from
github_sync
import
repo_path_from_location
,
export_to_github
from
github_sync
import
export_to_github
from
mitxmako.shortcuts
import
render_to_response
from
xmodule.modulestore.django
import
modulestore
...
...
@@ -51,11 +51,12 @@ def save_item(request):
modulestore
()
.
update_item
(
item_id
,
data
)
# Export the course back to github
# This uses wildcarding to find the course, which requires handling
# multiple courses returned, but there should only ever be one
course_location
=
Location
(
item_id
)
.
_replace
(
category
=
'course'
,
name
=
None
)
courses
=
modulestore
()
.
get_items
(
course_location
)
for
course
in
courses
:
repo_path
=
repo_path_from_location
(
course
.
location
)
export_to_github
(
course
,
repo_path
,
"CMS Edit"
)
export_to_github
(
course
,
"CMS Edit"
)
return
HttpResponse
(
json
.
dumps
({}))
...
...
cms/djangoapps/github_sync/__init__.py
View file @
6dd14124
...
...
@@ -12,6 +12,7 @@ from .exceptions import GithubSyncError
log
=
logging
.
getLogger
(
__name__
)
def
import_from_github
(
repo_settings
):
"""
Imports data into the modulestore based on the XML stored on github
...
...
@@ -19,10 +20,9 @@ def import_from_github(repo_settings):
repo_settings is a dictionary with the following keys:
path: file system path to the local git repo
branch: name of the branch to track on github
org: name of the organization to use in the imported course
course: name of the coures to use in the imported course
"""
repo_path
=
repo_settings
[
'path'
]
data_dir
,
course_dir
=
os
.
path
.
split
(
repo_path
)
if
not
os
.
path
.
isdir
(
repo_path
):
Repo
.
clone_from
(
repo_settings
[
'origin'
],
repo_path
)
...
...
@@ -34,18 +34,12 @@ def import_from_github(repo_settings):
# Do a hard reset to the remote branch so that we have a clean import
git_repo
.
git
.
checkout
(
repo_settings
[
'branch'
])
git_repo
.
head
.
reset
(
'origin/
%
s'
%
repo_settings
[
'branch'
],
index
=
True
,
working_tree
=
True
)
return
git_repo
.
head
.
commit
.
hexsha
,
import_from_xml
(
repo_settings
[
'org'
],
repo_settings
[
'course'
],
repo_path
)
def
repo_path_from_location
(
location
):
location
=
Location
(
location
)
for
name
,
repo
in
settings
.
REPOS
.
items
():
if
repo
[
'org'
]
==
location
.
org
and
repo
[
'course'
]
==
location
.
course
:
return
repo
[
'path'
]
module_store
=
import_from_xml
(
data_dir
,
course_dirs
=
[
course_dir
])
return
git_repo
.
head
.
commit
.
hexsha
,
module_store
.
courses
[
course_dir
]
def
export_to_github
(
course
,
repo_path
,
commit_message
):
def
export_to_github
(
course
,
commit_message
):
repo_path
=
settings
.
DATA_DIR
/
course
.
metadata
.
get
(
'course_dir'
,
course
.
location
.
course
)
fs
=
OSFS
(
repo_path
)
xml
=
course
.
export_to_xml
(
fs
)
...
...
cms/djangoapps/github_sync/tests/__init__.py
View file @
6dd14124
from
django.test
import
TestCase
from
path
import
path
import
shutil
from
github_sync
import
import_from_github
,
export_to_github
,
repo_path_from_location
from
github_sync
import
import_from_github
,
export_to_github
from
git
import
Repo
from
django.conf
import
settings
from
xmodule.modulestore.django
import
modulestore
...
...
@@ -10,6 +10,7 @@ from override_settings import override_settings
from
github_sync.exceptions
import
GithubSyncError
@override_settings
(
DATA_DIR
=
path
(
'test_root'
))
class
GithubSyncTestCase
(
TestCase
):
def
setUp
(
self
):
...
...
@@ -29,8 +30,6 @@ class GithubSyncTestCase(TestCase):
'path'
:
self
.
repo_dir
,
'origin'
:
self
.
remote_dir
,
'branch'
:
'master'
,
'org'
:
'org'
,
'course'
:
'course'
})
def
tearDown
(
self
):
...
...
@@ -49,7 +48,7 @@ class GithubSyncTestCase(TestCase):
"""
self
.
assertEquals
(
'Toy Course'
,
self
.
import_course
.
metadata
[
'display_name'
])
self
.
assertIn
(
Location
(
'i4x://
org/course
/chapter/Overview'
),
Location
(
'i4x://
edx/local_repo
/chapter/Overview'
),
[
child
.
location
for
child
in
self
.
import_course
.
get_children
()])
self
.
assertEquals
(
1
,
len
(
self
.
import_course
.
get_children
()))
...
...
@@ -58,7 +57,7 @@ class GithubSyncTestCase(TestCase):
"""
Test that with the GITHUB_PUSH feature disabled, no content is pushed to the remote
"""
export_to_github
(
self
.
import_course
,
self
.
repo_dir
,
'Test no-push'
)
export_to_github
(
self
.
import_course
,
'Test no-push'
)
self
.
assertEquals
(
1
,
Repo
(
self
.
remote_dir
)
.
head
.
commit
.
count
())
@override_settings
(
MITX_FEATURES
=
{
'GITHUB_PUSH'
:
True
})
...
...
@@ -67,7 +66,7 @@ class GithubSyncTestCase(TestCase):
Test that with GITHUB_PUSH enabled, content is pushed to the remote
"""
self
.
import_course
.
metadata
[
'display_name'
]
=
'Changed display name'
export_to_github
(
self
.
import_course
,
self
.
repo_dir
,
'Test push'
)
export_to_github
(
self
.
import_course
,
'Test push'
)
self
.
assertEquals
(
2
,
Repo
(
self
.
remote_dir
)
.
head
.
commit
.
count
())
@override_settings
(
MITX_FEATURES
=
{
'GITHUB_PUSH'
:
True
})
...
...
@@ -80,17 +79,6 @@ class GithubSyncTestCase(TestCase):
remote
=
Repo
(
self
.
remote_dir
)
remote
.
git
.
commit
(
allow_empty
=
True
,
m
=
"Testing conflict commit"
)
self
.
assertRaises
(
GithubSyncError
,
export_to_github
,
self
.
import_course
,
self
.
repo_dir
,
'Test push'
)
self
.
assertRaises
(
GithubSyncError
,
export_to_github
,
self
.
import_course
,
'Test push'
)
self
.
assertEquals
(
2
,
remote
.
head
.
reference
.
commit
.
count
())
self
.
assertEquals
(
"Testing conflict commit
\n
"
,
remote
.
head
.
reference
.
commit
.
message
)
@override_settings
(
REPOS
=
{
'namea'
:
{
'path'
:
'patha'
,
'org'
:
'orga'
,
'course'
:
'coursea'
},
'nameb'
:
{
'path'
:
'pathb'
,
'org'
:
'orgb'
,
'course'
:
'courseb'
}})
class
RepoPathLookupTestCase
(
TestCase
):
def
test_successful_lookup
(
self
):
self
.
assertEquals
(
'patha'
,
repo_path_from_location
(
'i4x://orga/coursea/course/foo'
))
self
.
assertEquals
(
'pathb'
,
repo_path_from_location
(
'i4x://orgb/courseb/course/foo'
))
def
test_failed_lookup
(
self
):
self
.
assertEquals
(
None
,
repo_path_from_location
(
'i4x://c/c/course/foo'
))
common/lib/xmodule/tests/__init__.py
View file @
6dd14124
...
...
@@ -412,210 +412,208 @@ class GraderTest(unittest.TestCase):
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
(
12
+
1
)
+
(
7
+
1
)
+
1
)
self
.
assertEqual
(
len
(
graded
[
'grade_breakdown'
]),
3
)
graded
=
zeroWeightsGrader
.
grade
(
self
.
test_gradesheet
)
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.2525
)
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
(
12
+
1
)
+
(
7
+
1
)
+
1
)
self
.
assertEqual
(
len
(
graded
[
'grade_breakdown'
]),
3
)
graded
=
zeroWeightsGrader
.
grade
(
self
.
test_gradesheet
)
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.2525
)
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
(
12
+
1
)
+
(
7
+
1
)
+
1
)
self
.
assertEqual
(
len
(
graded
[
'grade_breakdown'
]),
3
)
graded
=
allZeroWeightsGrader
.
grade
(
self
.
test_gradesheet
)
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.0
)
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
(
12
+
1
)
+
(
7
+
1
)
+
1
)
self
.
assertEqual
(
len
(
graded
[
'grade_breakdown'
]),
3
)
for
graded
in
[
weightedGrader
.
grade
(
self
.
empty_gradesheet
),
weightedGrader
.
grade
(
self
.
incomplete_gradesheet
),
zeroWeightsGrader
.
grade
(
self
.
empty_gradesheet
),
allZeroWeightsGrader
.
grade
(
self
.
empty_gradesheet
)]:
graded
=
allZeroWeightsGrader
.
grade
(
self
.
test_gradesheet
)
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.0
)
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
(
12
+
1
)
+
(
7
+
1
)
+
1
)
self
.
assertEqual
(
len
(
graded
[
'grade_breakdown'
]),
3
)
graded
=
emptyGrader
.
grade
(
self
.
test_gradesheet
)
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.0
)
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
0
)
self
.
assertEqual
(
len
(
graded
[
'grade_breakdown'
]),
0
)
for
graded
in
[
weightedGrader
.
grade
(
self
.
empty_gradesheet
),
weightedGrader
.
grade
(
self
.
incomplete_gradesheet
),
zeroWeightsGrader
.
grade
(
self
.
empty_gradesheet
),
allZeroWeightsGrader
.
grade
(
self
.
empty_gradesheet
)]:
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.0
)
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
(
12
+
1
)
+
(
7
+
1
)
+
1
)
self
.
assertEqual
(
len
(
graded
[
'grade_breakdown'
]),
3
)
graded
=
emptyGrader
.
grade
(
self
.
test_gradesheet
)
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.0
)
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
0
)
self
.
assertEqual
(
len
(
graded
[
'grade_breakdown'
]),
0
)
def
test_graderFromConf
(
self
):
#Confs always produce a graders.WeightedSubsectionsGrader, so we test this by repeating the test
#in test_graders.WeightedSubsectionsGrader, but generate the graders with confs.
weightedGrader
=
graders
.
grader_from_conf
([
{
'type'
:
"Homework"
,
'min_count'
:
12
,
'drop_count'
:
2
,
'short_label'
:
"HW"
,
'weight'
:
0.25
,
},
{
'type'
:
"Lab"
,
'min_count'
:
7
,
'drop_count'
:
3
,
'category'
:
"Labs"
,
'weight'
:
0.25
},
{
'type'
:
"Midterm"
,
'name'
:
"Midterm Exam"
,
'short_label'
:
"Midterm"
,
'weight'
:
0.5
,
},
])
emptyGrader
=
graders
.
grader_from_conf
([])
graded
=
weightedGrader
.
grade
(
self
.
test_gradesheet
)
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.5106547619047619
)
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
(
12
+
1
)
+
(
7
+
1
)
+
1
)
self
.
assertEqual
(
len
(
graded
[
'grade_breakdown'
]),
3
)
graded
=
emptyGrader
.
grade
(
self
.
test_gradesheet
)
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.0
)
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
0
)
self
.
assertEqual
(
len
(
graded
[
'grade_breakdown'
]),
0
)
#Test that graders can also be used instead of lists of dictionaries
homeworkGrader
=
graders
.
AssignmentFormatGrader
(
"Homework"
,
12
,
2
)
homeworkGrader2
=
graders
.
grader_from_conf
(
homeworkGrader
)
graded
=
homeworkGrader2
.
grade
(
self
.
test_gradesheet
)
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.11
)
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
12
+
1
)
def
test_graderFromConf
(
self
):
#Confs always produce a graders.WeightedSubsectionsGrader, so we test this by repeating the test
#in test_graders.WeightedSubsectionsGrader, but generate the graders with confs.
weightedGrader
=
graders
.
grader_from_conf
([
{
'type'
:
"Homework"
,
'min_count'
:
12
,
'drop_count'
:
2
,
'short_label'
:
"HW"
,
'weight'
:
0.25
,
},
{
'type'
:
"Lab"
,
'min_count'
:
7
,
'drop_count'
:
3
,
'category'
:
"Labs"
,
'weight'
:
0.25
},
{
'type'
:
"Midterm"
,
'name'
:
"Midterm Exam"
,
'short_label'
:
"Midterm"
,
'weight'
:
0.5
,
},
])
emptyGrader
=
graders
.
grader_from_conf
([])
graded
=
weightedGrader
.
grade
(
self
.
test_gradesheet
)
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.5106547619047619
)
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
(
12
+
1
)
+
(
7
+
1
)
+
1
)
self
.
assertEqual
(
len
(
graded
[
'grade_breakdown'
]),
3
)
graded
=
emptyGrader
.
grade
(
self
.
test_gradesheet
)
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.0
)
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
0
)
self
.
assertEqual
(
len
(
graded
[
'grade_breakdown'
]),
0
)
#Test that graders can also be used instead of lists of dictionaries
homeworkGrader
=
graders
.
AssignmentFormatGrader
(
"Homework"
,
12
,
2
)
homeworkGrader2
=
graders
.
grader_from_conf
(
homeworkGrader
)
graded
=
homeworkGrader2
.
grade
(
self
.
test_gradesheet
)
self
.
assertAlmostEqual
(
graded
[
'percent'
],
0.11
)
self
.
assertEqual
(
len
(
graded
[
'section_breakdown'
]),
12
+
1
)
#TODO: How do we test failure cases? The parser only logs an error when it can't parse something. Maybe it should throw exceptions?
#TODO: How do we test failure cases? The parser only logs an error when it can't parse something. Maybe it should throw exceptions?
# --------------------------------------------------------------------------
# Module progress tests
class
ProgressTest
(
unittest
.
TestCase
):
''' Test that basic Progress objects work. A Progress represents a
fraction between 0 and 1.
'''
not_started
=
Progress
(
0
,
17
)
part_done
=
Progress
(
2
,
6
)
half_done
=
Progress
(
3
,
6
)
also_half_done
=
Progress
(
1
,
2
)
done
=
Progress
(
7
,
7
)
def
test_create_object
(
self
):
# These should work:
p
=
Progress
(
0
,
2
)
p
=
Progress
(
1
,
2
)
p
=
Progress
(
2
,
2
)
p
=
Progress
(
2.5
,
5.0
)
p
=
Progress
(
3.7
,
12.3333
)
# These shouldn't
self
.
assertRaises
(
ValueError
,
Progress
,
0
,
0
)
self
.
assertRaises
(
ValueError
,
Progress
,
2
,
0
)
self
.
assertRaises
(
ValueError
,
Progress
,
1
,
-
2
)
self
.
assertRaises
(
ValueError
,
Progress
,
3
,
2
)
self
.
assertRaises
(
ValueError
,
Progress
,
-
2
,
5
)
self
.
assertRaises
(
TypeError
,
Progress
,
0
,
"all"
)
# check complex numbers just for the heck of it :)
self
.
assertRaises
(
TypeError
,
Progress
,
2
j
,
3
)
def
test_frac
(
self
):
p
=
Progress
(
1
,
2
)
(
a
,
b
)
=
p
.
frac
()
self
.
assertEqual
(
a
,
1
)
self
.
assertEqual
(
b
,
2
)
def
test_percent
(
self
):
self
.
assertEqual
(
self
.
not_started
.
percent
(),
0
)
self
.
assertAlmostEqual
(
self
.
part_done
.
percent
(),
33.33333333333333
)
self
.
assertEqual
(
self
.
half_done
.
percent
(),
50
)
self
.
assertEqual
(
self
.
done
.
percent
(),
100
)
self
.
assertEqual
(
self
.
half_done
.
percent
(),
self
.
also_half_done
.
percent
())
def
test_started
(
self
):
self
.
assertFalse
(
self
.
not_started
.
started
())
self
.
assertTrue
(
self
.
part_done
.
started
())
self
.
assertTrue
(
self
.
half_done
.
started
())
self
.
assertTrue
(
self
.
done
.
started
())
def
test_inprogress
(
self
):
# only true if working on it
self
.
assertFalse
(
self
.
done
.
inprogress
())
self
.
assertFalse
(
self
.
not_started
.
inprogress
())
self
.
assertTrue
(
self
.
part_done
.
inprogress
())
self
.
assertTrue
(
self
.
half_done
.
inprogress
())
def
test_done
(
self
):
self
.
assertTrue
(
self
.
done
.
done
())
self
.
assertFalse
(
self
.
half_done
.
done
())
self
.
assertFalse
(
self
.
not_started
.
done
())
def
test_str
(
self
):
self
.
assertEqual
(
str
(
self
.
not_started
),
"0/17"
)
self
.
assertEqual
(
str
(
self
.
part_done
),
"2/6"
)
self
.
assertEqual
(
str
(
self
.
done
),
"7/7"
)
def
test_ternary_str
(
self
):
self
.
assertEqual
(
self
.
not_started
.
ternary_str
(),
"none"
)
self
.
assertEqual
(
self
.
half_done
.
ternary_str
(),
"in_progress"
)
self
.
assertEqual
(
self
.
done
.
ternary_str
(),
"done"
)
def
test_to_js_status
(
self
):
'''Test the Progress.to_js_status_str() method'''
self
.
assertEqual
(
Progress
.
to_js_status_str
(
self
.
not_started
),
"none"
)
self
.
assertEqual
(
Progress
.
to_js_status_str
(
self
.
half_done
),
"in_progress"
)
self
.
assertEqual
(
Progress
.
to_js_status_str
(
self
.
done
),
"done"
)
self
.
assertEqual
(
Progress
.
to_js_status_str
(
None
),
"NA"
)
def
test_to_js_detail_str
(
self
):
'''Test the Progress.to_js_detail_str() method'''
f
=
Progress
.
to_js_detail_str
for
p
in
(
self
.
not_started
,
self
.
half_done
,
self
.
done
):
self
.
assertEqual
(
f
(
p
),
str
(
p
))
# But None should be encoded as NA
self
.
assertEqual
(
f
(
None
),
"NA"
)
def
test_add
(
self
):
'''Test the Progress.add_counts() method'''
p
=
Progress
(
0
,
2
)
p2
=
Progress
(
1
,
3
)
p3
=
Progress
(
2
,
5
)
pNone
=
None
add
=
lambda
a
,
b
:
Progress
.
add_counts
(
a
,
b
)
.
frac
()
self
.
assertEqual
(
add
(
p
,
p
),
(
0
,
4
))
self
.
assertEqual
(
add
(
p
,
p2
),
(
1
,
5
))
self
.
assertEqual
(
add
(
p2
,
p3
),
(
3
,
8
))
self
.
assertEqual
(
add
(
p2
,
pNone
),
p2
.
frac
())
self
.
assertEqual
(
add
(
pNone
,
p2
),
p2
.
frac
())
''' Test that basic Progress objects work. A Progress represents a
fraction between 0 and 1.
'''
not_started
=
Progress
(
0
,
17
)
part_done
=
Progress
(
2
,
6
)
half_done
=
Progress
(
3
,
6
)
also_half_done
=
Progress
(
1
,
2
)
done
=
Progress
(
7
,
7
)
def
test_create_object
(
self
):
# These should work:
p
=
Progress
(
0
,
2
)
p
=
Progress
(
1
,
2
)
p
=
Progress
(
2
,
2
)
p
=
Progress
(
2.5
,
5.0
)
p
=
Progress
(
3.7
,
12.3333
)
# These shouldn't
self
.
assertRaises
(
ValueError
,
Progress
,
0
,
0
)
self
.
assertRaises
(
ValueError
,
Progress
,
2
,
0
)
self
.
assertRaises
(
ValueError
,
Progress
,
1
,
-
2
)
self
.
assertRaises
(
ValueError
,
Progress
,
3
,
2
)
self
.
assertRaises
(
ValueError
,
Progress
,
-
2
,
5
)
self
.
assertRaises
(
TypeError
,
Progress
,
0
,
"all"
)
# check complex numbers just for the heck of it :)
self
.
assertRaises
(
TypeError
,
Progress
,
2
j
,
3
)
def
test_frac
(
self
):
p
=
Progress
(
1
,
2
)
(
a
,
b
)
=
p
.
frac
()
self
.
assertEqual
(
a
,
1
)
self
.
assertEqual
(
b
,
2
)
def
test_percent
(
self
):
self
.
assertEqual
(
self
.
not_started
.
percent
(),
0
)
self
.
assertAlmostEqual
(
self
.
part_done
.
percent
(),
33.33333333333333
)
self
.
assertEqual
(
self
.
half_done
.
percent
(),
50
)
self
.
assertEqual
(
self
.
done
.
percent
(),
100
)
self
.
assertEqual
(
self
.
half_done
.
percent
(),
self
.
also_half_done
.
percent
())
def
test_started
(
self
):
self
.
assertFalse
(
self
.
not_started
.
started
())
self
.
assertTrue
(
self
.
part_done
.
started
())
self
.
assertTrue
(
self
.
half_done
.
started
())
self
.
assertTrue
(
self
.
done
.
started
())
def
test_inprogress
(
self
):
# only true if working on it
self
.
assertFalse
(
self
.
done
.
inprogress
())
self
.
assertFalse
(
self
.
not_started
.
inprogress
())
self
.
assertTrue
(
self
.
part_done
.
inprogress
())
self
.
assertTrue
(
self
.
half_done
.
inprogress
())
def
test_done
(
self
):
self
.
assertTrue
(
self
.
done
.
done
())
self
.
assertFalse
(
self
.
half_done
.
done
())
self
.
assertFalse
(
self
.
not_started
.
done
())
def
test_str
(
self
):
self
.
assertEqual
(
str
(
self
.
not_started
),
"0/17"
)
self
.
assertEqual
(
str
(
self
.
part_done
),
"2/6"
)
self
.
assertEqual
(
str
(
self
.
done
),
"7/7"
)
def
test_ternary_str
(
self
):
self
.
assertEqual
(
self
.
not_started
.
ternary_str
(),
"none"
)
self
.
assertEqual
(
self
.
half_done
.
ternary_str
(),
"in_progress"
)
self
.
assertEqual
(
self
.
done
.
ternary_str
(),
"done"
)
def
test_to_js_status
(
self
):
'''Test the Progress.to_js_status_str() method'''
self
.
assertEqual
(
Progress
.
to_js_status_str
(
self
.
not_started
),
"none"
)
self
.
assertEqual
(
Progress
.
to_js_status_str
(
self
.
half_done
),
"in_progress"
)
self
.
assertEqual
(
Progress
.
to_js_status_str
(
self
.
done
),
"done"
)
self
.
assertEqual
(
Progress
.
to_js_status_str
(
None
),
"NA"
)
def
test_to_js_detail_str
(
self
):
'''Test the Progress.to_js_detail_str() method'''
f
=
Progress
.
to_js_detail_str
for
p
in
(
self
.
not_started
,
self
.
half_done
,
self
.
done
):
self
.
assertEqual
(
f
(
p
),
str
(
p
))
# But None should be encoded as NA
self
.
assertEqual
(
f
(
None
),
"NA"
)
def
test_add
(
self
):
'''Test the Progress.add_counts() method'''
p
=
Progress
(
0
,
2
)
p2
=
Progress
(
1
,
3
)
p3
=
Progress
(
2
,
5
)
pNone
=
None
add
=
lambda
a
,
b
:
Progress
.
add_counts
(
a
,
b
)
.
frac
()
self
.
assertEqual
(
add
(
p
,
p
),
(
0
,
4
))
self
.
assertEqual
(
add
(
p
,
p2
),
(
1
,
5
))
self
.
assertEqual
(
add
(
p2
,
p3
),
(
3
,
8
))
self
.
assertEqual
(
add
(
p2
,
pNone
),
p2
.
frac
())
self
.
assertEqual
(
add
(
pNone
,
p2
),
p2
.
frac
())
def
test_equality
(
self
):
'''Test that comparing Progress objects for equality
works correctly.'''
p
=
Progress
(
1
,
2
)
p2
=
Progress
(
2
,
4
)
p3
=
Progress
(
1
,
2
)
self
.
assertTrue
(
p
==
p3
)
self
.
assertFalse
(
p
==
p2
)
def
test_equality
(
self
):
'''Test that comparing Progress objects for equality
works correctly.'''
p
=
Progress
(
1
,
2
)
p2
=
Progress
(
2
,
4
)
p3
=
Progress
(
1
,
2
)
self
.
assertTrue
(
p
==
p3
)
self
.
assertFalse
(
p
==
p2
)
# Check != while we're at it
self
.
assertTrue
(
p
!=
p2
)
self
.
assertFalse
(
p
!=
p3
)
# Check != while we're at it
self
.
assertTrue
(
p
!=
p2
)
self
.
assertFalse
(
p
!=
p3
)
class
ModuleProgressTest
(
unittest
.
TestCase
):
''' Test that get_progress() does the right thing for the different modules
'''
def
test_xmodule_default
(
self
):
'''Make sure default get_progress exists, returns None'''
''' Test that get_progress() does the right thing for the different modules
'''
def
test_xmodule_default
(
self
):
'''Make sure default get_progress exists, returns None'''
xm
=
x_module
.
XModule
(
i4xs
,
'a://b/c/d/e'
,
{})
p
=
xm
.
get_progress
()
self
.
assertEqual
(
p
,
None
)
common/lib/xmodule/xmodule/capa_module.py
View file @
6dd14124
...
...
@@ -15,7 +15,6 @@ from xmodule.raw_module import RawDescriptor
from
progress
import
Progress
from
capa.capa_problem
import
LoncapaProblem
from
capa.responsetypes
import
StudentInputError
from
static_replace
import
replace_urls
log
=
logging
.
getLogger
(
"mitx.courseware"
)
...
...
@@ -275,7 +274,7 @@ class CapaModule(XModule):
html
=
'<div id="problem_{id}" class="problem" data-url="{ajax_url}">'
.
format
(
id
=
self
.
location
.
html_id
(),
ajax_url
=
self
.
system
.
ajax_url
)
+
html
+
"</div>"
return
replace_urls
(
html
,
self
.
metadata
[
'data_dir'
])
return
self
.
system
.
replace_urls
(
html
,
self
.
metadata
[
'data_dir'
])
def
handle_ajax
(
self
,
dispatch
,
get
):
'''
...
...
common/lib/xmodule/xmodule/modulestore/xml.py
View file @
6dd14124
...
...
@@ -20,19 +20,21 @@ class XMLModuleStore(ModuleStore):
"""
An XML backed ModuleStore
"""
def
__init__
(
self
,
data_dir
,
default_class
=
None
,
eager
=
False
):
def
__init__
(
self
,
data_dir
,
default_class
=
None
,
eager
=
False
,
course_dirs
=
None
):
"""
Initialize an XMLModuleStore from data_dir
data_dir: path to data directory containing the course directories
default_class: dot-separated string defining the default descriptor class to use if non is specified in entry_points
eager: If true, load the modules children immediately to force the entire course tree to be parsed
course_dirs: If specified, the list of course_dirs to load. Otherwise, load
all course dirs
"""
self
.
eager
=
eager
self
.
data_dir
=
path
(
data_dir
)
self
.
modules
=
{}
self
.
courses
=
[]
self
.
courses
=
{}
if
default_class
is
None
:
self
.
default_class
=
None
...
...
@@ -46,10 +48,14 @@ class XMLModuleStore(ModuleStore):
log
.
debug
(
'default_class =
%
s'
%
self
.
default_class
)
for
course_dir
in
os
.
listdir
(
self
.
data_dir
):
if
not
os
.
path
.
exists
(
self
.
data_dir
+
"/"
+
course_dir
+
"/course.xml"
)
:
if
course_dirs
is
not
None
and
course_dir
not
in
course_dirs
:
continue
self
.
courses
.
append
(
self
.
load_course
(
course_dir
))
if
not
os
.
path
.
exists
(
self
.
data_dir
/
course_dir
/
"course.xml"
):
continue
course_descriptor
=
self
.
load_course
(
course_dir
)
self
.
courses
[
course_dir
]
=
course_descriptor
def
load_course
(
self
,
course_dir
):
"""
...
...
@@ -148,7 +154,7 @@ class XMLModuleStore(ModuleStore):
"""
Returns a list of course descriptors
"""
return
self
.
courses
return
self
.
courses
.
values
()
def
create_item
(
self
,
location
):
raise
NotImplementedError
(
"XMLModuleStores are read-only"
)
...
...
lms/djangoapps/courseware/module_render.py
View file @
6dd14124
...
...
@@ -27,8 +27,8 @@ class I4xSystem(object):
and user, or other environment-specific info.
'''
def
__init__
(
self
,
ajax_url
,
track_function
,
get_module
,
render_template
,
user
=
None
,
filestore
=
None
):
get_module
,
render_template
,
replace_urls
,
user
=
None
,
filestore
=
None
):
'''
Create a closure around the system environment.
...
...
@@ -44,6 +44,8 @@ class I4xSystem(object):
user - The user to base the seed off of for this request
filestore - A filestore ojbect. Defaults to an instance of OSFS based at
settings.DATA_DIR.
replace_urls - TEMPORARY - A function like static_replace.replace_urls
that capa_module can use to fix up the static urls in ajax results.
'''
self
.
ajax_url
=
ajax_url
self
.
track_function
=
track_function
...
...
@@ -53,6 +55,7 @@ class I4xSystem(object):
self
.
exception404
=
Http404
self
.
DEBUG
=
settings
.
DEBUG
self
.
seed
=
user
.
id
if
user
is
not
None
else
0
self
.
replace_urls
=
replace_urls
def
get
(
self
,
attr
):
''' provide uniform access to attributes (like etree).'''
...
...
@@ -209,6 +212,9 @@ def get_module(user, request, location, student_module_cache, position=None):
(
module
,
_
,
_
,
_
)
=
get_module
(
user
,
request
,
location
,
student_module_cache
,
position
)
return
module
# TODO (cpennington): When modules are shared between courses, the static
# prefix is going to have to be specific to the module, not the directory
# that the xml was loaded from
system
=
I4xSystem
(
track_function
=
make_track_function
(
request
),
render_template
=
render_to_string
,
ajax_url
=
ajax_url
,
...
...
@@ -216,17 +222,18 @@ def get_module(user, request, location, student_module_cache, position=None):
filestore
=
descriptor
.
system
.
resources_fs
,
get_module
=
_get_module
,
user
=
user
,
# TODO (cpennington): This should be removed when all html from
# a module is coming through get_html and is therefore covered
# by the replace_static_urls code below
replace_urls
=
replace_urls
,
)
# pass position specified in URL to module through I4xSystem
system
.
set
(
'position'
,
position
)
module
=
descriptor
.
xmodule_constructor
(
system
)(
instance_state
,
shared_state
)
# TODO (cpennington): When modules are shared between courses, the static
# prefix is going to have to be specific to the module, not the directory
# that the xml was loaded from
prefix
=
module
.
metadata
[
'data_dir'
]
module
=
replace_static_urls
(
module
,
prefix
)
replace_prefix
=
module
.
metadata
[
'data_dir'
]
module
=
replace_static_urls
(
module
,
replace_prefix
)
if
settings
.
MITX_FEATURES
.
get
(
'DISPLAY_HISTOGRAMS_TO_STAFF'
)
and
user
.
is_staff
:
module
=
add_histogram
(
module
)
...
...
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