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
9732f151
Commit
9732f151
authored
Aug 25, 2014
by
Andy Armstrong
Committed by
Jonathan Piacenti
Aug 20, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement forum cohort visibility labels correctly
TNL-25
parent
b9e5b0d5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
162 additions
and
13 deletions
+162
-13
lms/djangoapps/django_comment_client/base/views.py
+3
-1
lms/djangoapps/django_comment_client/forum/tests.py
+86
-7
lms/djangoapps/django_comment_client/forum/views.py
+32
-5
lms/djangoapps/django_comment_client/utils.py
+31
-0
openedx/core/djangoapps/course_groups/cohorts.py
+10
-0
No files found.
lms/djangoapps/django_comment_client/base/views.py
View file @
9732f151
...
@@ -37,6 +37,7 @@ from django_comment_client.utils import (
...
@@ -37,6 +37,7 @@ from django_comment_client.utils import (
get_group_id_for_comments_service
,
get_group_id_for_comments_service
,
get_discussion_categories_ids
,
get_discussion_categories_ids
,
get_discussion_id_map
,
get_discussion_id_map
,
add_thread_group_name
)
)
from
django_comment_client.permissions
import
check_permissions_by_view
,
has_permission
from
django_comment_client.permissions
import
check_permissions_by_view
,
has_permission
from
eventtracking
import
tracker
from
eventtracking
import
tracker
...
@@ -260,7 +261,8 @@ def create_thread(request, course_id, commentable_id):
...
@@ -260,7 +261,8 @@ def create_thread(request, course_id, commentable_id):
_update_user_engagement_score
(
course_key
,
request
.
user
.
id
)
_update_user_engagement_score
(
course_key
,
request
.
user
.
id
)
add_courseware_context
([
data
],
course
,
user
)
add_courseware_context
([
data
],
course
,
user
)
add_thread_group_name
(
data
,
course_key
)
add_courseware_context
([
data
],
course
)
if
request
.
is_ajax
():
if
request
.
is_ajax
():
return
ajax_content_response
(
request
,
course_key
,
data
)
return
ajax_content_response
(
request
,
course_key
,
data
)
else
:
else
:
...
...
lms/djangoapps/django_comment_client/forum/tests.py
View file @
9732f151
...
@@ -105,7 +105,9 @@ class ViewsExceptionTestCase(UrlResetMixin, ModuleStoreTestCase):
...
@@ -105,7 +105,9 @@ class ViewsExceptionTestCase(UrlResetMixin, ModuleStoreTestCase):
self
.
assertEqual
(
self
.
response
.
status_code
,
404
)
self
.
assertEqual
(
self
.
response
.
status_code
,
404
)
def
make_mock_thread_data
(
course
,
text
,
thread_id
,
num_children
,
group_id
=
None
,
group_name
=
None
,
commentable_id
=
None
):
def
make_mock_thread_data
(
course
,
text
,
thread_id
,
num_children
,
include_children
,
group_id
=
None
,
group_name
=
None
,
commentable_id
=
None
):
thread_data
=
{
thread_data
=
{
"id"
:
thread_id
,
"id"
:
thread_id
,
"type"
:
"thread"
,
"type"
:
"thread"
,
...
@@ -122,6 +124,9 @@ def make_mock_thread_data(course, text, thread_id, num_children, group_id=None,
...
@@ -122,6 +124,9 @@ def make_mock_thread_data(course, text, thread_id, num_children, group_id=None,
if
group_id
is
not
None
:
if
group_id
is
not
None
:
thread_data
[
'group_name'
]
=
group_name
thread_data
[
'group_name'
]
=
group_name
if
num_children
is
not
None
:
if
num_children
is
not
None
:
thread_data
[
'group_id'
]
=
group_id
thread_data
[
'group_name'
]
=
group_name
if
include_children
:
thread_data
[
"children"
]
=
[{
thread_data
[
"children"
]
=
[{
"id"
:
"dummy_comment_id_{}"
.
format
(
i
),
"id"
:
"dummy_comment_id_{}"
.
format
(
i
),
"type"
:
"comment"
,
"type"
:
"comment"
,
...
@@ -150,14 +155,17 @@ def make_mock_request_impl(
...
@@ -150,14 +155,17 @@ def make_mock_request_impl(
thread_id
=
thread_id
,
thread_id
=
thread_id
,
num_children
=
None
,
num_children
=
None
,
group_id
=
group_id
,
group_id
=
group_id
,
commentable_id
=
commentable_id
commentable_id
=
commentable_id
,
include_children
=
False
)
)
]
]
}
}
elif
thread_id
and
url
.
endswith
(
thread_id
):
elif
thread_id
and
url
.
endswith
(
thread_id
):
data
=
make_mock_thread_data
(
data
=
{
course
=
course
,
text
=
text
,
thread_id
=
thread_id
,
num_children
=
num_thread_responses
,
group_id
=
group_id
"collection"
:
[
make_mock_thread_data
(
course
=
course
,
text
=
text
,
thread_id
=
thread_id
,
)
num_children
=
num_thread_responses
,
include_children
=
False
,
group_id
=
group_id
)]
}
elif
"/users/"
in
url
:
elif
"/users/"
in
url
:
data
=
{
data
=
{
"default_sort_key"
:
"date"
,
"default_sort_key"
:
"date"
,
...
@@ -228,7 +236,9 @@ class SingleThreadTestCase(ModuleStoreTestCase):
...
@@ -228,7 +236,9 @@ class SingleThreadTestCase(ModuleStoreTestCase):
# django view performs prior to writing thread data to the response
# django view performs prior to writing thread data to the response
self
.
assertEquals
(
self
.
assertEquals
(
response_data
[
"content"
],
response_data
[
"content"
],
strip_none
(
make_mock_thread_data
(
course
=
self
.
course
,
text
=
text
,
thread_id
=
thread_id
,
num_children
=
1
))
strip_none
(
make_mock_thread_data
(
course
=
self
.
course
,
text
=
text
,
thread_id
=
thread_id
,
num_children
=
1
,
include_children
=
False
))
)
)
mock_request
.
assert_called_with
(
mock_request
.
assert_called_with
(
"get"
,
"get"
,
...
@@ -408,7 +418,8 @@ class SingleCohortedThreadTestCase(CohortedTestCase):
...
@@ -408,7 +418,8 @@ class SingleCohortedThreadTestCase(CohortedTestCase):
thread_id
=
self
.
mock_thread_id
,
thread_id
=
self
.
mock_thread_id
,
num_children
=
1
,
num_children
=
1
,
group_id
=
self
.
student_cohort
.
id
,
group_id
=
self
.
student_cohort
.
id
,
group_name
=
self
.
student_cohort
.
name
group_name
=
self
.
student_cohort
.
name
,
include_children
=
False
,
)
)
)
)
...
@@ -990,6 +1001,74 @@ class InlineDiscussionTestCase(ModuleStoreTestCase):
...
@@ -990,6 +1001,74 @@ class InlineDiscussionTestCase(ModuleStoreTestCase):
@patch
(
'requests.request'
)
@patch
(
'requests.request'
)
class
SingleCohortedThreadTestCase
(
ModuleStoreTestCase
):
def
setUp
(
self
):
self
.
course
=
CourseFactory
.
create
()
self
.
student
=
UserFactory
.
create
()
CourseEnrollmentFactory
.
create
(
user
=
self
.
student
,
course_id
=
self
.
course
.
id
)
self
.
student_cohort
=
CourseUserGroup
.
objects
.
create
(
name
=
"student_cohort"
,
course_id
=
self
.
course
.
id
,
group_type
=
CourseUserGroup
.
COHORT
)
def
_create_mock_cohorted_thread
(
self
,
mock_request
):
self
.
mock_text
=
"dummy content"
self
.
mock_thread_id
=
"test_thread_id"
mock_request
.
side_effect
=
make_mock_request_impl
(
self
.
mock_text
,
self
.
mock_thread_id
,
group_id
=
self
.
student_cohort
.
id
)
def
test_ajax
(
self
,
mock_request
):
self
.
_create_mock_cohorted_thread
(
mock_request
)
request
=
RequestFactory
()
.
get
(
"dummy_url"
,
HTTP_X_REQUESTED_WITH
=
"XMLHttpRequest"
)
request
.
user
=
self
.
student
response
=
views
.
single_thread
(
request
,
self
.
course
.
id
.
to_deprecated_string
(),
"dummy_discussion_id"
,
self
.
mock_thread_id
)
self
.
assertEquals
(
response
.
status_code
,
200
)
response_data
=
json
.
loads
(
response
.
content
)
self
.
assertEquals
(
response_data
[
"content"
],
make_mock_thread_data
(
self
.
course
,
self
.
mock_text
,
self
.
mock_thread_id
,
1
,
True
,
group_id
=
self
.
student_cohort
.
id
,
group_name
=
self
.
student_cohort
.
name
,
)
)
def
test_html
(
self
,
mock_request
):
self
.
_create_mock_cohorted_thread
(
mock_request
)
request
=
RequestFactory
()
.
get
(
"dummy_url"
)
request
.
user
=
self
.
student
mako_middleware_process_request
(
request
)
response
=
views
.
single_thread
(
request
,
self
.
course
.
id
.
to_deprecated_string
(),
"dummy_discussion_id"
,
self
.
mock_thread_id
)
self
.
assertEquals
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
[
'Content-Type'
],
'text/html; charset=utf-8'
)
html
=
response
.
content
# Verify that the group name is correctly included in the HTML
self
.
assertRegexpMatches
(
html
,
r'"group_name": "student_cohort"'
)
@patch
(
'requests.request'
)
class
UserProfileTestCase
(
ModuleStoreTestCase
):
class
UserProfileTestCase
(
ModuleStoreTestCase
):
TEST_THREAD_TEXT
=
'userprofile-test-text'
TEST_THREAD_TEXT
=
'userprofile-test-text'
...
...
lms/djangoapps/django_comment_client/forum/views.py
View file @
9732f151
...
@@ -24,6 +24,7 @@ from openedx.core.djangoapps.course_groups.cohorts import (
...
@@ -24,6 +24,7 @@ from openedx.core.djangoapps.course_groups.cohorts import (
get_cohort_id
,
get_cohort_id
,
get_course_cohorts
,
get_course_cohorts
,
is_commentable_cohorted
,
is_commentable_cohorted
,
get_cohorted_threads_privacy
,
get_cohorted_commentables
)
get_cohorted_commentables
)
from
courseware.tabs
import
EnrolledTab
from
courseware.tabs
import
EnrolledTab
from
courseware.access
import
has_access
from
courseware.access
import
has_access
...
@@ -38,6 +39,8 @@ from django_comment_client.utils import (
...
@@ -38,6 +39,8 @@ from django_comment_client.utils import (
add_courseware_context
,
add_courseware_context
,
get_group_id_for_comments_service
get_group_id_for_comments_service
)
)
from
django_comment_client.utils
import
(
merge_dict
,
extract
,
strip_none
,
add_courseware_context
,
add_thread_group_name
)
import
django_comment_client.utils
as
utils
import
django_comment_client.utils
as
utils
import
lms.lib.comment_client
as
cc
import
lms.lib.comment_client
as
cc
...
@@ -110,6 +113,7 @@ def get_threads(request, course, discussion_id=None, per_page=THREADS_PER_PAGE):
...
@@ -110,6 +113,7 @@ def get_threads(request, course, discussion_id=None, per_page=THREADS_PER_PAGE):
'sort_order'
:
'desc'
,
'sort_order'
:
'desc'
,
'text'
:
''
,
'text'
:
''
,
'course_id'
:
unicode
(
course
.
id
),
'course_id'
:
unicode
(
course
.
id
),
'commentable_id'
:
discussion_id
,
'user_id'
:
request
.
user
.
id
,
'user_id'
:
request
.
user
.
id
,
'group_id'
:
get_group_id_for_comments_service
(
request
,
course
.
id
,
discussion_id
),
# may raise ValueError
'group_id'
:
get_group_id_for_comments_service
(
request
,
course
.
id
,
discussion_id
),
# may raise ValueError
}
}
...
@@ -135,6 +139,24 @@ def get_threads(request, course, discussion_id=None, per_page=THREADS_PER_PAGE):
...
@@ -135,6 +139,24 @@ def get_threads(request, course, discussion_id=None, per_page=THREADS_PER_PAGE):
#is user a moderator
#is user a moderator
#did the user request a group
#did the user request a group
#if the user requested a group explicitly, give them that group, otherwise, if mod, show all, else if student, use cohort
group_id
=
request
.
GET
.
get
(
'group_id'
)
if
group_id
==
"all"
:
group_id
=
None
if
not
group_id
:
if
not
has_permission
(
request
.
user
,
"see_all_cohorts"
,
course
.
id
):
group_id
=
get_cohort_id
(
request
.
user
,
course
.
id
)
if
not
group_id
and
get_cohorted_threads_privacy
(
course
.
id
)
==
'cohort-only'
:
default_query_params
[
'exclude_groups'
]
=
True
if
group_id
:
default_query_params
[
"group_id"
]
=
group_id
#so by default, a moderator sees all items, and a student sees his cohort
query_params
=
merge_dict
(
query_params
=
merge_dict
(
default_query_params
,
default_query_params
,
strip_none
(
strip_none
(
...
@@ -307,6 +329,7 @@ def single_thread(request, course_key, discussion_id, thread_id):
...
@@ -307,6 +329,7 @@ def single_thread(request, course_key, discussion_id, thread_id):
course
=
get_course_with_access
(
request
.
user
,
'load'
,
course_key
,
check_if_enrolled
=
True
)
course
=
get_course_with_access
(
request
.
user
,
'load'
,
course_key
,
check_if_enrolled
=
True
)
course_settings
=
make_course_settings
(
course
,
request
.
user
)
course_settings
=
make_course_settings
(
course
,
request
.
user
)
cc_user
=
cc
.
User
.
from_django_user
(
request
.
user
)
cc_user
=
cc
.
User
.
from_django_user
(
request
.
user
)
user_info
=
cc_user
.
to_dict
()
user_info
=
cc_user
.
to_dict
()
is_moderator
=
has_permission
(
request
.
user
,
"see_all_cohorts"
,
course_key
)
is_moderator
=
has_permission
(
request
.
user
,
"see_all_cohorts"
,
course_key
)
...
@@ -341,6 +364,8 @@ def single_thread(request, course_key, discussion_id, thread_id):
...
@@ -341,6 +364,8 @@ def single_thread(request, course_key, discussion_id, thread_id):
with
newrelic
.
agent
.
FunctionTrace
(
nr_transaction
,
"get_annotated_content_infos"
):
with
newrelic
.
agent
.
FunctionTrace
(
nr_transaction
,
"get_annotated_content_infos"
):
annotated_content_info
=
utils
.
get_annotated_content_infos
(
course_key
,
thread
,
request
.
user
,
user_info
=
user_info
)
annotated_content_info
=
utils
.
get_annotated_content_infos
(
course_key
,
thread
,
request
.
user
,
user_info
=
user_info
)
content
=
utils
.
prepare_content
(
thread
.
to_dict
(),
course_key
,
is_staff
)
content
=
utils
.
prepare_content
(
thread
.
to_dict
(),
course_key
,
is_staff
)
content
=
utils
.
safe_content
(
thread
.
to_dict
(),
course_key
,
is_staff
)
add_thread_group_name
(
content
,
course_key
)
with
newrelic
.
agent
.
FunctionTrace
(
nr_transaction
,
"add_courseware_context"
):
with
newrelic
.
agent
.
FunctionTrace
(
nr_transaction
,
"add_courseware_context"
):
add_courseware_context
([
content
],
course
,
request
.
user
)
add_courseware_context
([
content
],
course
,
request
.
user
)
return
utils
.
JsonResponse
({
return
utils
.
JsonResponse
({
...
@@ -353,12 +378,14 @@ def single_thread(request, course_key, discussion_id, thread_id):
...
@@ -353,12 +378,14 @@ def single_thread(request, course_key, discussion_id, thread_id):
threads
,
query_params
=
get_threads
(
request
,
course
)
threads
,
query_params
=
get_threads
(
request
,
course
)
except
ValueError
:
except
ValueError
:
return
HttpResponseBadRequest
(
"Invalid group_id"
)
return
HttpResponseBadRequest
(
"Invalid group_id"
)
threads
,
query_params
=
get_threads
(
request
,
course_key
)
threads
.
append
(
thread
.
to_dict
())
threads
.
append
(
thread
.
to_dict
())
with
newrelic
.
agent
.
FunctionTrace
(
nr_transaction
,
"add_courseware_context"
):
with
newrelic
.
agent
.
FunctionTrace
(
nr_transaction
,
"add_courseware_context"
):
add_courseware_context
(
threads
,
course
,
request
.
user
)
add_courseware_context
(
threads
,
course
,
request
.
user
)
for
thread
in
threads
:
for
thread
in
threads
:
add_thread_group_name
(
thread
,
course_key
)
# patch for backward compatibility with comments service
# patch for backward compatibility with comments service
if
"pinned"
not
in
thread
:
if
"pinned"
not
in
thread
:
thread
[
"pinned"
]
=
False
thread
[
"pinned"
]
=
False
...
@@ -395,11 +422,11 @@ def single_thread(request, course_key, discussion_id, thread_id):
...
@@ -395,11 +422,11 @@ def single_thread(request, course_key, discussion_id, thread_id):
'sort_preference'
:
cc_user
.
default_sort_key
,
'sort_preference'
:
cc_user
.
default_sort_key
,
'category_map'
:
course_settings
[
"category_map"
],
'category_map'
:
course_settings
[
"category_map"
],
'course_settings'
:
_attr_safe_json
(
course_settings
),
'course_settings'
:
_attr_safe_json
(
course_settings
),
'cohorted_commentables'
:
(
get_cohorted_commentables
(
course
_
id
)),
'cohorted_commentables'
:
(
get_cohorted_commentables
(
course
.
id
)),
'has_permission_to_create_thread'
:
cached_has_permission
(
request
.
user
,
"create_thread"
,
course_
id
),
'has_permission_to_create_thread'
:
has_permission
(
request
.
user
,
"create_thread"
,
course
.
id
),
'has_permission_to_create_comment'
:
cached_has_permission
(
request
.
user
,
"create_comment"
,
course_
id
),
'has_permission_to_create_comment'
:
has_permission
(
request
.
user
,
"create_comment"
,
course
.
id
),
'has_permission_to_create_subcomment'
:
cached_has_permission
(
request
.
user
,
"create_subcomment"
,
course_
id
),
'has_permission_to_create_subcomment'
:
has_permission
(
request
.
user
,
"create_subcomment"
,
course
.
id
),
'has_permission_to_openclose_thread'
:
cached_has_permission
(
request
.
user
,
"openclose_thread"
,
course_
id
)
'has_permission_to_openclose_thread'
:
has_permission
(
request
.
user
,
"openclose_thread"
,
course
.
id
)
}
}
return
render_to_response
(
'discussion/index.html'
,
context
)
return
render_to_response
(
'discussion/index.html'
,
context
)
...
...
lms/djangoapps/django_comment_client/utils.py
View file @
9732f151
...
@@ -2,6 +2,7 @@ from collections import defaultdict
...
@@ -2,6 +2,7 @@ from collections import defaultdict
from
datetime
import
datetime
from
datetime
import
datetime
import
json
import
json
import
logging
import
logging
import
string
import
pytz
import
pytz
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
...
@@ -25,6 +26,11 @@ from openedx.core.djangoapps.course_groups.cohorts import (
...
@@ -25,6 +26,11 @@ from openedx.core.djangoapps.course_groups.cohorts import (
)
)
from
openedx.core.djangoapps.course_groups.models
import
CourseUserGroup
from
openedx.core.djangoapps.course_groups.models
import
CourseUserGroup
from
xmodule.modulestore.django
import
modulestore
from
django.utils.timezone
import
UTC
from
opaque_keys.edx.locations
import
i4xEncoder
from
opaque_keys.edx.keys
import
CourseKey
import
json
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -584,6 +590,31 @@ def get_group_id_for_comments_service(request, course_key, commentable_id=None):
...
@@ -584,6 +590,31 @@ def get_group_id_for_comments_service(request, course_key, commentable_id=None):
return
None
return
None
def
add_thread_group_name
(
thread_info
,
course_key
):
"""
Augment the specified thread info to include the group name if a group id is present.
"""
if
thread_info
.
get
(
'group_id'
)
is
not
None
:
thread_info
[
'group_name'
]
=
get_cohort_by_id
(
course_key
,
thread_info
.
get
(
'group_id'
))
.
name
def
format_filename
(
filename
):
"""Take a string and return a valid filename constructed from the string.
Uses a whitelist approach: any characters not present in valid_chars are
removed. Also spaces are replaced with underscores.
Note: this method may produce invalid filenames such as ``, `.` or `..`
When I use this method I prepend a date string like '2009_01_15_19_46_32_'
and append a file extension like '.txt', so I avoid the potential of using
an invalid filename.
https://gist.github.com/seanh/93666
"""
valid_chars
=
"-_.()
%
s
%
s"
%
(
string
.
ascii_letters
,
string
.
digits
)
filename
=
''
.
join
(
c
for
c
in
filename
if
c
in
valid_chars
)
filename
=
filename
.
replace
(
' '
,
'_'
)
return
filename
def
is_comment_too_deep
(
parent
):
def
is_comment_too_deep
(
parent
):
"""
"""
Determine whether a comment with the given parent violates MAX_COMMENT_DEPTH
Determine whether a comment with the given parent violates MAX_COMMENT_DEPTH
...
...
openedx/core/djangoapps/course_groups/cohorts.py
View file @
9732f151
...
@@ -326,6 +326,16 @@ def get_cohort_by_id(course_key, cohort_id):
...
@@ -326,6 +326,16 @@ def get_cohort_by_id(course_key, cohort_id):
)
)
def
get_cohorted_threads_privacy
(
course_key
):
"""
Given a course key, return the cohorted threads privacy setting.
Raises:
Http404 if the course doesn't exist.
"""
return
courses
.
get_course_by_id
(
course_key
)
.
cohorted_threads_privacy
def
add_cohort
(
course_key
,
name
,
assignment_type
):
def
add_cohort
(
course_key
,
name
,
assignment_type
):
"""
"""
Add a cohort to a course. Raises ValueError if a cohort of the same name already
Add a cohort to a course. Raises ValueError if a cohort of the same name already
...
...
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