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
eafb0755
Commit
eafb0755
authored
Aug 12, 2015
by
Peter Fogg
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #9287 from edx/release
Release
parents
5b9b0a83
986564c4
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
222 additions
and
726 deletions
+222
-726
cms/djangoapps/contentstore/tests/test_contentstore.py
+2
-2
cms/djangoapps/contentstore/views/course.py
+1
-1
cms/djangoapps/contentstore/views/tests/test_import_export.py
+0
-35
cms/djangoapps/contentstore/views/tests/test_item.py
+1
-1
common/djangoapps/student/models.py
+0
-6
common/djangoapps/student/views.py
+0
-18
common/lib/xmodule/xmodule/course_module.py
+1
-1
lms/djangoapps/ccx/migrations/0002_convert_memberships.py
+102
-0
lms/djangoapps/ccx/models.py
+0
-47
lms/djangoapps/ccx/tests/factories.py
+0
-11
lms/djangoapps/ccx/tests/test_field_override_performance.py
+10
-11
lms/djangoapps/ccx/tests/test_models.py
+0
-115
lms/djangoapps/ccx/tests/test_utils.py
+0
-0
lms/djangoapps/ccx/tests/test_views.py
+27
-34
lms/djangoapps/ccx/utils.py
+12
-272
lms/djangoapps/ccx/views.py
+27
-21
lms/djangoapps/django_comment_client/utils.py
+1
-1
lms/djangoapps/mobile_api/users/tests.py
+2
-2
lms/djangoapps/oauth2_handler/handlers.py
+9
-16
lms/djangoapps/oauth2_handler/tests.py
+7
-35
lms/templates/ccx/_dashboard_ccx_listing.html
+0
-80
lms/templates/ccx/enrollment.html
+2
-2
lms/templates/dashboard.html
+0
-8
openedx/core/djangoapps/content/course_overviews/models.py
+15
-4
openedx/core/djangoapps/content/course_overviews/tests.py
+2
-2
requirements/edx/github.txt
+1
-1
No files found.
cms/djangoapps/contentstore/tests/test_contentstore.py
View file @
eafb0755
...
@@ -1903,8 +1903,8 @@ class RerunCourseTest(ContentStoreTestCase):
...
@@ -1903,8 +1903,8 @@ class RerunCourseTest(ContentStoreTestCase):
source_course
=
CourseFactory
.
create
(
advertised_start
=
"01-12-2015"
)
source_course
=
CourseFactory
.
create
(
advertised_start
=
"01-12-2015"
)
destination_course_key
=
self
.
post_rerun_request
(
source_course
.
id
)
destination_course_key
=
self
.
post_rerun_request
(
source_course
.
id
)
destination_course
=
self
.
store
.
get_course
(
destination_course_key
)
destination_course
=
self
.
store
.
get_course
(
destination_course_key
)
# Advertised_start is String field so it will return empty string if its not set
self
.
assertEqual
(
''
,
destination_course
.
advertised_start
)
self
.
assertEqual
(
None
,
destination_course
.
advertised_start
)
def
test_rerun_of_rerun
(
self
):
def
test_rerun_of_rerun
(
self
):
source_course
=
CourseFactory
.
create
()
source_course
=
CourseFactory
.
create
()
...
...
cms/djangoapps/contentstore/views/course.py
View file @
eafb0755
...
@@ -787,7 +787,7 @@ def _rerun_course(request, org, number, run, fields):
...
@@ -787,7 +787,7 @@ def _rerun_course(request, org, number, run, fields):
CourseRerunState
.
objects
.
initiated
(
source_course_key
,
destination_course_key
,
request
.
user
,
fields
[
'display_name'
])
CourseRerunState
.
objects
.
initiated
(
source_course_key
,
destination_course_key
,
request
.
user
,
fields
[
'display_name'
])
# Clear the fields that must be reset for the rerun
# Clear the fields that must be reset for the rerun
fields
[
'advertised_start'
]
=
''
fields
[
'advertised_start'
]
=
None
# Rerun the course as a new celery task
# Rerun the course as a new celery task
json_fields
=
json
.
dumps
(
fields
,
cls
=
EdxJSONEncoder
)
json_fields
=
json
.
dumps
(
fields
,
cls
=
EdxJSONEncoder
)
...
...
cms/djangoapps/contentstore/views/tests/test_import_export.py
View file @
eafb0755
...
@@ -451,41 +451,6 @@ class ExportTestCase(CourseTestCase):
...
@@ -451,41 +451,6 @@ class ExportTestCase(CourseTestCase):
finally
:
finally
:
shutil
.
rmtree
(
root_dir
/
name
)
shutil
.
rmtree
(
root_dir
/
name
)
def
test_library_import_then_export
(
self
):
"""
Verify that a library exports successfully after being imported.
"""
library
=
LibraryFactory
.
create
(
modulestore
=
self
.
store
)
lib_key
=
library
.
location
.
library_key
name
=
library
.
url_name
# import the library
extract_dir
=
path
(
tempfile
.
mkdtemp
(
dir
=
settings
.
DATA_DIR
))
extract_dir_relative
=
path
.
relpath
(
extract_dir
,
settings
.
DATA_DIR
)
try
:
with
tarfile
.
open
(
path
(
TEST_DATA_DIR
)
/
'imports'
/
'library.HhJfPD.tar.gz'
)
as
tar
:
safetar_extractall
(
tar
,
extract_dir
)
library_items
=
import_library_from_xml
(
self
.
store
,
self
.
user
.
id
,
settings
.
GITHUB_REPO_ROOT
,
[
extract_dir_relative
/
'library'
],
load_error_modules
=
False
,
static_content_store
=
contentstore
(),
target_id
=
lib_key
)
# verify library import correctly
self
.
assertEqual
(
lib_key
,
library_items
[
0
]
.
location
.
library_key
)
library
=
self
.
store
.
get_library
(
lib_key
)
self
.
assertEqual
(
len
(
library
.
children
),
3
)
# export library again
export_library_to_xml
(
self
.
store
,
contentstore
(),
lib_key
,
extract_dir
,
name
)
finally
:
shutil
.
rmtree
(
extract_dir
)
def
test_export_success_with_custom_tag
(
self
):
def
test_export_success_with_custom_tag
(
self
):
"""
"""
Verify that course export with customtag
Verify that course export with customtag
...
...
cms/djangoapps/contentstore/views/tests/test_item.py
View file @
eafb0755
...
@@ -625,7 +625,7 @@ class TestEditItem(TestEditItemSetup):
...
@@ -625,7 +625,7 @@ class TestEditItem(TestEditItemSetup):
data
=
{
'nullout'
:
[
'markdown'
]}
data
=
{
'nullout'
:
[
'markdown'
]}
)
)
problem
=
self
.
get_item_from_modulestore
(
self
.
problem_usage_key
,
verify_is_draft
=
True
)
problem
=
self
.
get_item_from_modulestore
(
self
.
problem_usage_key
,
verify_is_draft
=
True
)
self
.
assert
Equal
(
problem
.
markdown
,
''
)
self
.
assert
IsNone
(
problem
.
markdown
)
def
test_date_fields
(
self
):
def
test_date_fields
(
self
):
"""
"""
...
...
common/djangoapps/student/models.py
View file @
eafb0755
...
@@ -1200,12 +1200,6 @@ class CourseEnrollment(models.Model):
...
@@ -1200,12 +1200,6 @@ class CourseEnrollment(models.Model):
if
not
user
.
is_authenticated
():
if
not
user
.
is_authenticated
():
return
False
return
False
# unwrap CCXLocators so that we use the course as the access control
# source
from
ccx_keys.locator
import
CCXLocator
if
isinstance
(
course_key
,
CCXLocator
):
course_key
=
course_key
.
to_course_locator
()
try
:
try
:
record
=
CourseEnrollment
.
objects
.
get
(
user
=
user
,
course_id
=
course_key
)
record
=
CourseEnrollment
.
objects
.
get
(
user
=
user
,
course_id
=
course_key
)
return
record
.
is_active
return
record
.
is_active
...
...
common/djangoapps/student/views.py
View file @
eafb0755
...
@@ -655,13 +655,6 @@ def dashboard(request):
...
@@ -655,13 +655,6 @@ def dashboard(request):
)
)
courses_requirements_not_met
=
get_pre_requisite_courses_not_completed
(
user
,
courses_having_prerequisites
)
courses_requirements_not_met
=
get_pre_requisite_courses_not_completed
(
user
,
courses_having_prerequisites
)
ccx_membership_triplets
=
[]
if
settings
.
FEATURES
.
get
(
'CUSTOM_COURSES_EDX'
,
False
):
from
ccx.utils
import
get_ccx_membership_triplets
ccx_membership_triplets
=
get_ccx_membership_triplets
(
user
,
course_org_filter
,
org_filter_out_set
)
if
'notlive'
in
request
.
GET
:
if
'notlive'
in
request
.
GET
:
redirect_message
=
_
(
"The course you are looking for does not start until {date}."
)
.
format
(
redirect_message
=
_
(
"The course you are looking for does not start until {date}."
)
.
format
(
date
=
request
.
GET
[
'notlive'
]
date
=
request
.
GET
[
'notlive'
]
...
@@ -697,7 +690,6 @@ def dashboard(request):
...
@@ -697,7 +690,6 @@ def dashboard(request):
'provider_states'
:
[],
'provider_states'
:
[],
'order_history_list'
:
order_history_list
,
'order_history_list'
:
order_history_list
,
'courses_requirements_not_met'
:
courses_requirements_not_met
,
'courses_requirements_not_met'
:
courses_requirements_not_met
,
'ccx_membership_triplets'
:
ccx_membership_triplets
,
'nav_hidden'
:
True
,
'nav_hidden'
:
True
,
}
}
...
@@ -1904,16 +1896,6 @@ def activate_account(request, key):
...
@@ -1904,16 +1896,6 @@ def activate_account(request, key):
manual_enrollment_audit
.
reason
,
enrollment
manual_enrollment_audit
.
reason
,
enrollment
)
)
# enroll student in any pending CCXs he/she may have if auto_enroll flag is set
if
settings
.
FEATURES
.
get
(
'CUSTOM_COURSES_EDX'
):
from
ccx.models
import
CcxMembership
,
CcxFutureMembership
ccxfms
=
CcxFutureMembership
.
objects
.
filter
(
email
=
student
[
0
]
.
email
)
for
ccxfm
in
ccxfms
:
if
ccxfm
.
auto_enroll
:
CcxMembership
.
auto_enroll
(
student
[
0
],
ccxfm
)
resp
=
render_to_response
(
resp
=
render_to_response
(
"registration/activation_complete.html"
,
"registration/activation_complete.html"
,
{
{
...
...
common/lib/xmodule/xmodule/course_module.py
View file @
eafb0755
...
@@ -948,7 +948,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor, LicenseMixin):
...
@@ -948,7 +948,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor, LicenseMixin):
super
(
CourseDescriptor
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
super
(
CourseDescriptor
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
_
=
self
.
runtime
.
service
(
self
,
"i18n"
)
.
ugettext
_
=
self
.
runtime
.
service
(
self
,
"i18n"
)
.
ugettext
if
not
self
.
wiki_slug
:
if
self
.
wiki_slug
is
None
:
self
.
wiki_slug
=
self
.
location
.
course
self
.
wiki_slug
=
self
.
location
.
course
if
self
.
due_date_display_format
is
None
and
self
.
show_timezone
is
False
:
if
self
.
due_date_display_format
is
None
and
self
.
show_timezone
is
False
:
...
...
lms/djangoapps/ccx/migrations/0002_convert_memberships.py
0 → 100644
View file @
eafb0755
# -*- coding: utf-8 -*-
from
south.utils
import
datetime_utils
as
datetime
from
south.db
import
db
from
south.v2
import
DataMigration
from
django.db
import
models
from
opaque_keys
import
InvalidKeyError
class
Migration
(
DataMigration
):
def
forwards
(
self
,
orm
):
"Convert CCX Memberships to Course Enrollments."
from
ccx_keys.locator
import
CCXLocator
memberships
=
orm
[
'ccx.CcxMembership'
]
.
objects
.
select_related
(
'ccx'
,
'student'
)
.
all
()
for
membership
in
memberships
:
ccx
=
membership
.
ccx
try
:
course_key
=
CCXLocator
.
from_course_locator
(
ccx
.
course_id
,
ccx
.
id
)
enrollment
,
created
=
orm
[
'student.CourseEnrollment'
]
.
objects
.
get_or_create
(
user
=
membership
.
student
,
course_id
=
course_key
,
)
except
InvalidKeyError
:
membership
.
delete
()
def
backwards
(
self
,
orm
):
"""In the future, here we will convert back CCX Course Enrollments to CCX
Memberships.
"""
models
=
{
'auth.group'
:
{
'Meta'
:
{
'object_name'
:
'Group'
},
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'80'
}),
'permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
})
},
'auth.permission'
:
{
'Meta'
:
{
'ordering'
:
"('content_type__app_label', 'content_type__model', 'codename')"
,
'unique_together'
:
"(('content_type', 'codename'),)"
,
'object_name'
:
'Permission'
},
'codename'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'content_type'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['contenttypes.ContentType']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
})
},
'auth.user'
:
{
'Meta'
:
{
'object_name'
:
'User'
},
'date_joined'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'email'
:
(
'django.db.models.fields.EmailField'
,
[],
{
'max_length'
:
'75'
,
'blank'
:
'True'
}),
'first_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'groups'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Group']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'is_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'is_staff'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'is_superuser'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'last_login'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'last_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'password'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
}),
'user_permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'username'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'30'
})
},
'ccx.ccxfieldoverride'
:
{
'Meta'
:
{
'unique_together'
:
"(('ccx', 'location', 'field'),)"
,
'object_name'
:
'CcxFieldOverride'
},
'ccx'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['ccx.CustomCourseForEdX']"
}),
'field'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'location'
:
(
'xmodule_django.models.LocationKeyField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'value'
:
(
'django.db.models.fields.TextField'
,
[],
{
'default'
:
"'null'"
})
},
'ccx.customcourseforedx'
:
{
'Meta'
:
{
'object_name'
:
'CustomCourseForEdX'
},
'coach'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'display_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
})
},
'ccx.ccxmembership'
:
{
'Meta'
:
{
'object_name'
:
'CcxMembership'
},
'active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
False
}),
'ccx'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['ccx.CustomCourseForEdX']"
}),
'student'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
})
},
'student.courseenrollment'
:
{
'Meta'
:
{
'object_name'
:
'CourseEnrollment'
},
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'is_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
True
}),
'mode'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
,
'default'
:
'"honor"'
}),
'created'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
})
},
'contenttypes.contenttype'
:
{
'Meta'
:
{
'ordering'
:
"('name',)"
,
'unique_together'
:
"(('app_label', 'model'),)"
,
'object_name'
:
'ContentType'
,
'db_table'
:
"'django_content_type'"
},
'app_label'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'model'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
})
}
}
complete_apps
=
[
'ccx'
,
'ccx'
]
symmetrical
=
True
lms/djangoapps/ccx/models.py
View file @
eafb0755
...
@@ -9,7 +9,6 @@ from django.db import models
...
@@ -9,7 +9,6 @@ from django.db import models
from
django.utils.timezone
import
UTC
from
django.utils.timezone
import
UTC
from
lazy
import
lazy
from
lazy
import
lazy
from
student.models
import
CourseEnrollment
,
AlreadyEnrolledError
# pylint: disable=import-error
from
xmodule_django.models
import
CourseKeyField
,
LocationKeyField
# pylint: disable=import-error
from
xmodule_django.models
import
CourseKeyField
,
LocationKeyField
# pylint: disable=import-error
from
xmodule.error_module
import
ErrorDescriptor
from
xmodule.error_module
import
ErrorDescriptor
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
...
@@ -96,52 +95,6 @@ class CustomCourseForEdX(models.Model):
...
@@ -96,52 +95,6 @@ class CustomCourseForEdX(models.Model):
return
value
return
value
class
CcxMembership
(
models
.
Model
):
"""
Which students are in a CCX?
"""
ccx
=
models
.
ForeignKey
(
CustomCourseForEdX
,
db_index
=
True
)
student
=
models
.
ForeignKey
(
User
,
db_index
=
True
)
active
=
models
.
BooleanField
(
default
=
False
)
@classmethod
def
auto_enroll
(
cls
,
student
,
future_membership
):
"""convert future_membership to an active membership
"""
if
not
future_membership
.
auto_enroll
:
msg
=
"auto enrollment not allowed for {}"
raise
ValueError
(
msg
.
format
(
future_membership
))
membership
=
cls
(
ccx
=
future_membership
.
ccx
,
student
=
student
,
active
=
True
)
try
:
CourseEnrollment
.
enroll
(
student
,
future_membership
.
ccx
.
course_id
,
check_access
=
True
)
except
AlreadyEnrolledError
:
# if the user is already enrolled in the course, great!
pass
membership
.
save
()
future_membership
.
delete
()
@classmethod
def
memberships_for_user
(
cls
,
user
,
active
=
True
):
"""
active memberships for a user
"""
return
cls
.
objects
.
filter
(
student
=
user
,
active__exact
=
active
)
class
CcxFutureMembership
(
models
.
Model
):
"""
Which emails for non-users are waiting to be added to CCX on registration
"""
ccx
=
models
.
ForeignKey
(
CustomCourseForEdX
,
db_index
=
True
)
email
=
models
.
CharField
(
max_length
=
255
)
auto_enroll
=
models
.
BooleanField
(
default
=
0
)
class
CcxFieldOverride
(
models
.
Model
):
class
CcxFieldOverride
(
models
.
Model
):
"""
"""
Field overrides for custom courses.
Field overrides for custom courses.
...
...
lms/djangoapps/ccx/tests/factories.py
View file @
eafb0755
...
@@ -5,8 +5,6 @@ from factory import SubFactory
...
@@ -5,8 +5,6 @@ from factory import SubFactory
from
factory.django
import
DjangoModelFactory
from
factory.django
import
DjangoModelFactory
from
student.tests.factories
import
UserFactory
from
student.tests.factories
import
UserFactory
from
ccx.models
import
CustomCourseForEdX
# pylint: disable=import-error
from
ccx.models
import
CustomCourseForEdX
# pylint: disable=import-error
from
ccx.models
import
CcxMembership
# pylint: disable=import-error
from
ccx.models
import
CcxFutureMembership
# pylint: disable=import-error
class
CcxFactory
(
DjangoModelFactory
):
# pylint: disable=missing-docstring
class
CcxFactory
(
DjangoModelFactory
):
# pylint: disable=missing-docstring
...
@@ -14,12 +12,3 @@ class CcxFactory(DjangoModelFactory): # pylint: disable=missing-docstring
...
@@ -14,12 +12,3 @@ class CcxFactory(DjangoModelFactory): # pylint: disable=missing-docstring
display_name
=
"Test CCX"
display_name
=
"Test CCX"
id
=
None
# pylint: disable=redefined-builtin, invalid-name
id
=
None
# pylint: disable=redefined-builtin, invalid-name
coach
=
SubFactory
(
UserFactory
)
coach
=
SubFactory
(
UserFactory
)
class
CcxMembershipFactory
(
DjangoModelFactory
):
# pylint: disable=missing-docstring
FACTORY_FOR
=
CcxMembership
active
=
False
class
CcxFutureMembershipFactory
(
DjangoModelFactory
):
# pylint: disable=missing-docstring
FACTORY_FOR
=
CcxFutureMembership
lms/djangoapps/ccx/tests/test_field_override_performance.py
View file @
eafb0755
...
@@ -27,7 +27,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, \
...
@@ -27,7 +27,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, \
from
xmodule.modulestore.tests.factories
import
check_mongo_calls
,
CourseFactory
,
check_sum_of_calls
from
xmodule.modulestore.tests.factories
import
check_mongo_calls
,
CourseFactory
,
check_sum_of_calls
from
xmodule.modulestore.tests.utils
import
ProceduralCourseTestMixin
from
xmodule.modulestore.tests.utils
import
ProceduralCourseTestMixin
from
ccx_keys.locator
import
CCXLocator
from
ccx_keys.locator
import
CCXLocator
from
ccx.tests.factories
import
CcxFactory
,
CcxMembershipFactory
from
ccx.tests.factories
import
CcxFactory
@attr
(
'shard_1'
)
@attr
(
'shard_1'
)
...
@@ -65,7 +65,7 @@ class FieldOverridePerformanceTestCase(ProceduralCourseTestMixin,
...
@@ -65,7 +65,7 @@ class FieldOverridePerformanceTestCase(ProceduralCourseTestMixin,
MakoMiddleware
()
.
process_request
(
self
.
request
)
MakoMiddleware
()
.
process_request
(
self
.
request
)
def
setup_course
(
self
,
size
,
enable_ccx
):
def
setup_course
(
self
,
size
,
enable_ccx
,
view_as_ccx
):
"""
"""
Build a gradable course where each node has `size` children.
Build a gradable course where each node has `size` children.
"""
"""
...
@@ -112,16 +112,15 @@ class FieldOverridePerformanceTestCase(ProceduralCourseTestMixin,
...
@@ -112,16 +112,15 @@ class FieldOverridePerformanceTestCase(ProceduralCourseTestMixin,
)
)
self
.
populate_course
(
size
)
self
.
populate_course
(
size
)
course_key
=
self
.
course
.
id
if
enable_ccx
:
self
.
ccx
=
CcxFactory
.
create
(
course_id
=
self
.
course
.
id
)
if
view_as_ccx
:
course_key
=
CCXLocator
.
from_course_locator
(
self
.
course
.
id
,
self
.
ccx
.
id
)
CourseEnrollment
.
enroll
(
CourseEnrollment
.
enroll
(
self
.
student
,
self
.
student
,
self
.
course
.
id
course_key
)
if
enable_ccx
:
self
.
ccx
=
CcxFactory
.
create
()
CcxMembershipFactory
.
create
(
student
=
self
.
student
,
ccx
=
self
.
ccx
)
)
def
grade_course
(
self
,
course
,
view_as_ccx
):
def
grade_course
(
self
,
course
,
view_as_ccx
):
...
@@ -156,7 +155,7 @@ class FieldOverridePerformanceTestCase(ProceduralCourseTestMixin,
...
@@ -156,7 +155,7 @@ class FieldOverridePerformanceTestCase(ProceduralCourseTestMixin,
"""
"""
Renders the progress page, instrumenting Mongo reads and SQL queries.
Renders the progress page, instrumenting Mongo reads and SQL queries.
"""
"""
self
.
setup_course
(
course_width
,
enable_ccx
)
self
.
setup_course
(
course_width
,
enable_ccx
,
view_as_ccx
)
# Switch to published-only mode to simulate the LMS
# Switch to published-only mode to simulate the LMS
with
self
.
settings
(
MODULESTORE_BRANCH
=
'published-only'
):
with
self
.
settings
(
MODULESTORE_BRANCH
=
'published-only'
):
...
...
lms/djangoapps/ccx/tests/test_models.py
View file @
eafb0755
...
@@ -5,12 +5,9 @@ from datetime import datetime, timedelta
...
@@ -5,12 +5,9 @@ from datetime import datetime, timedelta
from
django.utils.timezone
import
UTC
from
django.utils.timezone
import
UTC
from
mock
import
patch
from
mock
import
patch
from
nose.plugins.attrib
import
attr
from
nose.plugins.attrib
import
attr
from
student.models
import
CourseEnrollment
# pylint: disable=import-error
from
student.roles
import
CourseCcxCoachRole
# pylint: disable=import-error
from
student.roles
import
CourseCcxCoachRole
# pylint: disable=import-error
from
student.tests.factories
import
(
# pylint: disable=import-error
from
student.tests.factories
import
(
# pylint: disable=import-error
AdminFactory
,
AdminFactory
,
CourseEnrollmentFactory
,
UserFactory
,
)
)
from
util.tests.test_date_utils
import
fake_ugettext
from
util.tests.test_date_utils
import
fake_ugettext
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
...
@@ -21,123 +18,11 @@ from xmodule.modulestore.tests.factories import (
...
@@ -21,123 +18,11 @@ from xmodule.modulestore.tests.factories import (
from
.factories
import
(
from
.factories
import
(
CcxFactory
,
CcxFactory
,
CcxFutureMembershipFactory
,
)
from
..models
import
(
CcxMembership
,
CcxFutureMembership
,
)
)
from
..overrides
import
override_field_for_ccx
from
..overrides
import
override_field_for_ccx
@attr
(
'shard_1'
)
@attr
(
'shard_1'
)
class
TestCcxMembership
(
ModuleStoreTestCase
):
"""Unit tests for the CcxMembership model
"""
def
setUp
(
self
):
"""common setup for all tests"""
super
(
TestCcxMembership
,
self
)
.
setUp
()
self
.
course
=
course
=
CourseFactory
.
create
()
coach
=
AdminFactory
.
create
()
role
=
CourseCcxCoachRole
(
course
.
id
)
role
.
add_users
(
coach
)
self
.
ccx
=
CcxFactory
(
course_id
=
course
.
id
,
coach
=
coach
)
enrollment
=
CourseEnrollmentFactory
.
create
(
course_id
=
course
.
id
)
self
.
enrolled_user
=
enrollment
.
user
self
.
unenrolled_user
=
UserFactory
.
create
()
def
create_future_enrollment
(
self
,
user
,
auto_enroll
=
True
):
"""
utility method to create future enrollment
"""
pfm
=
CcxFutureMembershipFactory
.
create
(
ccx
=
self
.
ccx
,
email
=
user
.
email
,
auto_enroll
=
auto_enroll
)
return
pfm
def
has_course_enrollment
(
self
,
user
):
"""
utility method to create future enrollment
"""
enrollment
=
CourseEnrollment
.
objects
.
filter
(
user
=
user
,
course_id
=
self
.
course
.
id
)
return
enrollment
.
exists
()
def
has_ccx_membership
(
self
,
user
):
"""
verify ccx membership
"""
membership
=
CcxMembership
.
objects
.
filter
(
student
=
user
,
ccx
=
self
.
ccx
,
active
=
True
)
return
membership
.
exists
()
def
has_ccx_future_membership
(
self
,
user
):
"""
verify future ccx membership
"""
future_membership
=
CcxFutureMembership
.
objects
.
filter
(
email
=
user
.
email
,
ccx
=
self
.
ccx
)
return
future_membership
.
exists
()
def
call_mut
(
self
,
student
,
future_membership
):
"""
Call the method undser test
"""
CcxMembership
.
auto_enroll
(
student
,
future_membership
)
def
test_ccx_auto_enroll_unregistered_user
(
self
):
"""verify auto_enroll works when user is not enrolled in the MOOC
n.b. After auto_enroll, user will have both a MOOC enrollment and a
CCX membership
"""
user
=
self
.
unenrolled_user
pfm
=
self
.
create_future_enrollment
(
user
)
self
.
assertTrue
(
self
.
has_ccx_future_membership
(
user
))
self
.
assertFalse
(
self
.
has_course_enrollment
(
user
))
# auto_enroll user
self
.
call_mut
(
user
,
pfm
)
self
.
assertTrue
(
self
.
has_course_enrollment
(
user
))
self
.
assertTrue
(
self
.
has_ccx_membership
(
user
))
self
.
assertFalse
(
self
.
has_ccx_future_membership
(
user
))
def
test_ccx_auto_enroll_registered_user
(
self
):
"""verify auto_enroll works when user is enrolled in the MOOC
"""
user
=
self
.
enrolled_user
pfm
=
self
.
create_future_enrollment
(
user
)
self
.
assertTrue
(
self
.
has_ccx_future_membership
(
user
))
self
.
assertTrue
(
self
.
has_course_enrollment
(
user
))
self
.
call_mut
(
user
,
pfm
)
self
.
assertTrue
(
self
.
has_course_enrollment
(
user
))
self
.
assertTrue
(
self
.
has_ccx_membership
(
user
))
self
.
assertFalse
(
self
.
has_ccx_future_membership
(
user
))
def
test_future_membership_disallows_auto_enroll
(
self
):
"""verify that the CcxFutureMembership can veto auto_enroll
"""
user
=
self
.
unenrolled_user
pfm
=
self
.
create_future_enrollment
(
user
,
auto_enroll
=
False
)
self
.
assertTrue
(
self
.
has_ccx_future_membership
(
user
))
self
.
assertFalse
(
self
.
has_course_enrollment
(
user
))
self
.
assertRaises
(
ValueError
,
self
.
call_mut
,
user
,
pfm
)
self
.
assertFalse
(
self
.
has_course_enrollment
(
user
))
self
.
assertFalse
(
self
.
has_ccx_membership
(
user
))
self
.
assertTrue
(
self
.
has_ccx_future_membership
(
user
))
@attr
(
'shard_1'
)
class
TestCCX
(
ModuleStoreTestCase
):
class
TestCCX
(
ModuleStoreTestCase
):
"""Unit tests for the CustomCourseForEdX model
"""Unit tests for the CustomCourseForEdX model
"""
"""
...
...
lms/djangoapps/ccx/tests/test_utils.py
View file @
eafb0755
This diff is collapsed.
Click to expand it.
lms/djangoapps/ccx/tests/test_views.py
View file @
eafb0755
...
@@ -20,9 +20,12 @@ from django.utils.timezone import UTC
...
@@ -20,9 +20,12 @@ from django.utils.timezone import UTC
from
django.test.utils
import
override_settings
from
django.test.utils
import
override_settings
from
django.test
import
RequestFactory
from
django.test
import
RequestFactory
from
edxmako.shortcuts
import
render_to_response
# pylint: disable=import-error
from
edxmako.shortcuts
import
render_to_response
# pylint: disable=import-error
from
student.models
import
CourseEnrollment
from
request_cache.middleware
import
RequestCache
from
request_cache.middleware
import
RequestCache
from
student.roles
import
CourseCcxCoachRole
# pylint: disable=import-error
from
student.roles
import
CourseCcxCoachRole
# pylint: disable=import-error
from
student.models
import
(
CourseEnrollment
,
CourseEnrollmentAllowed
,
)
from
student.tests.factories
import
(
# pylint: disable=import-error
from
student.tests.factories
import
(
# pylint: disable=import-error
AdminFactory
,
AdminFactory
,
CourseEnrollmentFactory
,
CourseEnrollmentFactory
,
...
@@ -42,14 +45,10 @@ from ccx_keys.locator import CCXLocator
...
@@ -42,14 +45,10 @@ from ccx_keys.locator import CCXLocator
from
..models
import
(
from
..models
import
(
CustomCourseForEdX
,
CustomCourseForEdX
,
CcxMembership
,
CcxFutureMembership
,
)
)
from
..overrides
import
get_override_for_ccx
,
override_field_for_ccx
from
..overrides
import
get_override_for_ccx
,
override_field_for_ccx
from
.factories
import
(
from
.factories
import
(
CcxFactory
,
CcxFactory
,
CcxMembershipFactory
,
CcxFutureMembershipFactory
,
)
)
...
@@ -280,7 +279,7 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -280,7 +279,7 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
self
.
assertTrue
(
student
.
email
in
outbox
[
0
]
.
recipients
())
# pylint: disable=no-member
self
.
assertTrue
(
student
.
email
in
outbox
[
0
]
.
recipients
())
# pylint: disable=no-member
# a CcxMembership exists for this student
# a CcxMembership exists for this student
self
.
assertTrue
(
self
.
assertTrue
(
C
cxMembership
.
objects
.
filter
(
ccx
=
ccx
,
student
=
student
)
.
exists
()
C
ourseEnrollment
.
objects
.
filter
(
course_id
=
self
.
course
.
id
,
user
=
student
)
.
exists
()
)
)
def
test_unenroll_member_student
(
self
):
def
test_unenroll_member_student
(
self
):
...
@@ -288,16 +287,15 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -288,16 +287,15 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""
"""
self
.
make_coach
()
self
.
make_coach
()
ccx
=
self
.
make_ccx
()
ccx
=
self
.
make_ccx
()
enrollment
=
CourseEnrollmentFactory
(
course_id
=
self
.
course
.
id
)
course_key
=
CCXLocator
.
from_course_locator
(
self
.
course
.
id
,
ccx
.
id
)
enrollment
=
CourseEnrollmentFactory
(
course_id
=
course_key
)
student
=
enrollment
.
user
student
=
enrollment
.
user
outbox
=
self
.
get_outbox
()
outbox
=
self
.
get_outbox
()
self
.
assertEqual
(
outbox
,
[])
self
.
assertEqual
(
outbox
,
[])
# student is member of CCX:
CcxMembershipFactory
(
ccx
=
ccx
,
student
=
student
)
url
=
reverse
(
url
=
reverse
(
'ccx_invite'
,
'ccx_invite'
,
kwargs
=
{
'course_id'
:
CCXLocator
.
from_course_locator
(
self
.
course
.
id
,
ccx
.
id
)
}
kwargs
=
{
'course_id'
:
course_key
}
)
)
data
=
{
data
=
{
'enrollment-button'
:
'Unenroll'
,
'enrollment-button'
:
'Unenroll'
,
...
@@ -311,10 +309,6 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -311,10 +309,6 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
self
.
assertTrue
(
302
in
response
.
redirect_chain
[
0
])
self
.
assertTrue
(
302
in
response
.
redirect_chain
[
0
])
self
.
assertEqual
(
len
(
outbox
),
1
)
self
.
assertEqual
(
len
(
outbox
),
1
)
self
.
assertTrue
(
student
.
email
in
outbox
[
0
]
.
recipients
())
# pylint: disable=no-member
self
.
assertTrue
(
student
.
email
in
outbox
[
0
]
.
recipients
())
# pylint: disable=no-member
# the membership for this student is gone
self
.
assertFalse
(
CcxMembership
.
objects
.
filter
(
ccx
=
ccx
,
student
=
student
)
.
exists
()
)
def
test_enroll_non_user_student
(
self
):
def
test_enroll_non_user_student
(
self
):
"""enroll a list of students who are not users yet
"""enroll a list of students who are not users yet
...
@@ -322,12 +316,13 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -322,12 +316,13 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
test_email
=
"nobody@nowhere.com"
test_email
=
"nobody@nowhere.com"
self
.
make_coach
()
self
.
make_coach
()
ccx
=
self
.
make_ccx
()
ccx
=
self
.
make_ccx
()
course_key
=
CCXLocator
.
from_course_locator
(
self
.
course
.
id
,
ccx
.
id
)
outbox
=
self
.
get_outbox
()
outbox
=
self
.
get_outbox
()
self
.
assertEqual
(
outbox
,
[])
self
.
assertEqual
(
outbox
,
[])
url
=
reverse
(
url
=
reverse
(
'ccx_invite'
,
'ccx_invite'
,
kwargs
=
{
'course_id'
:
CCXLocator
.
from_course_locator
(
self
.
course
.
id
,
ccx
.
id
)
}
kwargs
=
{
'course_id'
:
course_key
}
)
)
data
=
{
data
=
{
'enrollment-button'
:
'Enroll'
,
'enrollment-button'
:
'Enroll'
,
...
@@ -342,8 +337,8 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -342,8 +337,8 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
self
.
assertEqual
(
len
(
outbox
),
1
)
self
.
assertEqual
(
len
(
outbox
),
1
)
self
.
assertTrue
(
test_email
in
outbox
[
0
]
.
recipients
())
self
.
assertTrue
(
test_email
in
outbox
[
0
]
.
recipients
())
self
.
assertTrue
(
self
.
assertTrue
(
C
cxFutureMembership
.
objects
.
filter
(
C
ourseEnrollmentAllowed
.
objects
.
filter
(
c
cx
=
ccx
,
email
=
test_email
c
ourse_id
=
course_key
,
email
=
test_email
)
.
exists
()
)
.
exists
()
)
)
...
@@ -352,14 +347,16 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -352,14 +347,16 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""
"""
test_email
=
"nobody@nowhere.com"
test_email
=
"nobody@nowhere.com"
self
.
make_coach
()
self
.
make_coach
()
course
=
CourseFactory
.
create
()
ccx
=
self
.
make_ccx
()
ccx
=
self
.
make_ccx
()
course_key
=
CCXLocator
.
from_course_locator
(
course
.
id
,
ccx
.
id
)
outbox
=
self
.
get_outbox
()
outbox
=
self
.
get_outbox
()
C
cxFutureMembershipFactory
(
ccx
=
ccx
,
email
=
test_email
)
C
ourseEnrollmentAllowed
(
course_id
=
course_key
,
email
=
test_email
)
self
.
assertEqual
(
outbox
,
[])
self
.
assertEqual
(
outbox
,
[])
url
=
reverse
(
url
=
reverse
(
'ccx_invite'
,
'ccx_invite'
,
kwargs
=
{
'course_id'
:
CCXLocator
.
from_course_locator
(
self
.
course
.
id
,
ccx
.
id
)
}
kwargs
=
{
'course_id'
:
course_key
}
)
)
data
=
{
data
=
{
'enrollment-button'
:
'Unenroll'
,
'enrollment-button'
:
'Unenroll'
,
...
@@ -371,11 +368,9 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -371,11 +368,9 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
# we were redirected to our current location
# we were redirected to our current location
self
.
assertEqual
(
len
(
response
.
redirect_chain
),
1
)
self
.
assertEqual
(
len
(
response
.
redirect_chain
),
1
)
self
.
assertTrue
(
302
in
response
.
redirect_chain
[
0
])
self
.
assertTrue
(
302
in
response
.
redirect_chain
[
0
])
self
.
assertEqual
(
len
(
outbox
),
1
)
self
.
assertTrue
(
test_email
in
outbox
[
0
]
.
recipients
())
self
.
assertFalse
(
self
.
assertFalse
(
C
cxFutureMembership
.
objects
.
filter
(
C
ourseEnrollmentAllowed
.
objects
.
filter
(
c
cx
=
ccx
,
email
=
test_email
c
ourse_id
=
course_key
,
email
=
test_email
)
.
exists
()
)
.
exists
()
)
)
...
@@ -384,7 +379,8 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -384,7 +379,8 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""
"""
self
.
make_coach
()
self
.
make_coach
()
ccx
=
self
.
make_ccx
()
ccx
=
self
.
make_ccx
()
enrollment
=
CourseEnrollmentFactory
(
course_id
=
self
.
course
.
id
)
course_key
=
CCXLocator
.
from_course_locator
(
self
.
course
.
id
,
ccx
.
id
)
enrollment
=
CourseEnrollmentFactory
(
course_id
=
course_key
)
student
=
enrollment
.
user
student
=
enrollment
.
user
# no emails have been sent so far
# no emails have been sent so far
outbox
=
self
.
get_outbox
()
outbox
=
self
.
get_outbox
()
...
@@ -392,7 +388,7 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -392,7 +388,7 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
url
=
reverse
(
url
=
reverse
(
'ccx_manage_student'
,
'ccx_manage_student'
,
kwargs
=
{
'course_id'
:
CCXLocator
.
from_course_locator
(
self
.
course
.
id
,
ccx
.
id
)
}
kwargs
=
{
'course_id'
:
course_key
}
)
)
data
=
{
data
=
{
'student-action'
:
'add'
,
'student-action'
:
'add'
,
...
@@ -406,7 +402,7 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -406,7 +402,7 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
self
.
assertEqual
(
outbox
,
[])
self
.
assertEqual
(
outbox
,
[])
# a CcxMembership exists for this student
# a CcxMembership exists for this student
self
.
assertTrue
(
self
.
assertTrue
(
C
cxMembership
.
objects
.
filter
(
ccx
=
ccx
,
student
=
student
)
.
exists
()
C
ourseEnrollment
.
objects
.
filter
(
course_id
=
course_key
,
user
=
student
)
.
exists
()
)
)
def
test_manage_remove_single_student
(
self
):
def
test_manage_remove_single_student
(
self
):
...
@@ -414,9 +410,9 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -414,9 +410,9 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""
"""
self
.
make_coach
()
self
.
make_coach
()
ccx
=
self
.
make_ccx
()
ccx
=
self
.
make_ccx
()
enrollment
=
CourseEnrollmentFactory
(
course_id
=
self
.
course
.
id
)
course_key
=
CCXLocator
.
from_course_locator
(
self
.
course
.
id
,
ccx
.
id
)
enrollment
=
CourseEnrollmentFactory
(
course_id
=
course_key
)
student
=
enrollment
.
user
student
=
enrollment
.
user
CcxMembershipFactory
(
ccx
=
ccx
,
student
=
student
)
# no emails have been sent so far
# no emails have been sent so far
outbox
=
self
.
get_outbox
()
outbox
=
self
.
get_outbox
()
self
.
assertEqual
(
outbox
,
[])
self
.
assertEqual
(
outbox
,
[])
...
@@ -435,10 +431,6 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -435,10 +431,6 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
self
.
assertEqual
(
len
(
response
.
redirect_chain
),
1
)
self
.
assertEqual
(
len
(
response
.
redirect_chain
),
1
)
self
.
assertTrue
(
302
in
response
.
redirect_chain
[
0
])
self
.
assertTrue
(
302
in
response
.
redirect_chain
[
0
])
self
.
assertEqual
(
outbox
,
[])
self
.
assertEqual
(
outbox
,
[])
# a CcxMembership exists for this student
self
.
assertFalse
(
CcxMembership
.
objects
.
filter
(
ccx
=
ccx
,
student
=
student
)
.
exists
()
)
GET_CHILDREN
=
XModuleMixin
.
get_children
GET_CHILDREN
=
XModuleMixin
.
get_children
...
@@ -525,7 +517,6 @@ class TestCCXGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -525,7 +517,6 @@ class TestCCXGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
self
.
student
=
student
=
UserFactory
.
create
()
self
.
student
=
student
=
UserFactory
.
create
()
CourseEnrollmentFactory
.
create
(
user
=
student
,
course_id
=
self
.
course
.
id
)
CourseEnrollmentFactory
.
create
(
user
=
student
,
course_id
=
self
.
course
.
id
)
CcxMembershipFactory
(
ccx
=
ccx
,
student
=
student
,
active
=
True
)
# create grades for self.student as if they'd submitted the ccx
# create grades for self.student as if they'd submitted the ccx
for
chapter
in
self
.
course
.
get_children
():
for
chapter
in
self
.
course
.
get_children
():
...
@@ -674,12 +665,14 @@ class TestStudentDashboardWithCCX(ModuleStoreTestCase):
...
@@ -674,12 +665,14 @@ class TestStudentDashboardWithCCX(ModuleStoreTestCase):
self
.
ccx
=
CcxFactory
(
course_id
=
self
.
split_course
.
id
,
coach
=
self
.
coach
)
self
.
ccx
=
CcxFactory
(
course_id
=
self
.
split_course
.
id
,
coach
=
self
.
coach
)
last_week
=
datetime
.
datetime
.
now
(
UTC
())
-
datetime
.
timedelta
(
days
=
7
)
last_week
=
datetime
.
datetime
.
now
(
UTC
())
-
datetime
.
timedelta
(
days
=
7
)
override_field_for_ccx
(
self
.
ccx
,
self
.
split_course
,
'start'
,
last_week
)
# Required by self.ccx.has_started().
override_field_for_ccx
(
self
.
ccx
,
self
.
split_course
,
'start'
,
last_week
)
# Required by self.ccx.has_started().
CcxMembershipFactory
(
ccx
=
self
.
ccx
,
student
=
self
.
student
,
active
=
True
)
course_key
=
CCXLocator
.
from_course_locator
(
self
.
split_course
.
id
,
self
.
ccx
.
id
)
CourseEnrollment
.
enroll
(
self
.
student
,
course_key
)
def
test_load_student_dashboard
(
self
):
def
test_load_student_dashboard
(
self
):
self
.
client
.
login
(
username
=
self
.
student
.
username
,
password
=
self
.
student_password
)
self
.
client
.
login
(
username
=
self
.
student
.
username
,
password
=
self
.
student_password
)
response
=
self
.
client
.
get
(
reverse
(
'dashboard'
))
response
=
self
.
client
.
get
(
reverse
(
'dashboard'
))
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertTrue
(
re
.
search
(
'Test CCX'
,
response
.
content
))
def
flatten
(
seq
):
def
flatten
(
seq
):
...
...
lms/djangoapps/ccx/utils.py
View file @
eafb0755
This diff is collapsed.
Click to expand it.
lms/djangoapps/ccx/views.py
View file @
eafb0755
...
@@ -36,22 +36,24 @@ from courseware.module_render import get_module_for_descriptor
...
@@ -36,22 +36,24 @@ from courseware.module_render import get_module_for_descriptor
from
edxmako.shortcuts
import
render_to_response
from
edxmako.shortcuts
import
render_to_response
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.keys
import
CourseKey
from
ccx_keys.locator
import
CCXLocator
from
ccx_keys.locator
import
CCXLocator
from
student.roles
import
CourseCcxCoachRole
from
student.roles
import
CourseCcxCoachRole
# pylint: disable=import-error
from
student.models
import
CourseEnrollment
from
instructor.offline_gradecalc
import
student_grades
from
instructor.offline_gradecalc
import
student_grades
# pylint: disable=import-error
from
instructor.views.api
import
_split_input_list
from
instructor.views.api
import
_split_input_list
# pylint: disable=import-error
from
instructor.views.tools
import
get_student_from_identifier
from
instructor.views.tools
import
get_student_from_identifier
# pylint: disable=import-error
from
instructor.enrollment
import
(
enroll_email
,
unenroll_email
,
get_email_params
,
)
from
.models
import
CustomCourseForEdX
,
CcxMembership
from
.models
import
CustomCourseForEdX
from
.overrides
import
(
from
.overrides
import
(
clear_override_for_ccx
,
clear_override_for_ccx
,
get_override_for_ccx
,
get_override_for_ccx
,
override_field_for_ccx
,
override_field_for_ccx
,
)
)
from
.utils
import
(
enroll_email
,
unenroll_email
,
)
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -127,7 +129,7 @@ def dashboard(request, course, ccx=None):
...
@@ -127,7 +129,7 @@ def dashboard(request, course, ccx=None):
context
[
'schedule'
]
=
json
.
dumps
(
schedule
,
indent
=
4
)
context
[
'schedule'
]
=
json
.
dumps
(
schedule
,
indent
=
4
)
context
[
'save_url'
]
=
reverse
(
context
[
'save_url'
]
=
reverse
(
'save_ccx'
,
kwargs
=
{
'course_id'
:
ccx_locator
})
'save_ccx'
,
kwargs
=
{
'course_id'
:
ccx_locator
})
context
[
'ccx_members'
]
=
C
cxMembership
.
objects
.
filter
(
ccx
=
ccx
)
context
[
'ccx_members'
]
=
C
ourseEnrollment
.
objects
.
filter
(
course_id
=
ccx_locator
)
context
[
'gradebook_url'
]
=
reverse
(
context
[
'gradebook_url'
]
=
reverse
(
'ccx_gradebook'
,
kwargs
=
{
'course_id'
:
ccx_locator
})
'ccx_gradebook'
,
kwargs
=
{
'course_id'
:
ccx_locator
})
context
[
'grades_csv_url'
]
=
reverse
(
context
[
'grades_csv_url'
]
=
reverse
(
...
@@ -156,7 +158,7 @@ def create_ccx(request, course, ccx=None):
...
@@ -156,7 +158,7 @@ def create_ccx(request, course, ccx=None):
"You cannot create a CCX from a course using a deprecated id. "
"You cannot create a CCX from a course using a deprecated id. "
"Please create a rerun of this course in the studio to allow "
"Please create a rerun of this course in the studio to allow "
"this action."
))
"this action."
))
url
=
reverse
(
'ccx_coach_dashboard'
,
kwargs
=
{
'course_id'
,
course
.
id
})
url
=
reverse
(
'ccx_coach_dashboard'
,
kwargs
=
{
'course_id'
:
course
.
id
})
return
redirect
(
url
)
return
redirect
(
url
)
ccx
=
CustomCourseForEdX
(
ccx
=
CustomCourseForEdX
(
...
@@ -407,15 +409,18 @@ def ccx_invite(request, course, ccx=None):
...
@@ -407,15 +409,18 @@ def ccx_invite(request, course, ccx=None):
email
=
user
.
email
email
=
user
.
email
try
:
try
:
validate_email
(
email
)
validate_email
(
email
)
course_key
=
CCXLocator
.
from_course_locator
(
course
.
id
,
ccx
.
id
)
email_params
=
get_email_params
(
course
,
auto_enroll
)
if
action
==
'Enroll'
:
if
action
==
'Enroll'
:
enroll_email
(
enroll_email
(
c
cx
,
c
ourse_key
,
email
,
email
,
auto_enroll
=
auto_enroll
,
auto_enroll
=
auto_enroll
,
email_students
=
email_students
email_students
=
email_students
,
email_params
=
email_params
)
)
if
action
==
"Unenroll"
:
if
action
==
"Unenroll"
:
unenroll_email
(
c
cx
,
email
,
email_students
=
email_student
s
)
unenroll_email
(
c
ourse_key
,
email
,
email_students
=
email_students
,
email_params
=
email_param
s
)
except
ValidationError
:
except
ValidationError
:
log
.
info
(
'Invalid user name or email when trying to invite students:
%
s'
,
email
)
log
.
info
(
'Invalid user name or email when trying to invite students:
%
s'
,
email
)
url
=
reverse
(
url
=
reverse
(
...
@@ -444,20 +449,21 @@ def ccx_student_management(request, course, ccx=None):
...
@@ -444,20 +449,21 @@ def ccx_student_management(request, course, ccx=None):
else
:
else
:
email
=
user
.
email
email
=
user
.
email
course_key
=
CCXLocator
.
from_course_locator
(
course
.
id
,
ccx
.
id
)
try
:
try
:
validate_email
(
email
)
validate_email
(
email
)
if
action
==
'add'
:
if
action
==
'add'
:
# by decree, no emails sent to students added this way
# by decree, no emails sent to students added this way
# by decree, any students added this way are auto_enrolled
# by decree, any students added this way are auto_enrolled
enroll_email
(
c
cx
,
email
,
auto_enroll
=
True
,
email_students
=
False
)
enroll_email
(
c
ourse_key
,
email
,
auto_enroll
=
True
,
email_students
=
False
)
elif
action
==
'revoke'
:
elif
action
==
'revoke'
:
unenroll_email
(
c
cx
,
email
,
email_students
=
False
)
unenroll_email
(
c
ourse_key
,
email
,
email_students
=
False
)
except
ValidationError
:
except
ValidationError
:
log
.
info
(
'Invalid user name or email when trying to enroll student:
%
s'
,
email
)
log
.
info
(
'Invalid user name or email when trying to enroll student:
%
s'
,
email
)
url
=
reverse
(
url
=
reverse
(
'ccx_coach_dashboard'
,
'ccx_coach_dashboard'
,
kwargs
=
{
'course_id'
:
CCXLocator
.
from_course_locator
(
course
.
id
,
ccx
.
id
)
}
kwargs
=
{
'course_id'
:
course_key
}
)
)
return
redirect
(
url
)
return
redirect
(
url
)
...
@@ -496,8 +502,8 @@ def ccx_gradebook(request, course, ccx=None):
...
@@ -496,8 +502,8 @@ def ccx_gradebook(request, course, ccx=None):
prep_course_for_grading
(
course
,
request
)
prep_course_for_grading
(
course
,
request
)
enrolled_students
=
User
.
objects
.
filter
(
enrolled_students
=
User
.
objects
.
filter
(
c
cxmembership__ccx
=
ccx
,
c
ourseenrollment__course_id
=
ccx_key
,
c
cxmembership_
_active
=
1
c
ourseenrollment__is
_active
=
1
)
.
order_by
(
'username'
)
.
select_related
(
"profile"
)
)
.
order_by
(
'username'
)
.
select_related
(
"profile"
)
student_info
=
[
student_info
=
[
...
@@ -535,8 +541,8 @@ def ccx_grades_csv(request, course, ccx=None):
...
@@ -535,8 +541,8 @@ def ccx_grades_csv(request, course, ccx=None):
prep_course_for_grading
(
course
,
request
)
prep_course_for_grading
(
course
,
request
)
enrolled_students
=
User
.
objects
.
filter
(
enrolled_students
=
User
.
objects
.
filter
(
c
cxmembership__ccx
=
ccx
,
c
ourseenrollment__course_id
=
ccx_key
,
c
cxmembership_
_active
=
1
c
ourseenrollment__is
_active
=
1
)
.
order_by
(
'username'
)
.
select_related
(
"profile"
)
)
.
order_by
(
'username'
)
.
select_related
(
"profile"
)
grades
=
iterate_grades_for
(
course
,
enrolled_students
)
grades
=
iterate_grades_for
(
course
,
enrolled_students
)
...
...
lms/djangoapps/django_comment_client/utils.py
View file @
eafb0755
...
@@ -87,7 +87,7 @@ def has_forum_access(uname, course_id, rolename):
...
@@ -87,7 +87,7 @@ def has_forum_access(uname, course_id, rolename):
def
has_required_keys
(
module
):
def
has_required_keys
(
module
):
"""Returns True iff module has the proper attributes for generating metadata with get_discussion_id_map_entry()"""
"""Returns True iff module has the proper attributes for generating metadata with get_discussion_id_map_entry()"""
for
key
in
(
'discussion_id'
,
'discussion_category'
,
'discussion_target'
):
for
key
in
(
'discussion_id'
,
'discussion_category'
,
'discussion_target'
):
if
not
getattr
(
module
,
key
,
None
)
:
if
getattr
(
module
,
key
,
None
)
is
None
:
log
.
debug
(
"Required key '
%
s' not in discussion
%
s, leaving out of category map"
,
key
,
module
.
location
)
log
.
debug
(
"Required key '
%
s' not in discussion
%
s, leaving out of category map"
,
key
,
module
.
location
)
return
False
return
False
return
True
return
True
...
...
lms/djangoapps/mobile_api/users/tests.py
View file @
eafb0755
...
@@ -146,9 +146,9 @@ class TestUserEnrollmentApi(MobileAPITestCase, MobileAuthUserTestMixin):
...
@@ -146,9 +146,9 @@ class TestUserEnrollmentApi(MobileAPITestCase, MobileAuthUserTestMixin):
@ddt.data
(
@ddt.data
(
(
NEXT_WEEK
,
ADVERTISED_START
,
ADVERTISED_START
,
"string"
),
(
NEXT_WEEK
,
ADVERTISED_START
,
ADVERTISED_START
,
"string"
),
(
NEXT_WEEK
,
None
,
''
,
"string
"
),
(
NEXT_WEEK
,
None
,
defaultfilters
.
date
(
NEXT_WEEK
,
"DATE_FORMAT"
),
"timestamp
"
),
(
DEFAULT_START_DATE
,
ADVERTISED_START
,
ADVERTISED_START
,
"string"
),
(
DEFAULT_START_DATE
,
ADVERTISED_START
,
ADVERTISED_START
,
"string"
),
(
DEFAULT_START_DATE
,
None
,
''
,
"string
"
)
(
DEFAULT_START_DATE
,
None
,
None
,
"empty
"
)
)
)
@ddt.unpack
@ddt.unpack
@patch.dict
(
'django.conf.settings.FEATURES'
,
{
'DISABLE_START_DATES'
:
False
})
@patch.dict
(
'django.conf.settings.FEATURES'
,
{
'DISABLE_START_DATES'
:
False
})
...
...
lms/djangoapps/oauth2_handler/handlers.py
View file @
eafb0755
...
@@ -4,11 +4,12 @@ from django.conf import settings
...
@@ -4,11 +4,12 @@ from django.conf import settings
from
django.core.cache
import
cache
from
django.core.cache
import
cache
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
from
courseware.access
import
has_access
from
openedx.core.djangoapps.user_api.models
import
UserPreference
from
openedx.core.djangoapps.user_api.models
import
UserPreference
from
student.models
import
anonymous_id_for_user
from
student.models
import
anonymous_id_for_user
from
student.models
import
UserProfile
from
student.models
import
UserProfile
from
lang_pref
import
LANGUAGE_KEY
from
lang_pref
import
LANGUAGE_KEY
from
student.roles
import
(
GlobalStaff
,
CourseStaffRole
,
CourseInstructorRole
,
UserBasedRole
)
from
student.roles
import
GlobalStaff
,
CourseStaffRole
,
CourseInstructorRole
class
OpenIDHandler
(
object
):
class
OpenIDHandler
(
object
):
...
@@ -189,31 +190,23 @@ class CourseAccessHandler(object):
...
@@ -189,31 +190,23 @@ class CourseAccessHandler(object):
return
course_ids
return
course_ids
# pylint: disable=missing-docstring
def
_get_courses_with_access_type
(
self
,
user
,
access_type
):
def
_get_courses_with_access_type
(
self
,
user
,
access_type
):
"""
If global staff, returns all courses. Otherwise, returns list of courses
based on role access (e.g. courses that course staff has access to).
"""
# Check the application cache and update if not present. The application
# Check the application cache and update if not present. The application
# cache is useful since there are calls to different endpoints in close
# cache is useful since there are calls to different endpoints in close
# succession, for example the id_token and user_info endpoints.
# succession, for example the id_token and user_info endpoints.
key
=
'-'
.
join
([
str
(
self
.
__class__
),
str
(
user
.
id
),
access_type
])
key
=
'-'
.
join
([
str
(
self
.
__class__
),
str
(
user
.
id
),
access_type
])
course_ids
=
cache
.
get
(
key
)
course_ids
=
cache
.
get
(
key
)
if
not
course_ids
:
if
not
course_ids
:
if
GlobalStaff
()
.
has_user
(
user
):
# TODO: This code should be optimized in the future to caching
# the list of all courses in the system.
# The modulestore has all courses, while the roles table only has courses
# with roles. Thus, we'll use the modulestore to fetch all courses.
courses
=
_get_all_courses
()
courses
=
_get_all_courses
()
# Global staff have access to all courses. Filter courses for non-global staff.
if
not
GlobalStaff
()
.
has_user
(
user
):
courses
=
[
course
for
course
in
courses
if
has_access
(
user
,
access_type
,
course
)]
course_ids
=
[
unicode
(
course
.
id
)
for
course
in
courses
]
course_ids
=
[
unicode
(
course
.
id
)
for
course
in
courses
]
else
:
# Getting courses based on roles avoid querying mongo and thus faster
courses
=
UserBasedRole
(
user
,
access_type
)
.
courses_with_role
()
course_ids
=
[
unicode
(
course
.
course_id
)
for
course
in
courses
]
cache
.
set
(
key
,
course_ids
,
self
.
COURSE_CACHE_TIMEOUT
)
cache
.
set
(
key
,
course_ids
,
self
.
COURSE_CACHE_TIMEOUT
)
...
...
lms/djangoapps/oauth2_handler/tests.py
View file @
eafb0755
...
@@ -5,10 +5,9 @@ from lang_pref import LANGUAGE_KEY
...
@@ -5,10 +5,9 @@ from lang_pref import LANGUAGE_KEY
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
xmodule.modulestore.tests.django_utils
import
TEST_DATA_MIXED_TOY_MODULESTORE
from
xmodule.modulestore.tests.django_utils
import
TEST_DATA_MIXED_TOY_MODULESTORE
from
xmodule.modulestore.tests.factories
import
check_mongo_calls
,
check_mongo_calls_range
from
student.models
import
anonymous_id_for_user
from
student.models
import
anonymous_id_for_user
from
student.models
import
UserProfile
from
student.models
import
UserProfile
from
student.roles
import
CourseStaffRole
,
CourseInstructorRole
,
GlobalStaff
from
student.roles
import
CourseStaffRole
,
CourseInstructorRole
from
student.tests.factories
import
UserFactory
,
UserProfileFactory
from
student.tests.factories
import
UserFactory
,
UserProfileFactory
from
openedx.core.djangoapps.user_api.preferences.api
import
set_user_preference
from
openedx.core.djangoapps.user_api.preferences.api
import
set_user_preference
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
...
@@ -78,7 +77,6 @@ class IDTokenTest(BaseTestMixin, IDTokenTestCase):
...
@@ -78,7 +77,6 @@ class IDTokenTest(BaseTestMixin, IDTokenTestCase):
self
.
assertEqual
(
language
,
locale
)
self
.
assertEqual
(
language
,
locale
)
def
test_no_special_course_access
(
self
):
def
test_no_special_course_access
(
self
):
with
check_mongo_calls
(
0
):
scopes
,
claims
=
self
.
get_id_token_values
(
'openid course_instructor course_staff'
)
scopes
,
claims
=
self
.
get_id_token_values
(
'openid course_instructor course_staff'
)
self
.
assertNotIn
(
'course_staff'
,
scopes
)
self
.
assertNotIn
(
'course_staff'
,
scopes
)
self
.
assertNotIn
(
'staff_courses'
,
claims
)
self
.
assertNotIn
(
'staff_courses'
,
claims
)
...
@@ -88,15 +86,17 @@ class IDTokenTest(BaseTestMixin, IDTokenTestCase):
...
@@ -88,15 +86,17 @@ class IDTokenTest(BaseTestMixin, IDTokenTestCase):
def
test_course_staff_courses
(
self
):
def
test_course_staff_courses
(
self
):
CourseStaffRole
(
self
.
course_key
)
.
add_users
(
self
.
user
)
CourseStaffRole
(
self
.
course_key
)
.
add_users
(
self
.
user
)
with
check_mongo_calls
(
0
):
scopes
,
claims
=
self
.
get_id_token_values
(
'openid course_staff'
)
scopes
,
claims
=
self
.
get_id_token_values
(
'openid course_staff'
)
self
.
assertIn
(
'course_staff'
,
scopes
)
self
.
assertIn
(
'course_staff'
,
scopes
)
self
.
assertNotIn
(
'staff_courses'
,
claims
)
# should not return courses in id_token
self
.
assertNotIn
(
'staff_courses'
,
claims
)
# should not return courses in id_token
def
test_course_instructor_courses
(
self
):
def
test_course_instructor_courses
(
self
):
CourseInstructorRole
(
self
.
course_key
)
.
add_users
(
self
.
user
)
CourseInstructorRole
(
self
.
course_key
)
.
add_users
(
self
.
user
)
with
check_mongo_calls
(
0
):
scopes
,
claims
=
self
.
get_id_token_values
(
'openid course_instructor'
)
scopes
,
claims
=
self
.
get_id_token_values
(
'openid course_instructor'
)
self
.
assertIn
(
'course_instructor'
,
scopes
)
self
.
assertIn
(
'course_instructor'
,
scopes
)
self
.
assertNotIn
(
'instructor_courses'
,
claims
)
# should not return courses in id_token
self
.
assertNotIn
(
'instructor_courses'
,
claims
)
# should not return courses in id_token
...
@@ -113,7 +113,6 @@ class IDTokenTest(BaseTestMixin, IDTokenTestCase):
...
@@ -113,7 +113,6 @@ class IDTokenTest(BaseTestMixin, IDTokenTestCase):
}
}
}
}
with
check_mongo_calls
(
0
):
scopes
,
claims
=
self
.
get_id_token_values
(
scope
=
'openid course_staff'
,
claims
=
claims
)
scopes
,
claims
=
self
.
get_id_token_values
(
scope
=
'openid course_staff'
,
claims
=
claims
)
self
.
assertIn
(
'course_staff'
,
scopes
)
self
.
assertIn
(
'course_staff'
,
scopes
)
...
@@ -134,11 +133,6 @@ class IDTokenTest(BaseTestMixin, IDTokenTestCase):
...
@@ -134,11 +133,6 @@ class IDTokenTest(BaseTestMixin, IDTokenTestCase):
class
UserInfoTest
(
BaseTestMixin
,
UserInfoTestCase
):
class
UserInfoTest
(
BaseTestMixin
,
UserInfoTestCase
):
def
setUp
(
self
):
super
(
UserInfoTest
,
self
)
.
setUp
()
# clear the course ID cache
cache
.
clear
()
def
token_for_scope
(
self
,
scope
):
def
token_for_scope
(
self
,
scope
):
full_scope
=
'openid
%
s'
%
scope
full_scope
=
'openid
%
s'
%
scope
self
.
set_access_token_scope
(
full_scope
)
self
.
set_access_token_scope
(
full_scope
)
...
@@ -164,39 +158,19 @@ class UserInfoTest(BaseTestMixin, UserInfoTestCase):
...
@@ -164,39 +158,19 @@ class UserInfoTest(BaseTestMixin, UserInfoTestCase):
self
.
assertEqual
(
result
.
status_code
,
200
)
self
.
assertEqual
(
result
.
status_code
,
200
)
return
claims
return
claims
def
test_request_global_staff_courses_using_scope
(
self
):
GlobalStaff
()
.
add_users
(
self
.
user
)
with
check_mongo_calls_range
(
min_finds
=
1
):
claims
=
self
.
get_with_scope
(
'course_staff'
)
courses
=
claims
[
'staff_courses'
]
self
.
assertIn
(
self
.
course_id
,
courses
)
self
.
assertEqual
(
len
(
courses
),
1
)
def
test_request_staff_courses_using_scope
(
self
):
def
test_request_staff_courses_using_scope
(
self
):
CourseStaffRole
(
self
.
course_key
)
.
add_users
(
self
.
user
)
CourseStaffRole
(
self
.
course_key
)
.
add_users
(
self
.
user
)
with
check_mongo_calls
(
0
):
claims
=
self
.
get_with_scope
(
'course_staff'
)
claims
=
self
.
get_with_scope
(
'course_staff'
)
courses
=
claims
[
'staff_courses'
]
courses
=
claims
[
'staff_courses'
]
self
.
assertIn
(
self
.
course_id
,
courses
)
self
.
assertIn
(
self
.
course_id
,
courses
)
self
.
assertEqual
(
len
(
courses
),
1
)
self
.
assertEqual
(
len
(
courses
),
1
)
def
test_request_instructor_courses_using_scope
(
self
):
def
test_request_instructor_courses_using_scope
(
self
):
CourseInstructorRole
(
self
.
course_key
)
.
add_users
(
self
.
user
)
CourseInstructorRole
(
self
.
course_key
)
.
add_users
(
self
.
user
)
with
check_mongo_calls
(
0
):
claims
=
self
.
get_with_scope
(
'course_instructor'
)
claims
=
self
.
get_with_scope
(
'course_instructor'
)
courses
=
claims
[
'instructor_courses'
]
self
.
assertIn
(
self
.
course_id
,
courses
)
self
.
assertEqual
(
len
(
courses
),
1
)
def
test_request_global_staff_courses_with_claims
(
self
):
GlobalStaff
()
.
add_users
(
self
.
user
)
values
=
[
self
.
course_id
,
'some_invalid_course'
]
courses
=
claims
[
'instructor_courses'
]
with
check_mongo_calls_range
(
min_finds
=
1
):
claims
=
self
.
get_with_claim_value
(
'course_staff'
,
'staff_courses'
,
values
)
self
.
assertEqual
(
len
(
claims
),
2
)
courses
=
claims
[
'staff_courses'
]
self
.
assertIn
(
self
.
course_id
,
courses
)
self
.
assertIn
(
self
.
course_id
,
courses
)
self
.
assertEqual
(
len
(
courses
),
1
)
self
.
assertEqual
(
len
(
courses
),
1
)
...
@@ -204,7 +178,6 @@ class UserInfoTest(BaseTestMixin, UserInfoTestCase):
...
@@ -204,7 +178,6 @@ class UserInfoTest(BaseTestMixin, UserInfoTestCase):
CourseStaffRole
(
self
.
course_key
)
.
add_users
(
self
.
user
)
CourseStaffRole
(
self
.
course_key
)
.
add_users
(
self
.
user
)
values
=
[
self
.
course_id
,
'some_invalid_course'
]
values
=
[
self
.
course_id
,
'some_invalid_course'
]
with
check_mongo_calls
(
0
):
claims
=
self
.
get_with_claim_value
(
'course_staff'
,
'staff_courses'
,
values
)
claims
=
self
.
get_with_claim_value
(
'course_staff'
,
'staff_courses'
,
values
)
self
.
assertEqual
(
len
(
claims
),
2
)
self
.
assertEqual
(
len
(
claims
),
2
)
...
@@ -216,7 +189,6 @@ class UserInfoTest(BaseTestMixin, UserInfoTestCase):
...
@@ -216,7 +189,6 @@ class UserInfoTest(BaseTestMixin, UserInfoTestCase):
CourseInstructorRole
(
self
.
course_key
)
.
add_users
(
self
.
user
)
CourseInstructorRole
(
self
.
course_key
)
.
add_users
(
self
.
user
)
values
=
[
'edX/toy/TT_2012_Fall'
,
self
.
course_id
,
'invalid_course_id'
]
values
=
[
'edX/toy/TT_2012_Fall'
,
self
.
course_id
,
'invalid_course_id'
]
with
check_mongo_calls
(
0
):
claims
=
self
.
get_with_claim_value
(
'course_instructor'
,
'instructor_courses'
,
values
)
claims
=
self
.
get_with_claim_value
(
'course_instructor'
,
'instructor_courses'
,
values
)
self
.
assertEqual
(
len
(
claims
),
2
)
self
.
assertEqual
(
len
(
claims
),
2
)
...
...
lms/templates/ccx/_dashboard_ccx_listing.html
deleted
100644 → 0
View file @
5b9b0a83
<
%
page
args=
"ccx, membership, course_overview, show_courseware_link, is_course_blocked"
/>
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
core
.
urlresolvers
import
reverse
from
courseware
.
courses
import
course_image_url
,
get_course_about_section
from
ccx_keys
.
locator
import
CCXLocator
%
>
<
%
ccx_target =
reverse('info',
args=
[CCXLocator.from_course_locator(course_overview.id,
ccx
.
id
)])
%
>
<li
class=
"course-item"
>
<article
class=
"course"
>
<section
class=
"details"
>
<div
class=
"wrapper-course-image"
aria-hidden=
"true"
>
% if show_courseware_link:
% if not is_course_blocked:
<a
href=
"${ccx_target}"
class=
"cover"
>
<img
src=
"${course_overview.course_image_url}"
class=
"course-image"
alt=
"${_('{course_number} {ccx_name} Cover Image').format(course_number=course_overview.number, ccx_name=ccx.display_name) |h}"
/>
</a>
% else:
<a
class=
"fade-cover"
>
<img
src=
"${course_overview.course_image_url}"
class=
"course-image"
alt=
"${_('{course_number} {ccx_name} Cover Image').format(course_number=course_overview.number, ccx_name=ccx.display_name) |h}"
/>
</a>
% endif
% else:
<a
class=
"cover"
>
<img
src=
"${course_overview.course_image_url}"
class=
"course-image"
alt=
"${_('{course_number} {ccx_name} Cover Image').format(course_number=course_overview.number, ccx_name=ccx.display_name) |h}"
/>
</a>
% endif
</div>
<div
class=
"wrapper-course-details"
>
<h3
class=
"course-title"
>
% if show_courseware_link:
% if not is_course_blocked:
<a
href=
"${ccx_target}"
>
${ccx.display_name}
</a>
% else:
<a
class=
"disable-look"
>
${ccx.display_name}
</a>
% endif
% else:
<span>
${ccx.display_name}
</span>
% endif
</h3>
<div
class=
"course-info"
>
<span
class=
"info-university"
>
${get_course_about_section(course_overview, 'university')} -
</span>
<span
class=
"info-course-id"
>
${course_overview.display_number_with_default | h}
</span>
<span
class=
"info-date-block"
data-tooltip=
"Hi"
>
% if ccx.has_ended():
${_("Ended - {end_date}").format(end_date=ccx.end_datetime_text("SHORT_DATE"))}
% elif ccx.has_started():
${_("Started - {start_date}").format(start_date=ccx.start_datetime_text("SHORT_DATE"))}
% else: # hasn't started yet
${_("Starts - {start_date}").format(start_date=ccx.start_datetime_text("SHORT_DATE"))}
% endif
</span>
</div>
% if show_courseware_link:
<div
class=
"wrapper-course-actions"
>
<div
class=
"course-actions"
>
% if ccx.has_ended():
% if not is_course_blocked:
<a
href=
"${ccx_target}"
class=
"enter-course archived"
>
${_('View Archived Custom Course')}
<span
class=
"sr"
>
${ccx.display_name}
</span></a>
% else:
<a
class=
"enter-course-blocked archived"
>
${_('View Archived Custom Course')}
<span
class=
"sr"
>
${ccx.display_name}
</span></a>
% endif
% else:
% if not is_course_blocked:
<a
href=
"${ccx_target}"
class=
"enter-course"
>
${_('View Custom Course')}
<span
class=
"sr"
>
${ccx.display_name}
</span></a>
% else:
<a
class=
"enter-course-blocked"
>
${_('View Custom Course')}
<span
class=
"sr"
>
${ccx.display_name}
</span></a>
% endif
% endif
</div>
</div>
% endif
</div>
</section>
</article>
</li>
lms/templates/ccx/enrollment.html
View file @
eafb0755
...
@@ -63,8 +63,8 @@
...
@@ -63,8 +63,8 @@
<tbody>
<tbody>
%for member in ccx_members:
%for member in ccx_members:
<tr>
<tr>
<td>
${member.
student
}
</td>
<td>
${member.
user
}
</td>
<td>
${member.
student
.email}
</td>
<td>
${member.
user
.email}
</td>
<td><div
class=
"revoke"
><i
class=
"fa fa-times-circle"
></i>
Revoke access
</div></td>
<td><div
class=
"revoke"
><i
class=
"fa fa-times-circle"
></i>
Revoke access
</div></td>
</tr>
</tr>
%endfor
%endfor
...
...
lms/templates/dashboard.html
View file @
eafb0755
...
@@ -97,14 +97,6 @@ import json
...
@@ -97,14 +97,6 @@ import json
<
%
include
file=
'dashboard/_dashboard_course_listing.html'
args=
"course_overview=enrollment.course_overview, enrollment=enrollment, show_courseware_link=show_courseware_link, cert_status=cert_status, credit_status=credit_status, show_email_settings=show_email_settings, course_mode_info=course_mode_info, show_refund_option=show_refund_option, is_paid_course=is_paid_course, is_course_blocked=is_course_blocked, verification_status=course_verification_status, course_requirements=course_requirements, dashboard_index=dashboard_index, share_settings=share_settings, user=user"
/>
<
%
include
file=
'dashboard/_dashboard_course_listing.html'
args=
"course_overview=enrollment.course_overview, enrollment=enrollment, show_courseware_link=show_courseware_link, cert_status=cert_status, credit_status=credit_status, show_email_settings=show_email_settings, course_mode_info=course_mode_info, show_refund_option=show_refund_option, is_paid_course=is_paid_course, is_course_blocked=is_course_blocked, verification_status=course_verification_status, course_requirements=course_requirements, dashboard_index=dashboard_index, share_settings=share_settings, user=user"
/>
% endfor
% endfor
% if settings.FEATURES.get('CUSTOM_COURSES_EDX', False):
% for ccx, membership, course_overview in ccx_membership_triplets:
<
%
show_courseware_link =
ccx.has_started()
%
>
<
%
is_course_blocked =
False
%
>
<
%
include
file=
'ccx/_dashboard_ccx_listing.html'
args=
"ccx=ccx, membership=membership, course_overview=course_overview, show_courseware_link=show_courseware_link, is_course_blocked=is_course_blocked"
/>
% endfor
% endif
</ul>
</ul>
% else:
% else:
<section
class=
"empty-dashboard-message"
>
<section
class=
"empty-dashboard-message"
>
...
...
openedx/core/djangoapps/content/course_overviews/models.py
View file @
eafb0755
...
@@ -16,6 +16,8 @@ from xmodule.error_module import ErrorDescriptor
...
@@ -16,6 +16,8 @@ from xmodule.error_module import ErrorDescriptor
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
from
xmodule_django.models
import
CourseKeyField
,
UsageKeyField
from
xmodule_django.models
import
CourseKeyField
,
UsageKeyField
from
ccx_keys.locator
import
CCXLocator
class
CourseOverview
(
TimeStampedModel
):
class
CourseOverview
(
TimeStampedModel
):
"""
"""
...
@@ -101,17 +103,26 @@ class CourseOverview(TimeStampedModel):
...
@@ -101,17 +103,26 @@ class CourseOverview(TimeStampedModel):
except
ValueError
:
except
ValueError
:
lowest_passing_grade
=
None
lowest_passing_grade
=
None
display_name
=
course
.
display_name
start
=
course
.
start
end
=
course
.
end
if
isinstance
(
course
.
id
,
CCXLocator
):
from
ccx.utils
import
get_ccx_from_ccx_locator
# pylint: disable=import-error
ccx
=
get_ccx_from_ccx_locator
(
course
.
id
)
display_name
=
ccx
.
display_name
start
=
ccx
.
start
end
=
ccx
.
due
return
cls
(
return
cls
(
version
=
cls
.
VERSION
,
version
=
cls
.
VERSION
,
id
=
course
.
id
,
id
=
course
.
id
,
_location
=
course
.
location
,
_location
=
course
.
location
,
display_name
=
course
.
display_name
,
display_name
=
display_name
,
display_number_with_default
=
course
.
display_number_with_default
,
display_number_with_default
=
course
.
display_number_with_default
,
display_org_with_default
=
course
.
display_org_with_default
,
display_org_with_default
=
course
.
display_org_with_default
,
start
=
course
.
start
,
start
=
start
,
end
=
course
.
end
,
end
=
end
,
advertised_start
=
course
.
advertised_start
,
advertised_start
=
course
.
advertised_start
,
course_image_url
=
course_image_url
(
course
),
course_image_url
=
course_image_url
(
course
),
...
...
openedx/core/djangoapps/content/course_overviews/tests.py
View file @
eafb0755
...
@@ -191,7 +191,7 @@ class CourseOverviewTestCase(ModuleStoreTestCase):
...
@@ -191,7 +191,7 @@ class CourseOverviewTestCase(ModuleStoreTestCase):
"display_name"
:
""
,
# Empty display name
"display_name"
:
""
,
# Empty display name
"start"
:
LAST_MONTH
,
# Course already ended
"start"
:
LAST_MONTH
,
# Course already ended
"end"
:
LAST_WEEK
,
"end"
:
LAST_WEEK
,
"advertised_start"
:
''
,
# No advertised start
"advertised_start"
:
None
,
# No advertised start
"pre_requisite_courses"
:
[],
# No pre-requisites
"pre_requisite_courses"
:
[],
# No pre-requisites
"static_asset_path"
:
""
,
# Empty asset path
"static_asset_path"
:
""
,
# Empty asset path
"certificates_show_before_end"
:
False
,
"certificates_show_before_end"
:
False
,
...
@@ -200,7 +200,7 @@ class CourseOverviewTestCase(ModuleStoreTestCase):
...
@@ -200,7 +200,7 @@ class CourseOverviewTestCase(ModuleStoreTestCase):
# # Don't set display name
# # Don't set display name
"start"
:
DEFAULT_START_DATE
,
# Default start and end dates
"start"
:
DEFAULT_START_DATE
,
# Default start and end dates
"end"
:
None
,
"end"
:
None
,
"advertised_start"
:
''
,
# No advertised start
"advertised_start"
:
None
,
# No advertised start
"pre_requisite_courses"
:
[],
# No pre-requisites
"pre_requisite_courses"
:
[],
# No pre-requisites
"static_asset_path"
:
None
,
# No asset path
"static_asset_path"
:
None
,
# No asset path
"certificates_show_before_end"
:
False
,
"certificates_show_before_end"
:
False
,
...
...
requirements/edx/github.txt
View file @
eafb0755
...
@@ -34,7 +34,7 @@ git+https://github.com/hmarr/django-debug-toolbar-mongo.git@b0686a76f1ce3532088c
...
@@ -34,7 +34,7 @@ git+https://github.com/hmarr/django-debug-toolbar-mongo.git@b0686a76f1ce3532088c
git+https://github.com/edx/rfc6266.git@v0.0.5-edx#egg=rfc6266==0.0.5-edx
git+https://github.com/edx/rfc6266.git@v0.0.5-edx#egg=rfc6266==0.0.5-edx
# Our libraries:
# Our libraries:
-e git+https://github.com/edx/XBlock.git@
0d3d43bd1ef0f882ef70d19d4f652d35dd37eb01
#egg=XBlock
-e git+https://github.com/edx/XBlock.git@
d1ff8cf31a9b94916ce06ba06d4176bd72e15768
#egg=XBlock
-e git+https://github.com/edx/codejail.git@6b17c33a89bef0ac510926b1d7fea2748b73aadd#egg=codejail
-e git+https://github.com/edx/codejail.git@6b17c33a89bef0ac510926b1d7fea2748b73aadd#egg=codejail
-e git+https://github.com/edx/js-test-tool.git@v0.1.6#egg=js_test_tool
-e git+https://github.com/edx/js-test-tool.git@v0.1.6#egg=js_test_tool
-e git+https://github.com/edx/event-tracking.git@0.2.0#egg=event-tracking
-e git+https://github.com/edx/event-tracking.git@0.2.0#egg=event-tracking
...
...
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