BOM-2358 : Pyupgrade in dashboard, debug, discussion apps (#26529)

* Python code cleanup by the cleanup-python-code Jenkins job.

This pull request was generated by the cleanup-python-code Jenkins job, which ran
```
cd lms/djangoapps/dashboard; find . -type f -name '*.py' | while read fname; do sed -i 's/  # lint-amnesty, pylint: disable=super-with-arguments//; s/  # lint-amnesty, pylint: disable=import-error, wrong-import-order//; s/  # lint-amnesty, pylint: disable=wrong-import-order//' "$fname"; done; find . -type f -name '*.py' | while read fname; do pyupgrade --exit-zero-even-if-changed --py3-plus --py36-plus --py38-plus "$fname"; done; isort --recursive .
```

The following packages were installed:
`pyupgrade,isort`

* feedback done

Co-authored-by: Zulqarnain <muhammad.zulqarnain@arbisoft.com>
This commit is contained in:
edX requirements bot
2021-02-22 05:42:21 -05:00
committed by GitHub
parent e505d99237
commit f33f12bbea
52 changed files with 730 additions and 798 deletions

View File

@@ -17,9 +17,9 @@ from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from opaque_keys.edx.locator import CourseLocator
from six import StringIO
from xmodule.util.sandboxing import DEFAULT_PYTHON_LIB_FILENAME
from lms.djangoapps.dashboard.models import CourseImportLog
from xmodule.util.sandboxing import DEFAULT_PYTHON_LIB_FILENAME
log = logging.getLogger(__name__)
@@ -35,7 +35,7 @@ class GitImportError(Exception):
def __init__(self, message=None):
if message is None:
message = self.MESSAGE
super(GitImportError, self).__init__(message) # lint-amnesty, pylint: disable=super-with-arguments
super().__init__(message)
class GitImportErrorNoDir(GitImportError):
@@ -43,11 +43,11 @@ class GitImportErrorNoDir(GitImportError):
GitImportError when no directory exists at the specified path.
"""
def __init__(self, repo_dir):
super(GitImportErrorNoDir, self).__init__( # lint-amnesty, pylint: disable=super-with-arguments
super().__init__(
_(
u"Path {0} doesn't exist, please create it, "
u"or configure a different path with "
u"GIT_REPO_DIR"
"Path {0} doesn't exist, please create it, "
"or configure a different path with "
"GIT_REPO_DIR"
).format(repo_dir)
)
@@ -118,8 +118,8 @@ def cmd_log(cmd, cwd):
"""
output = subprocess.check_output(cmd, cwd=cwd, stderr=subprocess.STDOUT).decode('utf-8')
log.debug(u'Command was: %r. Working directory was: %r', ' '.join(cmd), cwd)
log.debug(u'Command output was: %r', output)
log.debug('Command was: %r. Working directory was: %r', ' '.join(cmd), cwd)
log.debug('Command output was: %r', output)
return output
@@ -135,15 +135,15 @@ def switch_branch(branch, rdir):
try:
cmd_log(['git', 'fetch', ], rdir)
except subprocess.CalledProcessError as ex:
log.exception(u'Unable to fetch remote: %r', ex.output)
log.exception('Unable to fetch remote: %r', ex.output)
raise GitImportErrorCannotBranch() # lint-amnesty, pylint: disable=raise-missing-from
# Check if the branch is available from the remote.
cmd = ['git', 'ls-remote', 'origin', '-h', 'refs/heads/{0}'.format(branch), ]
cmd = ['git', 'ls-remote', 'origin', '-h', f'refs/heads/{branch}', ]
try:
output = cmd_log(cmd, rdir)
except subprocess.CalledProcessError as ex:
log.exception(u'Getting a list of remote branches failed: %r', ex.output)
log.exception('Getting a list of remote branches failed: %r', ex.output)
raise GitImportErrorCannotBranch() # lint-amnesty, pylint: disable=raise-missing-from
if branch not in output:
raise GitImportErrorRemoteBranchMissing()
@@ -152,7 +152,7 @@ def switch_branch(branch, rdir):
try:
output = cmd_log(cmd, rdir)
except subprocess.CalledProcessError as ex:
log.exception(u'Getting a list of local branches failed: %r', ex.output)
log.exception('Getting a list of local branches failed: %r', ex.output)
raise GitImportErrorCannotBranch() # lint-amnesty, pylint: disable=raise-missing-from
branches = []
for line in output.split('\n'):
@@ -161,18 +161,18 @@ def switch_branch(branch, rdir):
if branch not in branches:
# Checkout with -b since it is remote only
cmd = ['git', 'checkout', '--force', '--track',
'-b', branch, 'origin/{0}'.format(branch), ]
'-b', branch, f'origin/{branch}', ]
try:
cmd_log(cmd, rdir)
except subprocess.CalledProcessError as ex:
log.exception(u'Unable to checkout remote branch: %r', ex.output)
log.exception('Unable to checkout remote branch: %r', ex.output)
raise GitImportErrorCannotBranch() # lint-amnesty, pylint: disable=raise-missing-from
# Go ahead and reset hard to the newest version of the branch now that we know
# it is local.
try:
cmd_log(['git', 'reset', '--hard', 'origin/{0}'.format(branch), ], rdir)
cmd_log(['git', 'reset', '--hard', f'origin/{branch}', ], rdir)
except subprocess.CalledProcessError as ex:
log.exception(u'Unable to reset to branch: %r', ex.output)
log.exception('Unable to reset to branch: %r', ex.output)
raise GitImportErrorCannotBranch() # lint-amnesty, pylint: disable=raise-missing-from
@@ -215,9 +215,9 @@ def add_repo(repo, rdir_in, branch=None):
rdir = os.path.basename(rdir_in)
else:
rdir = repo.rsplit('/', 1)[-1].rsplit('.git', 1)[0]
log.debug(u'rdir = %s', rdir)
log.debug('rdir = %s', rdir)
rdirp = '{0}/{1}'.format(git_repo_dir, rdir)
rdirp = f'{git_repo_dir}/{rdir}'
if os.path.exists(rdirp):
log.info('directory already exists, doing a git pull instead '
'of git clone')
@@ -231,7 +231,7 @@ def add_repo(repo, rdir_in, branch=None):
try:
ret_git = cmd_log(cmd, cwd=cwd)
except subprocess.CalledProcessError as ex:
log.exception(u'Error running git pull: %r', ex.output)
log.exception('Error running git pull: %r', ex.output)
raise GitImportErrorCannotPull() # lint-amnesty, pylint: disable=raise-missing-from
if branch:
@@ -242,10 +242,10 @@ def add_repo(repo, rdir_in, branch=None):
try:
commit_id = cmd_log(cmd, cwd=rdirp)
except subprocess.CalledProcessError as ex:
log.exception(u'Unable to get git log: %r', ex.output)
log.exception('Unable to get git log: %r', ex.output)
raise GitImportErrorBadRepo() # lint-amnesty, pylint: disable=raise-missing-from
ret_git += u'\nCommit ID: {0}'.format(commit_id)
ret_git += f'\nCommit ID: {commit_id}'
# get branch
cmd = ['git', 'symbolic-ref', '--short', 'HEAD', ]
@@ -254,10 +254,10 @@ def add_repo(repo, rdir_in, branch=None):
except subprocess.CalledProcessError as ex:
# I can't discover a way to excercise this, but git is complex
# so still logging and raising here in case.
log.exception(u'Unable to determine branch: %r', ex.output)
log.exception('Unable to determine branch: %r', ex.output)
raise GitImportErrorBadRepo() # lint-amnesty, pylint: disable=raise-missing-from
ret_git += u'{0}Branch: {1}'.format(' \n', branch)
ret_git += '{}Branch: {}'.format(' \n', branch)
# Get XML logging logger and capture debug to parse results
output = StringIO()
@@ -306,8 +306,8 @@ def add_repo(repo, rdir_in, branch=None):
# We want set course id in CourseImportLog as CourseLocator. So that in split module
# environment course id remain consistent as CourseLocator instance.
course_key = CourseLocator(*course_id)
cdir = '{0}/{1}'.format(git_repo_dir, course_key.course)
log.debug(u'Studio course dir = %s', cdir)
cdir = f'{git_repo_dir}/{course_key.course}'
log.debug('Studio course dir = %s', cdir)
if os.path.exists(cdir) and not os.path.islink(cdir):
log.debug(' -> exists, but is not symlink')
@@ -319,7 +319,7 @@ def add_repo(repo, rdir_in, branch=None):
log.exception('Failed to remove course directory')
if not os.path.exists(cdir):
log.debug(u' -> creating symlink between %s and %s', rdirp, cdir)
log.debug(' -> creating symlink between %s and %s', rdirp, cdir)
try:
os.symlink(os.path.abspath(rdirp), os.path.abspath(cdir))
except OSError:
@@ -348,5 +348,5 @@ def add_repo(repo, rdir_in, branch=None):
)
cil.save()
log.debug(u'saved CourseImportLog for %s', cil.course_id)
log.debug('saved CourseImportLog for %s', cil.course_id)
mdb.close()

View File

@@ -7,11 +7,11 @@ import logging
from django.core.management.base import BaseCommand, CommandError
from django.utils.translation import ugettext as _
from lms.djangoapps.dashboard import git_import
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.xml import XMLModuleStore
from lms.djangoapps.dashboard import git_import
log = logging.getLogger(__name__)
@@ -26,8 +26,8 @@ class Command(BaseCommand):
# to store the courses for use on the Web site.
help = ('Usage: '
'git_add_course repository_url [directory to check out into] [repository_branch] '
'\n{0}'.format(_('Import the specified git repository and optional branch into the '
'modulestore and optionally specified directory.')))
'\n{}'.format(_('Import the specified git repository and optional branch into the '
'modulestore and optionally specified directory.')))
def add_arguments(self, parser):
# Positional arguments

View File

@@ -16,6 +16,10 @@ from django.core.management.base import CommandError
from django.test.utils import override_settings
from opaque_keys.edx.keys import CourseKey
from six import StringIO
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.mongo_connection import MONGO_HOST, MONGO_PORT_NUM
import lms.djangoapps.dashboard.git_import as git_import
from lms.djangoapps.dashboard.git_import import (
@@ -26,10 +30,6 @@ from lms.djangoapps.dashboard.git_import import (
GitImportErrorRemoteBranchMissing,
GitImportErrorUrlBad
)
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.mongo_connection import MONGO_HOST, MONGO_PORT_NUM
TEST_MONGODB_LOG = {
'host': MONGO_HOST,
'port': MONGO_PORT_NUM,
@@ -41,7 +41,7 @@ TEST_MONGODB_LOG = {
@override_settings(
MONGODB_LOG=TEST_MONGODB_LOG,
GIT_REPO_DIR=settings.TEST_ROOT / "course_repos_{}".format(uuid4().hex)
GIT_REPO_DIR=settings.TEST_ROOT / f"course_repos_{uuid4().hex}"
)
@unittest.skipUnless(settings.FEATURES.get('ENABLE_SYSADMIN_DASHBOARD'),
"ENABLE_SYSADMIN_DASHBOARD not set")
@@ -57,7 +57,7 @@ class TestGitAddCourse(SharedModuleStoreTestCase):
ENABLED_CACHES = ['default', 'mongo_metadata_inheritance', 'loc_cache']
def setUp(self):
super(TestGitAddCourse, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.git_repo_dir = settings.GIT_REPO_DIR
def assertCommandFailureRegexp(self, regex, *args):
@@ -72,17 +72,14 @@ class TestGitAddCourse(SharedModuleStoreTestCase):
Validate argument checking
"""
# No argument given.
if six.PY2:
self.assertCommandFailureRegexp('Error: too few arguments')
else:
self.assertCommandFailureRegexp('Error: the following arguments are required: repository_url')
self.assertCommandFailureRegexp('Error: the following arguments are required: repository_url')
# Extra/Un-named arguments given.
self.assertCommandFailureRegexp(
'Error: unrecognized arguments: blah blah blah',
'blah', 'blah', 'blah', 'blah')
# Not a valid path.
self.assertCommandFailureRegexp(
u'Path {0} doesn\'t exist, please create it,'.format(self.git_repo_dir),
f'Path {self.git_repo_dir} doesn\'t exist, please create it,',
'blah')
# Test successful import from command
if not os.path.isdir(self.git_repo_dir):
@@ -119,14 +116,14 @@ class TestGitAddCourse(SharedModuleStoreTestCase):
git_import.add_repo('file:///foobar.git', None, None)
# Test git repo that exists, but is "broken"
bare_repo = os.path.abspath('{0}/{1}'.format(settings.TEST_ROOT, 'bare.git'))
bare_repo = os.path.abspath('{}/{}'.format(settings.TEST_ROOT, 'bare.git'))
os.mkdir(bare_repo)
self.addCleanup(shutil.rmtree, bare_repo)
subprocess.check_output(['git', '--bare', 'init', ], stderr=subprocess.STDOUT,
cwd=bare_repo)
with pytest.raises(GitImportErrorBadRepo):
git_import.add_repo('file://{0}'.format(bare_repo), None, None)
git_import.add_repo(f'file://{bare_repo}', None, None)
def test_detached_repo(self):
"""
@@ -188,7 +185,7 @@ class TestGitAddCourse(SharedModuleStoreTestCase):
This wil create conditions to exercise bad paths in the switch_branch function.
"""
# create bare repo that we can mess with and attempt an import
bare_repo = os.path.abspath('{0}/{1}'.format(settings.TEST_ROOT, 'bare.git'))
bare_repo = os.path.abspath('{}/{}'.format(settings.TEST_ROOT, 'bare.git'))
os.mkdir(bare_repo)
self.addCleanup(shutil.rmtree, bare_repo)
subprocess.check_output(['git', '--bare', 'init', ], stderr=subprocess.STDOUT,
@@ -202,7 +199,7 @@ class TestGitAddCourse(SharedModuleStoreTestCase):
rdir = '{0}/bare'.format(repo_dir)
with pytest.raises(GitImportErrorBadRepo):
git_import.add_repo('file://{0}'.format(bare_repo), None, None)
git_import.add_repo(f'file://{bare_repo}', None, None)
# Get logger for checking strings in logs
output = StringIO()
@@ -212,12 +209,12 @@ class TestGitAddCourse(SharedModuleStoreTestCase):
glog.addHandler(test_log_handler)
# Move remote so fetch fails
shutil.move(bare_repo, '{0}/not_bare.git'.format(settings.TEST_ROOT))
shutil.move(bare_repo, f'{settings.TEST_ROOT}/not_bare.git')
try:
git_import.switch_branch('master', rdir)
except GitImportError:
assert 'Unable to fetch remote' in output.getvalue()
shutil.move('{0}/not_bare.git'.format(settings.TEST_ROOT), bare_repo)
shutil.move(f'{settings.TEST_ROOT}/not_bare.git', bare_repo)
output.truncate(0)
# Replace origin with a different remote

View File

@@ -2,7 +2,6 @@
import mongoengine
from xmodule.modulestore.mongoengine_fields import CourseKeyField

View File

@@ -2,13 +2,12 @@
This module creates a sysadmin dashboard for managing and viewing
courses.
"""
import json
import logging
import os
import subprocess
import warnings
from io import StringIO
import mongoengine
from django.conf import settings
@@ -26,18 +25,17 @@ from django.views.decorators.http import condition
from django.views.generic.base import TemplateView
from opaque_keys.edx.keys import CourseKey
from path import Path as path
from six import StringIO, text_type
from xmodule.modulestore.django import modulestore
import lms.djangoapps.dashboard.git_import as git_import
from common.djangoapps.track import views as track_views
from lms.djangoapps.dashboard.git_import import GitImportError
from lms.djangoapps.dashboard.models import CourseImportLog
from common.djangoapps.edxmako.shortcuts import render_to_response
from lms.djangoapps.courseware.courses import get_course_by_id
from openedx.core.djangolib.markup import HTML
from common.djangoapps.student.models import CourseEnrollment, Registration, UserProfile
from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole
from xmodule.modulestore.django import modulestore
from common.djangoapps.track import views as track_views
from lms.djangoapps.courseware.courses import get_course_by_id
from lms.djangoapps.dashboard.git_import import GitImportError
from lms.djangoapps.dashboard.models import CourseImportLog
from openedx.core.djangolib.markup import HTML
log = logging.getLogger(__name__)
@@ -56,9 +54,9 @@ class SysadminDashboardView(TemplateView):
warnings.warn("Sysadmin Dashboard is deprecated. See DEPR-118.", DeprecationWarning)
self.def_ms = modulestore()
self.msg = u''
self.msg = ''
self.datatable = []
super(SysadminDashboardView, self).__init__(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments
super().__init__(**kwargs)
@method_decorator(ensure_csrf_cookie)
@method_decorator(login_required)
@@ -66,7 +64,7 @@ class SysadminDashboardView(TemplateView):
must_revalidate=True))
@method_decorator(condition(etag_func=None))
def dispatch(self, *args, **kwargs):
return super(SysadminDashboardView, self).dispatch(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
return super().dispatch(*args, **kwargs)
def get_courses(self):
""" Get an iterable list of courses."""
@@ -88,7 +86,7 @@ class Users(SysadminDashboardView):
if not name:
return _('Must provide full name')
msg = u''
msg = ''
if not password:
return _('Password must be supplied')
@@ -104,7 +102,7 @@ class Users(SysadminDashboardView):
try:
user.save()
except IntegrityError:
msg += _(u'Oops, failed to create user {user}, {error}').format(
msg += _('Oops, failed to create user {user}, {error}').format(
user=user,
error="IntegrityError"
)
@@ -117,7 +115,7 @@ class Users(SysadminDashboardView):
profile.name = name
profile.save()
msg += _(u'User {user} created successfully!').format(user=user)
msg += _('User {user} created successfully!').format(user=user)
return msg
def delete_user(self, uname):
@@ -129,19 +127,19 @@ class Users(SysadminDashboardView):
try:
user = User.objects.get(email=uname)
except User.DoesNotExist as err:
msg = _(u'Cannot find user with email address {email_addr}').format(email_addr=uname)
msg = _('Cannot find user with email address {email_addr}').format(email_addr=uname)
return msg
else:
try:
user = User.objects.get(username=uname)
except User.DoesNotExist as err:
msg = _(u'Cannot find user with username {username} - {error}').format(
msg = _('Cannot find user with username {username} - {error}').format(
username=uname,
error=str(err)
)
return msg
user.delete()
return _(u'Deleted user {username}').format(username=uname)
return _('Deleted user {username}').format(username=uname)
def make_datatable(self):
"""
@@ -185,12 +183,12 @@ class Users(SysadminDashboardView):
uname = request.POST.get('student_uname', '').strip()
name = request.POST.get('student_fullname', '').strip()
password = request.POST.get('student_password', '').strip()
self.msg = HTML(u'<h4>{0}</h4><p>{1}</p><hr />{2}').format(
self.msg = HTML('<h4>{0}</h4><p>{1}</p><hr />{2}').format(
_('Create User Results'),
self.create_user(uname, name, password), self.msg)
elif action == 'del_user':
uname = request.POST.get('student_uname', '').strip()
self.msg = HTML(u'<h4>{0}</h4><p>{1}</p><hr />{2}').format(
self.msg = HTML('<h4>{0}</h4><p>{1}</p><hr />{2}').format(
_('Delete User Results'), self.delete_user(uname), self.msg)
context = {
'datatable': self.make_datatable(),
@@ -222,14 +220,14 @@ class Courses(SysadminDashboardView):
return info
cmd = ['git', 'log', '-1',
u'--format=format:{ "commit": "%H", "author": "%an %ae", "date": "%ad"}', ]
'--format=format:{ "commit": "%H", "author": "%an %ae", "date": "%ad"}', ]
try:
output_json = json.loads(subprocess.check_output(cmd, cwd=gdir).decode('utf-8'))
info = [output_json['commit'],
output_json['date'],
output_json['author'], ]
except OSError as error:
log.warning(text_type(u"Error fetching git data: %s - %s"), text_type(cdir), text_type(error))
log.warning("Error fetching git data: %s - %s", str(cdir), str(error))
except (ValueError, subprocess.CalledProcessError):
pass
@@ -251,9 +249,9 @@ class Courses(SysadminDashboardView):
at debug level for display in template
"""
msg = u''
msg = ''
log.debug(u'Adding course using git repo %s', gitloc)
log.debug('Adding course using git repo %s', gitloc)
# Grab logging output for debugging imports
output = StringIO()
@@ -291,8 +289,8 @@ class Courses(SysadminDashboardView):
msg_header = _('Added Course')
color = 'blue'
msg = HTML(u"<h4 style='color:{0}'>{1}</h4>").format(color, msg_header)
msg += HTML(u"<pre>{0}</pre>").format(escape(ret))
msg = HTML("<h4 style='color:{0}'>{1}</h4>").format(color, msg_header)
msg += HTML("<pre>{0}</pre>").format(escape(ret))
return msg
def make_datatable(self, courses=None):
@@ -302,7 +300,7 @@ class Courses(SysadminDashboardView):
courses = courses or self.get_courses()
for course in courses:
gdir = course.id.course
data.append([course.display_name, text_type(course.id)]
data.append([course.display_name, str(course.id)]
+ self.git_info_for_course(gdir))
return dict(header=[_('Course Name'),
@@ -357,7 +355,7 @@ class Courses(SysadminDashboardView):
course_found = True
except Exception as err: # pylint: disable=broad-except
self.msg += _( # lint-amnesty, pylint: disable=translation-of-non-string
HTML(u'Error - cannot get course with ID {0}<br/><pre>{1}</pre>')
HTML('Error - cannot get course with ID {0}<br/><pre>{1}</pre>')
).format(
course_key,
escape(str(err))
@@ -368,8 +366,8 @@ class Courses(SysadminDashboardView):
self.def_ms.delete_course(course.id, request.user.id)
# don't delete user permission groups, though
self.msg += \
HTML(u"<font color='red'>{0} {1} = {2} ({3})</font>").format(
_('Deleted'), text_type(course.location), text_type(course.id), course.display_name)
HTML("<font color='red'>{0} {1} = {2} ({3})</font>").format(
_('Deleted'), str(course.location), str(course.id), course.display_name)
context = {
'datatable': self.make_datatable(list(courses.values())),
@@ -477,7 +475,7 @@ class GitLogs(TemplateView):
cilset = CourseImportLog.objects.filter(
course_id=course_id
).order_by('-created')
log.debug(u'cilset length=%s', len(cilset))
log.debug('cilset length=%s', len(cilset))
# Paginate the query set
paginator = Paginator(cilset, page_size)
@@ -494,7 +492,7 @@ class GitLogs(TemplateView):
mdb.close()
context = {
'logs': logs,
'course_id': text_type(course_id) if course_id else None,
'course_id': str(course_id) if course_id else None,
'error_msg': error_msg,
'page_size': page_size
}

View File

@@ -1,8 +1,6 @@
"""
Provide tests for sysadmin dashboard feature in sysadmin.py
"""
import glob
import os
import re
@@ -18,19 +16,17 @@ from django.test.utils import override_settings
from django.urls import reverse
from opaque_keys.edx.locator import CourseLocator
from pytz import UTC
from six import text_type
from six.moves import range
from lms.djangoapps.dashboard.git_import import GitImportErrorNoDir
from lms.djangoapps.dashboard.models import CourseImportLog
from openedx.core.djangolib.markup import Text
from common.djangoapps.student.roles import CourseStaffRole, GlobalStaff
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.util.date_utils import DEFAULT_DATE_TIME_FORMAT, get_time_display
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, SharedModuleStoreTestCase
from xmodule.modulestore.tests.mongo_connection import MONGO_HOST, MONGO_PORT_NUM
from common.djangoapps.student.roles import CourseStaffRole, GlobalStaff
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.util.date_utils import DEFAULT_DATE_TIME_FORMAT, get_time_display
from lms.djangoapps.dashboard.git_import import GitImportErrorNoDir
from lms.djangoapps.dashboard.models import CourseImportLog
from openedx.core.djangolib.markup import Text
TEST_MONGODB_LOG = {
'host': MONGO_HOST,
'port': MONGO_PORT_NUM,
@@ -52,7 +48,7 @@ class SysadminBaseTestCase(SharedModuleStoreTestCase):
def setUp(self):
"""Setup test case by adding primary user."""
super(SysadminBaseTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.user = UserFactory.create(username='test_user',
email='test_user+sysadmin@edx.org',
password='foo')
@@ -73,7 +69,7 @@ class SysadminBaseTestCase(SharedModuleStoreTestCase):
def _rm_edx4edx(self):
"""Deletes the sample course from the XML store"""
def_ms = modulestore()
course_path = '{0}/edx4edx_lite'.format(
course_path = '{}/edx4edx_lite'.format(
os.path.abspath(settings.DATA_DIR))
try:
# using XML store
@@ -86,11 +82,11 @@ class SysadminBaseTestCase(SharedModuleStoreTestCase):
response = self.client.post(
reverse('sysadmin_courses'),
{
'course_id': text_type(course.id),
'course_id': str(course.id),
'action': 'del_course',
}
)
self.addCleanup(self._rm_glob, '{0}_deleted_*'.format(course_path))
self.addCleanup(self._rm_glob, f'{course_path}_deleted_*')
return response
@@ -112,7 +108,7 @@ class SysadminBaseTestCase(SharedModuleStoreTestCase):
@override_settings(
MONGODB_LOG=TEST_MONGODB_LOG,
GIT_REPO_DIR=settings.TEST_ROOT / "course_repos_{}".format(uuid4().hex)
GIT_REPO_DIR=settings.TEST_ROOT / f"course_repos_{uuid4().hex}"
)
@unittest.skipUnless(settings.FEATURES.get('ENABLE_SYSADMIN_DASHBOARD'),
"ENABLE_SYSADMIN_DASHBOARD not set")
@@ -124,7 +120,7 @@ class TestSysAdminMongoCourseImport(SysadminBaseTestCase):
@classmethod
def tearDownClass(cls):
"""Delete mongo log entries after test."""
super(TestSysAdminMongoCourseImport, cls).tearDownClass()
super().tearDownClass()
try:
mongoengine.connect(TEST_MONGODB_LOG['db'])
CourseImportLog.objects.all().delete()
@@ -153,7 +149,7 @@ class TestSysAdminMongoCourseImport(SysadminBaseTestCase):
# Create git loaded course
response = self._add_edx4edx()
self.assertContains(response, Text(text_type(GitImportErrorNoDir(settings.GIT_REPO_DIR))))
self.assertContains(response, Text(str(GitImportErrorNoDir(settings.GIT_REPO_DIR))))
def test_mongo_course_add_delete(self):
"""
@@ -182,7 +178,7 @@ class TestSysAdminMongoCourseImport(SysadminBaseTestCase):
# Regex of first 3 columns of course information table row for
# test course loaded from git. Would not have sha1 if
# git_info_for_course failed.
table_re = re.compile(u"""
table_re = re.compile("""
<tr>\\s+
<td>edX\\sAuthor\\sCourse</td>\\s+ # expected test git course name
<td>course-v1:MITx\\+edx4edx\\+edx4edx</td>\\s+ # expected test git course_id
@@ -314,7 +310,7 @@ class TestSysAdminMongoCourseImport(SysadminBaseTestCase):
page
)
)
self.assertContains(response, u'Page {} of 2'.format(expected))
self.assertContains(response, f'Page {expected} of 2')
CourseImportLog.objects.delete()

View File

@@ -6,15 +6,11 @@ For each of the courses, it loops through all of the modules, and dumps
each as a separate output file containing the json representation
of each of its fields (including those fields that are set as default values).
"""
import json
import six
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
from path import Path as path
from six import text_type
from xmodule.modulestore.xml import XMLModuleStore
@@ -28,7 +24,7 @@ class Command(BaseCommand):
def handle(self, *args, **options):
if len(args) != 1:
raise CommandError(u'Must called with arguments: {}'.format(self.args))
raise CommandError(f'Must called with arguments: {self.args}')
xml_module_store = XMLModuleStore(
data_dir=settings.DATA_DIR,
@@ -40,12 +36,12 @@ class Command(BaseCommand):
export_dir = path(args[0])
for course_id, course_modules in six.iteritems(xml_module_store.modules):
for course_id, course_modules in xml_module_store.modules.items():
course_path = course_id.replace('/', '_')
for location, descriptor in six.iteritems(course_modules):
location_path = text_type(location).replace('/', '_')
for location, descriptor in course_modules.items():
location_path = str(location).replace('/', '_')
data = {}
for field_name, field in six.iteritems(descriptor.fields):
for field_name, field in descriptor.fields.items():
try:
data[field_name] = field.read_json(descriptor)
except Exception as exc: # pylint: disable=broad-except

View File

@@ -4,12 +4,12 @@
import pprint
import traceback
from codejail.safe_exec import safe_exec
from django.contrib.auth.decorators import login_required
from django.http import Http404, HttpResponse
from django.utils.html import escape
from django.views.decorators.csrf import ensure_csrf_cookie
from codejail.safe_exec import safe_exec
from common.djangoapps.edxmako.shortcuts import render_to_response
from openedx.core.djangolib.markup import HTML
@@ -52,7 +52,7 @@ def show_parameters(request):
"""A page that shows what parameters were on the URL and post."""
html_list = []
for name, value in sorted(request.GET.items()):
html_list.append(escape(u"GET {}: {!r}".format(name, value)))
html_list.append(escape(f"GET {name}: {value!r}"))
for name, value in sorted(request.POST.items()):
html_list.append(escape(u"POST {}: {!r}".format(name, value)))
html_list.append(escape(f"POST {name}: {value!r}"))
return HttpResponse("\n".join(HTML("<p>{}</p>").format(h) for h in html_list))

View File

@@ -17,21 +17,21 @@ class DiscussionConfig(AppConfig):
Application Configuration for Discussion.
"""
name = u'lms.djangoapps.discussion'
name = 'lms.djangoapps.discussion'
plugin_app = {
PluginURLs.CONFIG: {
ProjectType.LMS: {
PluginURLs.NAMESPACE: u'',
PluginURLs.REGEX: r'^courses/{}/discussion/forum/'.format(COURSE_ID_PATTERN),
PluginURLs.RELATIVE_PATH: u'urls',
PluginURLs.NAMESPACE: '',
PluginURLs.REGEX: fr'^courses/{COURSE_ID_PATTERN}/discussion/forum/',
PluginURLs.RELATIVE_PATH: 'urls',
}
},
PluginSettings.CONFIG: {
ProjectType.CMS: {
SettingsType.COMMON: {PluginSettings.RELATIVE_PATH: u'settings.common'},
SettingsType.COMMON: {PluginSettings.RELATIVE_PATH: 'settings.common'},
},
ProjectType.LMS: {
SettingsType.COMMON: {PluginSettings.RELATIVE_PATH: u'settings.common'},
SettingsType.COMMON: {PluginSettings.RELATIVE_PATH: 'settings.common'},
},
}
}

View File

@@ -2,20 +2,17 @@
"""
Transformers for Discussion-related events.
"""
import six
from django.contrib.auth.models import User
from django.urls import NoReverseMatch, reverse
from eventtracking.processors.exceptions import EventEmissionExit
from opaque_keys import InvalidKeyError
from opaque_keys.edx.locator import CourseLocator
from common.djangoapps.track.transformers import EventTransformer, EventTransformerRegistry
from common.djangoapps.track.views.segmentio import BI_SCREEN_VIEWED_EVENT_NAME, FORUM_THREAD_VIEWED_EVENT_LABEL
from lms.djangoapps.discussion.django_comment_client.base.views import add_truncated_title_to_event_data
from lms.djangoapps.discussion.django_comment_client.permissions import get_team
from lms.djangoapps.discussion.django_comment_client.utils import get_cached_discussion_id_map_by_course_id
from common.djangoapps.track.transformers import EventTransformer, EventTransformerRegistry
from common.djangoapps.track.views.segmentio import BI_SCREEN_VIEWED_EVENT_NAME, FORUM_THREAD_VIEWED_EVENT_LABEL
def _get_string(dictionary, key, del_if_bad=True):
@@ -28,7 +25,7 @@ def _get_string(dictionary, key, del_if_bad=True):
"""
if key in dictionary:
value = dictionary[key]
if isinstance(value, six.string_types):
if isinstance(value, str):
return value
else:
if del_if_bad:

View File

@@ -1,6 +1,5 @@
import pytest
# pylint: skip-file
# -*- coding: utf-8 -*-
"""Tests for django comment client views."""
@@ -10,7 +9,6 @@ from contextlib import contextmanager
import ddt
import mock
import six
from django.contrib.auth.models import User
from django.core.management import call_command
from django.test.client import RequestFactory
@@ -18,12 +16,16 @@ from django.urls import reverse
from eventtracking.processors.exceptions import EventEmissionExit
from mock import ANY, Mock, patch
from opaque_keys.edx.keys import CourseKey
from six import text_type
from six.moves import range
from common.test.utils import MockSignalHandlerMixin, disable_signal
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from common.djangoapps.student.roles import CourseStaffRole, UserBasedRole
from common.djangoapps.student.tests.factories import CourseAccessRoleFactory, CourseEnrollmentFactory, UserFactory
from common.djangoapps.track.middleware import TrackMiddleware
from common.djangoapps.track.views import segmentio
from common.djangoapps.track.views.tests.base import SEGMENTIO_TEST_USER_ID, SegmentIOTrackingTestCaseBase
from common.djangoapps.util.testing import UrlResetMixin
from common.test.utils import MockSignalHandlerMixin, disable_signal
from lms.djangoapps.discussion.django_comment_client.base import views
from lms.djangoapps.discussion.django_comment_client.tests.group_id import (
CohortedTopicGroupIdTestMixin,
@@ -49,12 +51,6 @@ from openedx.core.djangoapps.django_comment_common.utils import (
)
from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES
from openedx.core.lib.teams_config import TeamsConfig
from common.djangoapps.student.roles import CourseStaffRole, UserBasedRole
from common.djangoapps.student.tests.factories import CourseAccessRoleFactory, CourseEnrollmentFactory, UserFactory
from common.djangoapps.track.middleware import TrackMiddleware
from common.djangoapps.track.views import segmentio
from common.djangoapps.track.views.tests.base import SEGMENTIO_TEST_USER_ID, SegmentIOTrackingTestCaseBase
from common.djangoapps.util.testing import UrlResetMixin
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
@@ -71,7 +67,7 @@ QUERY_COUNT_TABLE_BLACKLIST = WAFFLE_TABLES
# pylint: disable=missing-docstring
class MockRequestSetupMixin(object):
class MockRequestSetupMixin:
def _create_response_mock(self, data):
return Mock(
text=json.dumps(data),
@@ -103,7 +99,7 @@ class CreateThreadGroupIdTestCase(
return views.create_thread(
request,
course_id=six.text_type(self.course.id),
course_id=str(self.course.id),
commentable_id=commentable_id
)
@@ -150,7 +146,7 @@ class ThreadActionGroupIdTestCase(
return getattr(views, view_name)(
request,
course_id=six.text_type(self.course.id),
course_id=str(self.course.id),
thread_id="dummy",
**(view_args or {})
)
@@ -209,7 +205,7 @@ class ThreadActionGroupIdTestCase(
)
class ViewsTestCaseMixin(object):
class ViewsTestCaseMixin:
def set_up_course(self, module_count=0):
"""
@@ -230,13 +226,13 @@ class ViewsTestCaseMixin(object):
ItemFactory.create(
parent_location=self.course.location,
category='discussion',
discussion_id='id_module_{}'.format(i),
discussion_category=u'Category {}'.format(i),
discussion_target=u'Discussion {}'.format(i)
discussion_id=f'id_module_{i}',
discussion_category=f'Category {i}',
discussion_target=f'Discussion {i}'
)
# seed the forums permissions and roles
call_command('seed_permissions_roles', six.text_type(self.course_id))
call_command('seed_permissions_roles', str(self.course_id))
# Patch the comment client user save method so it does not try
# to create a new cc user when creating a django user
@@ -322,24 +318,24 @@ class ViewsTestCaseMixin(object):
if extra_request_data:
thread.update(extra_request_data)
url = reverse('create_thread', kwargs={'commentable_id': 'i4x-MITx-999-course-Robot_Super_Course',
'course_id': six.text_type(self.course_id)})
'course_id': str(self.course_id)})
response = self.client.post(url, data=thread)
assert mock_request.called
expected_data = {
'thread_type': 'discussion',
'body': u'this is a post',
'body': 'this is a post',
'context': ThreadContext.COURSE,
'anonymous_to_peers': False, 'user_id': 1,
'title': u'Hello',
'commentable_id': u'i4x-MITx-999-course-Robot_Super_Course',
'title': 'Hello',
'commentable_id': 'i4x-MITx-999-course-Robot_Super_Course',
'anonymous': False,
'course_id': six.text_type(self.course_id),
'course_id': str(self.course_id),
}
if extra_response_data:
expected_data.update(extra_response_data)
mock_request.assert_called_with(
'post',
'{prefix}/i4x-MITx-999-course-Robot_Super_Course/threads'.format(prefix=CS_PREFIX),
f'{CS_PREFIX}/i4x-MITx-999-course-Robot_Super_Course/threads',
data=expected_data,
params={'request_id': ANY},
headers=ANY,
@@ -360,7 +356,7 @@ class ViewsTestCaseMixin(object):
response = self.client.post(
reverse("update_thread", kwargs={
"thread_id": "dummy",
"course_id": six.text_type(self.course_id)
"course_id": str(self.course_id)
}),
data={"body": "foo", "title": "foo", "commentable_id": "some_topic"}
)
@@ -389,7 +385,7 @@ class ViewsQueryCountTestCase(
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(ViewsQueryCountTestCase, self).setUp()
super().setUp()
def count_queries(func): # pylint: disable=no-self-argument
"""
@@ -438,7 +434,7 @@ class ViewsTestCase(
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
with super(ViewsTestCase, cls).setUpClassAndTestData():
with super().setUpClassAndTestData():
cls.course = CourseFactory.create(
org='MITx', course='999',
discussion_topics={"Some Topic": {"id": "some_topic"}},
@@ -447,19 +443,19 @@ class ViewsTestCase(
@classmethod
def setUpTestData(cls):
super(ViewsTestCase, cls).setUpTestData()
super().setUpTestData()
cls.course_id = cls.course.id
# seed the forums permissions and roles
call_command('seed_permissions_roles', six.text_type(cls.course_id))
call_command('seed_permissions_roles', str(cls.course_id))
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
# Patching the ENABLE_DISCUSSION_SERVICE value affects the contents of urls.py,
# so we need to call super.setUp() which reloads urls.py (because
# of the UrlResetMixin)
super(ViewsTestCase, self).setUp()
super().setUp()
# Patch the comment client user save method so it does not try
# to create a new cc user when creating a django user
@@ -523,7 +519,7 @@ class ViewsTestCase(
response = self.client.post(
reverse(
view_name,
kwargs={"course_id": six.text_type(self.course_id), "thread_id": 'i4x-MITx-999-course-Robot_Super_Course'}
kwargs={"course_id": str(self.course_id), "thread_id": 'i4x-MITx-999-course-Robot_Super_Course'}
)
)
assert response.status_code == 200
@@ -540,7 +536,7 @@ class ViewsTestCase(
with self.assert_discussion_signals('thread_deleted'):
response = views.delete_thread(
request,
course_id=six.text_type(self.course.id),
course_id=str(self.course.id),
thread_id=test_thread_id
)
assert response.status_code == 200
@@ -558,14 +554,14 @@ class ViewsTestCase(
with self.assert_discussion_signals('comment_deleted'):
response = views.delete_comment(
request,
course_id=six.text_type(self.course.id),
course_id=str(self.course.id),
comment_id=test_comment_id
)
assert response.status_code == 200
assert mock_request.called
args = mock_request.call_args[0]
assert args[0] == 'delete'
assert args[1].endswith('/{}'.format(test_comment_id))
assert args[1].endswith(f"/{test_comment_id}")
def _test_request_error(self, view_name, view_kwargs, data, mock_request):
"""
@@ -583,7 +579,7 @@ class ViewsTestCase(
def test_create_thread_no_title(self, mock_request):
self._test_request_error(
"create_thread",
{"commentable_id": "dummy", "course_id": six.text_type(self.course_id)},
{"commentable_id": "dummy", "course_id": str(self.course_id)},
{"body": "foo"},
mock_request
)
@@ -591,7 +587,7 @@ class ViewsTestCase(
def test_create_thread_empty_title(self, mock_request):
self._test_request_error(
"create_thread",
{"commentable_id": "dummy", "course_id": six.text_type(self.course_id)},
{"commentable_id": "dummy", "course_id": str(self.course_id)},
{"body": "foo", "title": " "},
mock_request
)
@@ -599,7 +595,7 @@ class ViewsTestCase(
def test_create_thread_no_body(self, mock_request):
self._test_request_error(
"create_thread",
{"commentable_id": "dummy", "course_id": six.text_type(self.course_id)},
{"commentable_id": "dummy", "course_id": str(self.course_id)},
{"title": "foo"},
mock_request
)
@@ -607,7 +603,7 @@ class ViewsTestCase(
def test_create_thread_empty_body(self, mock_request):
self._test_request_error(
"create_thread",
{"commentable_id": "dummy", "course_id": six.text_type(self.course_id)},
{"commentable_id": "dummy", "course_id": str(self.course_id)},
{"body": " ", "title": "foo"},
mock_request
)
@@ -615,7 +611,7 @@ class ViewsTestCase(
def test_update_thread_no_title(self, mock_request):
self._test_request_error(
"update_thread",
{"thread_id": "dummy", "course_id": six.text_type(self.course_id)},
{"thread_id": "dummy", "course_id": str(self.course_id)},
{"body": "foo"},
mock_request
)
@@ -623,7 +619,7 @@ class ViewsTestCase(
def test_update_thread_empty_title(self, mock_request):
self._test_request_error(
"update_thread",
{"thread_id": "dummy", "course_id": six.text_type(self.course_id)},
{"thread_id": "dummy", "course_id": str(self.course_id)},
{"body": "foo", "title": " "},
mock_request
)
@@ -631,7 +627,7 @@ class ViewsTestCase(
def test_update_thread_no_body(self, mock_request):
self._test_request_error(
"update_thread",
{"thread_id": "dummy", "course_id": six.text_type(self.course_id)},
{"thread_id": "dummy", "course_id": str(self.course_id)},
{"title": "foo"},
mock_request
)
@@ -639,7 +635,7 @@ class ViewsTestCase(
def test_update_thread_empty_body(self, mock_request):
self._test_request_error(
"update_thread",
{"thread_id": "dummy", "course_id": six.text_type(self.course_id)},
{"thread_id": "dummy", "course_id": str(self.course_id)},
{"body": " ", "title": "foo"},
mock_request
)
@@ -655,7 +651,7 @@ class ViewsTestCase(
def test_update_thread_wrong_commentable_id(self, mock_get_discussion_id_map, mock_request):
self._test_request_error(
"update_thread",
{"thread_id": "dummy", "course_id": six.text_type(self.course_id)},
{"thread_id": "dummy", "course_id": str(self.course_id)},
{"body": "foo", "title": "foo", "commentable_id": "wrong_commentable"},
mock_request
)
@@ -666,7 +662,7 @@ class ViewsTestCase(
response = self.client.post(
reverse(
"create_comment",
kwargs={"course_id": six.text_type(self.course_id), "thread_id": "dummy"}
kwargs={"course_id": str(self.course_id), "thread_id": "dummy"}
),
data={"body": "body"}
)
@@ -675,7 +671,7 @@ class ViewsTestCase(
def test_create_comment_no_body(self, mock_request):
self._test_request_error(
"create_comment",
{"thread_id": "dummy", "course_id": six.text_type(self.course_id)},
{"thread_id": "dummy", "course_id": str(self.course_id)},
{},
mock_request
)
@@ -683,7 +679,7 @@ class ViewsTestCase(
def test_create_comment_empty_body(self, mock_request):
self._test_request_error(
"create_comment",
{"thread_id": "dummy", "course_id": six.text_type(self.course_id)},
{"thread_id": "dummy", "course_id": str(self.course_id)},
{"body": " "},
mock_request
)
@@ -691,7 +687,7 @@ class ViewsTestCase(
def test_create_sub_comment_no_body(self, mock_request):
self._test_request_error(
"create_sub_comment",
{"comment_id": "dummy", "course_id": six.text_type(self.course_id)},
{"comment_id": "dummy", "course_id": str(self.course_id)},
{},
mock_request
)
@@ -699,7 +695,7 @@ class ViewsTestCase(
def test_create_sub_comment_empty_body(self, mock_request):
self._test_request_error(
"create_sub_comment",
{"comment_id": "dummy", "course_id": six.text_type(self.course_id)},
{"comment_id": "dummy", "course_id": str(self.course_id)},
{"body": " "},
mock_request
)
@@ -707,7 +703,7 @@ class ViewsTestCase(
def test_update_comment_no_body(self, mock_request):
self._test_request_error(
"update_comment",
{"comment_id": "dummy", "course_id": six.text_type(self.course_id)},
{"comment_id": "dummy", "course_id": str(self.course_id)},
{},
mock_request
)
@@ -715,7 +711,7 @@ class ViewsTestCase(
def test_update_comment_empty_body(self, mock_request):
self._test_request_error(
"update_comment",
{"comment_id": "dummy", "course_id": six.text_type(self.course_id)},
{"comment_id": "dummy", "course_id": str(self.course_id)},
{"body": " "},
mock_request
)
@@ -728,14 +724,14 @@ class ViewsTestCase(
response = self.client.post(
reverse(
"update_comment",
kwargs={"course_id": six.text_type(self.course_id), "comment_id": comment_id}
kwargs={"course_id": str(self.course_id), "comment_id": comment_id}
),
data={"body": updated_body}
)
assert response.status_code == 200
mock_request.assert_called_with(
"put",
"{prefix}/comments/{comment_id}".format(prefix=CS_PREFIX, comment_id=comment_id),
f"{CS_PREFIX}/comments/{comment_id}",
headers=ANY,
params=ANY,
timeout=ANY,
@@ -779,14 +775,14 @@ class ViewsTestCase(
})
url = reverse('flag_abuse_for_thread', kwargs={
'thread_id': '518d4237b023791dca00000d',
'course_id': six.text_type(self.course_id)
'course_id': str(self.course_id)
})
response = self.client.post(url)
assert mock_request.called
call_list = [
(
('get', '{prefix}/threads/518d4237b023791dca00000d'.format(prefix=CS_PREFIX)),
('get', f'{CS_PREFIX}/threads/518d4237b023791dca00000d'),
{
'data': None,
'params': {'mark_as_read': True, 'request_id': ANY, 'with_responses': False},
@@ -795,7 +791,7 @@ class ViewsTestCase(
}
),
(
('put', '{prefix}/threads/518d4237b023791dca00000d/abuse_flag'.format(prefix=CS_PREFIX)),
('put', f'{CS_PREFIX}/threads/518d4237b023791dca00000d/abuse_flag'),
{
'data': {'user_id': '1'},
'params': {'request_id': ANY},
@@ -804,7 +800,7 @@ class ViewsTestCase(
}
),
(
('get', '{prefix}/threads/518d4237b023791dca00000d'.format(prefix=CS_PREFIX)),
('get', f'{CS_PREFIX}/threads/518d4237b023791dca00000d'),
{
'data': None,
'params': {'mark_as_read': True, 'request_id': ANY, 'with_responses': False},
@@ -856,14 +852,14 @@ class ViewsTestCase(
})
url = reverse('un_flag_abuse_for_thread', kwargs={
'thread_id': '518d4237b023791dca00000d',
'course_id': six.text_type(self.course_id)
'course_id': str(self.course_id)
})
response = self.client.post(url)
assert mock_request.called
call_list = [
(
('get', '{prefix}/threads/518d4237b023791dca00000d'.format(prefix=CS_PREFIX)),
('get', f'{CS_PREFIX}/threads/518d4237b023791dca00000d'),
{
'data': None,
'params': {'mark_as_read': True, 'request_id': ANY, 'with_responses': False},
@@ -872,7 +868,7 @@ class ViewsTestCase(
}
),
(
('put', '{prefix}/threads/518d4237b023791dca00000d/abuse_unflag'.format(prefix=CS_PREFIX)),
('put', f'{CS_PREFIX}/threads/518d4237b023791dca00000d/abuse_unflag'),
{
'data': {'user_id': '1'},
'params': {'request_id': ANY},
@@ -881,7 +877,7 @@ class ViewsTestCase(
}
),
(
('get', '{prefix}/threads/518d4237b023791dca00000d'.format(prefix=CS_PREFIX)),
('get', f'{CS_PREFIX}/threads/518d4237b023791dca00000d'),
{
'data': None,
'params': {'mark_as_read': True, 'request_id': ANY, 'with_responses': False},
@@ -927,14 +923,14 @@ class ViewsTestCase(
})
url = reverse('flag_abuse_for_comment', kwargs={
'comment_id': '518d4237b023791dca00000d',
'course_id': six.text_type(self.course_id)
'course_id': str(self.course_id)
})
response = self.client.post(url)
assert mock_request.called
call_list = [
(
('get', '{prefix}/comments/518d4237b023791dca00000d'.format(prefix=CS_PREFIX)),
('get', f'{CS_PREFIX}/comments/518d4237b023791dca00000d'),
{
'data': None,
'params': {'request_id': ANY},
@@ -943,7 +939,7 @@ class ViewsTestCase(
}
),
(
('put', '{prefix}/comments/518d4237b023791dca00000d/abuse_flag'.format(prefix=CS_PREFIX)),
('put', f'{CS_PREFIX}/comments/518d4237b023791dca00000d/abuse_flag'),
{
'data': {'user_id': '1'},
'params': {'request_id': ANY},
@@ -952,7 +948,7 @@ class ViewsTestCase(
}
),
(
('get', '{prefix}/comments/518d4237b023791dca00000d'.format(prefix=CS_PREFIX)),
('get', f'{CS_PREFIX}/comments/518d4237b023791dca00000d'),
{
'data': None,
'params': {'request_id': ANY},
@@ -998,14 +994,14 @@ class ViewsTestCase(
})
url = reverse('un_flag_abuse_for_comment', kwargs={
'comment_id': '518d4237b023791dca00000d',
'course_id': six.text_type(self.course_id)
'course_id': str(self.course_id)
})
response = self.client.post(url)
assert mock_request.called
call_list = [
(
('get', '{prefix}/comments/518d4237b023791dca00000d'.format(prefix=CS_PREFIX)),
('get', f'{CS_PREFIX}/comments/518d4237b023791dca00000d'),
{
'data': None,
'params': {'request_id': ANY},
@@ -1014,7 +1010,7 @@ class ViewsTestCase(
}
),
(
('put', '{prefix}/comments/518d4237b023791dca00000d/abuse_unflag'.format(prefix=CS_PREFIX)),
('put', f'{CS_PREFIX}/comments/518d4237b023791dca00000d/abuse_unflag'),
{
'data': {'user_id': '1'},
'params': {'request_id': ANY},
@@ -1023,7 +1019,7 @@ class ViewsTestCase(
}
),
(
('get', '{prefix}/comments/518d4237b023791dca00000d'.format(prefix=CS_PREFIX)),
('get', f'{CS_PREFIX}/comments/518d4237b023791dca00000d'),
{
'data': None,
'params': {'request_id': ANY},
@@ -1050,7 +1046,7 @@ class ViewsTestCase(
response = self.client.post(
reverse(
view_name,
kwargs={item_id: 'dummy', 'course_id': six.text_type(self.course_id)}
kwargs={item_id: 'dummy', 'course_id': str(self.course_id)}
)
)
assert response.status_code == 200
@@ -1062,7 +1058,7 @@ class ViewsTestCase(
response = self.client.post(
reverse(
'endorse_comment',
kwargs={'comment_id': 'dummy', 'course_id': six.text_type(self.course_id)}
kwargs={'comment_id': 'dummy', 'course_id': str(self.course_id)}
)
)
assert response.status_code == 200
@@ -1075,12 +1071,12 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
with super(ViewPermissionsTestCase, cls).setUpClassAndTestData():
with super().setUpClassAndTestData():
cls.course = CourseFactory.create()
@classmethod
def setUpTestData(cls):
super(ViewPermissionsTestCase, cls).setUpTestData()
super().setUpTestData()
seed_permissions_roles(cls.course.id)
@@ -1095,13 +1091,13 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(ViewPermissionsTestCase, self).setUp()
super().setUp()
def test_pin_thread_as_student(self, mock_request):
self._set_mock_request_data(mock_request, {})
self.client.login(username=self.student.username, password=self.password)
response = self.client.post(
reverse("pin_thread", kwargs={"course_id": six.text_type(self.course.id), "thread_id": "dummy"})
reverse("pin_thread", kwargs={"course_id": str(self.course.id), "thread_id": "dummy"})
)
assert response.status_code == 401
@@ -1109,7 +1105,7 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
self._set_mock_request_data(mock_request, {})
self.client.login(username=self.moderator.username, password=self.password)
response = self.client.post(
reverse("pin_thread", kwargs={"course_id": six.text_type(self.course.id), "thread_id": "dummy"})
reverse("pin_thread", kwargs={"course_id": str(self.course.id), "thread_id": "dummy"})
)
assert response.status_code == 200
@@ -1117,7 +1113,7 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
self._set_mock_request_data(mock_request, {})
self.client.login(username=self.student.username, password=self.password)
response = self.client.post(
reverse("un_pin_thread", kwargs={"course_id": six.text_type(self.course.id), "thread_id": "dummy"})
reverse("un_pin_thread", kwargs={"course_id": str(self.course.id), "thread_id": "dummy"})
)
assert response.status_code == 401
@@ -1125,7 +1121,7 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
self._set_mock_request_data(mock_request, {})
self.client.login(username=self.moderator.username, password=self.password)
response = self.client.post(
reverse("un_pin_thread", kwargs={"course_id": six.text_type(self.course.id), "thread_id": "dummy"})
reverse("un_pin_thread", kwargs={"course_id": str(self.course.id), "thread_id": "dummy"})
)
assert response.status_code == 200
@@ -1148,7 +1144,7 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
)
self.client.login(username=self.moderator.username, password=self.password)
response = self.client.post(
reverse("endorse_comment", kwargs={"course_id": six.text_type(self.course.id), "comment_id": "dummy"})
reverse("endorse_comment", kwargs={"course_id": str(self.course.id), "comment_id": "dummy"})
)
assert response.status_code == 200
@@ -1160,7 +1156,7 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
)
self.client.login(username=self.student.username, password=self.password)
response = self.client.post(
reverse("endorse_comment", kwargs={"course_id": six.text_type(self.course.id), "comment_id": "dummy"})
reverse("endorse_comment", kwargs={"course_id": str(self.course.id), "comment_id": "dummy"})
)
assert response.status_code == 401
@@ -1172,7 +1168,7 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
)
self.client.login(username=self.student.username, password=self.password)
response = self.client.post(
reverse("endorse_comment", kwargs={"course_id": six.text_type(self.course.id), "comment_id": "dummy"})
reverse("endorse_comment", kwargs={"course_id": str(self.course.id), "comment_id": "dummy"})
)
assert response.status_code == 200
@@ -1186,12 +1182,12 @@ class CreateThreadUnicodeTestCase(
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
with super(CreateThreadUnicodeTestCase, cls).setUpClassAndTestData():
with super().setUpClassAndTestData():
cls.course = CourseFactory.create()
@classmethod
def setUpTestData(cls):
super(CreateThreadUnicodeTestCase, cls).setUpTestData()
super().setUpTestData()
seed_permissions_roles(cls.course.id)
cls.student = UserFactory.create()
@@ -1208,7 +1204,7 @@ class CreateThreadUnicodeTestCase(
request.view_name = "create_thread"
response = views.create_thread(
# The commentable ID contains a username, the Unicode char below ensures it works fine
request, course_id=six.text_type(self.course.id), commentable_id=u"non_tåem_dummy_id"
request, course_id=str(self.course.id), commentable_id="non_tåem_dummy_id"
)
assert response.status_code == 200
@@ -1228,12 +1224,12 @@ class UpdateThreadUnicodeTestCase(
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
with super(UpdateThreadUnicodeTestCase, cls).setUpClassAndTestData():
with super().setUpClassAndTestData():
cls.course = CourseFactory.create()
@classmethod
def setUpTestData(cls):
super(UpdateThreadUnicodeTestCase, cls).setUpTestData()
super().setUpTestData()
seed_permissions_roles(cls.course.id)
cls.student = UserFactory.create()
@@ -1252,7 +1248,7 @@ class UpdateThreadUnicodeTestCase(
request = RequestFactory().post("dummy_url", {"body": text, "title": text, "thread_type": "question", "commentable_id": "test_commentable"})
request.user = self.student
request.view_name = "update_thread"
response = views.update_thread(request, course_id=six.text_type(self.course.id), thread_id="dummy_thread_id")
response = views.update_thread(request, course_id=str(self.course.id), thread_id="dummy_thread_id")
assert response.status_code == 200
assert mock_request.called
@@ -1273,12 +1269,12 @@ class CreateCommentUnicodeTestCase(
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
with super(CreateCommentUnicodeTestCase, cls).setUpClassAndTestData():
with super().setUpClassAndTestData():
cls.course = CourseFactory.create()
@classmethod
def setUpTestData(cls):
super(CreateCommentUnicodeTestCase, cls).setUpTestData()
super().setUpTestData()
seed_permissions_roles(cls.course.id)
cls.student = UserFactory.create()
@@ -1299,7 +1295,7 @@ class CreateCommentUnicodeTestCase(
request.user = self.student
request.view_name = "create_comment"
response = views.create_comment(
request, course_id=six.text_type(self.course.id), thread_id="dummy_thread_id"
request, course_id=str(self.course.id), thread_id="dummy_thread_id"
)
assert response.status_code == 200
@@ -1320,12 +1316,12 @@ class UpdateCommentUnicodeTestCase(
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
with super(UpdateCommentUnicodeTestCase, cls).setUpClassAndTestData():
with super().setUpClassAndTestData():
cls.course = CourseFactory.create()
@classmethod
def setUpTestData(cls):
super(UpdateCommentUnicodeTestCase, cls).setUpTestData()
super().setUpTestData()
seed_permissions_roles(cls.course.id)
cls.student = UserFactory.create()
@@ -1340,7 +1336,7 @@ class UpdateCommentUnicodeTestCase(
request = RequestFactory().post("dummy_url", {"body": text})
request.user = self.student
request.view_name = "update_comment"
response = views.update_comment(request, course_id=six.text_type(self.course.id), comment_id="dummy_comment_id")
response = views.update_comment(request, course_id=str(self.course.id), comment_id="dummy_comment_id")
assert response.status_code == 200
assert mock_request.called
@@ -1360,12 +1356,12 @@ class CreateSubCommentUnicodeTestCase(
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
with super(CreateSubCommentUnicodeTestCase, cls).setUpClassAndTestData():
with super().setUpClassAndTestData():
cls.course = CourseFactory.create()
@classmethod
def setUpTestData(cls):
super(CreateSubCommentUnicodeTestCase, cls).setUpTestData()
super().setUpTestData()
seed_permissions_roles(cls.course.id)
cls.student = UserFactory.create()
@@ -1388,7 +1384,7 @@ class CreateSubCommentUnicodeTestCase(
Thread.commentable_id = "test_commentable"
try:
response = views.create_sub_comment(
request, course_id=six.text_type(self.course.id), comment_id="dummy_comment_id"
request, course_id=str(self.course.id), comment_id="dummy_comment_id"
)
assert response.status_code == 200
@@ -1441,7 +1437,7 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
with super(TeamsPermissionsTestCase, cls).setUpClassAndTestData():
with super().setUpClassAndTestData():
teams_config_data = {
'topics': [{'id': "topic_id", 'name': 'Solar Power', 'description': 'Solar power is hot'}]
}
@@ -1449,7 +1445,7 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
@classmethod
def setUpTestData(cls):
super(TeamsPermissionsTestCase, cls).setUpTestData()
super().setUpTestData()
cls.course = CourseFactory.create()
cls.password = "test password"
seed_permissions_roles(cls.course.id)
@@ -1491,7 +1487,7 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
# Create a team
cls.team_commentable_id = "team_discussion_id"
cls.team = CourseTeamFactory.create(
name=u'The Only Team',
name='The Only Team',
course_id=cls.course.id,
topic_id='topic_id',
discussion_topic_id=cls.team_commentable_id
@@ -1510,7 +1506,7 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(TeamsPermissionsTestCase, self).setUp()
super().setUp()
def _setup_mock(self, user, mock_request, data):
user = getattr(self, user)
@@ -1555,14 +1551,14 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
"closed": False, "commentable_id": commentable_id,
"context": "standalone",
"username": thread_author.username,
"course_id": six.text_type(self.course.id)
"course_id": str(self.course.id)
}
)
response = self.client.post(
reverse(
"update_thread",
kwargs={
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"thread_id": "dummy"
}
),
@@ -1599,14 +1595,14 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
"commentable_id": commentable_id,
"user_id": str(comment_author.id),
"username": comment_author.username,
"course_id": six.text_type(self.course.id)
"course_id": str(self.course.id)
})
response = self.client.post(
reverse(
"delete_comment",
kwargs={
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"comment_id": "dummy"
}
),
@@ -1627,7 +1623,7 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
reverse(
"create_comment",
kwargs={
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"thread_id": "dummy"
}
),
@@ -1650,7 +1646,7 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
reverse(
"create_sub_comment",
kwargs={
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"comment_id": "dummy_comment"
}
),
@@ -1674,7 +1670,7 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
response = self.client.post(
reverse(
action,
kwargs={"course_id": six.text_type(self.course.id), "comment_id": "dummy_comment"}
kwargs={"course_id": str(self.course.id), "comment_id": "dummy_comment"}
)
)
assert response.status_code == status_code
@@ -1696,7 +1692,7 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
response = self.client.post(
reverse(
action,
kwargs={"course_id": six.text_type(self.course.id), "thread_id": "dummy_thread"}
kwargs={"course_id": str(self.course.id), "thread_id": "dummy_thread"}
)
)
assert response.status_code == status_code
@@ -1714,12 +1710,12 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
with super(ForumEventTestCase, cls).setUpClassAndTestData():
with super().setUpClassAndTestData():
cls.course = CourseFactory.create()
@classmethod
def setUpTestData(cls):
super(ForumEventTestCase, cls).setUpTestData()
super().setUpTestData()
seed_permissions_roles(cls.course.id)
@@ -1742,7 +1738,7 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
request = RequestFactory().post("dummy_url", {"body": "Test comment", 'auto_subscribe': True})
request.user = self.student
request.view_name = "create_comment"
views.create_comment(request, course_id=six.text_type(self.course.id), thread_id='test_thread_id')
views.create_comment(request, course_id=str(self.course.id), thread_id='test_thread_id')
event_name, event = mock_emit.call_args[0]
assert event_name == 'edx.forum.response.created'
@@ -1769,7 +1765,7 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
request = RequestFactory().post("dummy_url", {"body": "Another comment"})
request.user = self.student
request.view_name = "create_sub_comment"
views.create_sub_comment(request, course_id=six.text_type(self.course.id), comment_id="dummy_comment_id")
views.create_sub_comment(request, course_id=str(self.course.id), comment_id="dummy_comment_id")
event_name, event = mock_emit.call_args[0]
assert event_name == 'edx.forum.comment.created'
@@ -1818,7 +1814,7 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
request.user = user
request.view_name = view_name
getattr(views, view_name)(request, course_id=six.text_type(self.course.id), **view_kwargs)
getattr(views, view_name)(request, course_id=str(self.course.id), **view_kwargs)
name, event = mock_emit.call_args[0]
assert name == event_name
@@ -1845,7 +1841,7 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
request.user = self.student
request.view_name = view_name
view_function = getattr(views, view_name)
kwargs = dict(course_id=six.text_type(self.course.id))
kwargs = dict(course_id=str(self.course.id))
kwargs[obj_id_name] = obj_id_name
if not undo:
kwargs.update(value='up')
@@ -1853,7 +1849,7 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
assert mock_emit.called
event_name, event = mock_emit.call_args[0]
assert event_name == 'edx.forum.{}.voted'.format(obj_type)
assert event_name == f'edx.forum.{obj_type}.voted'
assert event['target_username'] == 'gumprecht'
assert event['undo_vote'] == undo
assert event['vote_value'] == 'up'
@@ -1864,12 +1860,12 @@ class UsersEndpointTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockRe
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
with super(UsersEndpointTestCase, cls).setUpClassAndTestData():
with super().setUpClassAndTestData():
cls.course = CourseFactory.create()
@classmethod
def setUpTestData(cls):
super(UsersEndpointTestCase, cls).setUpTestData()
super().setUpTestData()
seed_permissions_roles(cls.course.id)
@@ -1892,7 +1888,7 @@ class UsersEndpointTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockRe
request = getattr(RequestFactory(), method)("dummy_url", kwargs)
request.user = self.student
request.view_name = "users"
return views.users(request, course_id=text_type(course_id))
return views.users(request, course_id=str(course_id))
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
def test_finds_exact_match(self, mock_request):
@@ -2050,7 +2046,7 @@ class ForumThreadViewedEventTransformerTestCase(ForumsEnableMixin, UrlResetMixin
@mock.patch.dict("common.djangoapps.student.models.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(ForumThreadViewedEventTransformerTestCase, self).setUp()
super().setUp()
self.courses_by_store = {
ModuleStoreEnum.Type.mongo: CourseFactory.create(
org='TestX',
@@ -2198,7 +2194,7 @@ class ForumThreadViewedEventTransformerTestCase(ForumsEnableMixin, UrlResetMixin
topic_id=commentable_id,
thread_id=thread_id,
)
expected_path = '/courses/{0}/discussion/forum/{1}/threads/{2}'.format(
expected_path = '/courses/{}/discussion/forum/{}/threads/{}'.format(
course.id, commentable_id, thread_id
)
assert event_trans['event'].get('url').endswith(expected_path)
@@ -2221,7 +2217,7 @@ class ForumThreadViewedEventTransformerTestCase(ForumsEnableMixin, UrlResetMixin
topic_id=self.category.discussion_id,
)
assert event_trans_2['event'].get('category_id') == self.category.discussion_id
full_category_name = u'{0} / {1}'.format(self.category.discussion_category, self.category.discussion_target)
full_category_name = f'{self.category.discussion_category} / {self.category.discussion_target}'
assert event_trans_2['event'].get('category_name') == full_category_name
def test_roles(self):

View File

@@ -1,6 +1,4 @@
"""Views for discussion forums."""
import functools
import json
import logging
@@ -18,10 +16,10 @@ from django.views.decorators import csrf
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.decorators.http import require_GET, require_POST
from opaque_keys.edx.keys import CourseKey
from six import text_type
import lms.djangoapps.discussion.django_comment_client.settings as cc_settings
import openedx.core.djangoapps.django_comment_common.comment_client as cc
from common.djangoapps.util.file import store_uploaded_file
from lms.djangoapps.courseware.access import has_access
from lms.djangoapps.courseware.courses import get_course_by_id, get_course_overview_with_access, get_course_with_access
from lms.djangoapps.courseware.exceptions import CourseAccessRedirect
@@ -57,7 +55,6 @@ from openedx.core.djangoapps.django_comment_common.signals import (
thread_voted
)
from openedx.core.djangoapps.django_comment_common.utils import ThreadContext
from common.djangoapps.util.file import store_uploaded_file
log = logging.getLogger(__name__)
@@ -237,7 +234,7 @@ def create_thread(request, course_id, commentable_id):
Given a course and commentable ID, create the thread
"""
log.debug(u"Creating new thread in %r, id %r", course_id, commentable_id)
log.debug("Creating new thread in %r, id %r", course_id, commentable_id)
course_key = CourseKey.from_string(course_id)
course = get_course_with_access(request.user, 'load', course_key)
post = request.POST
@@ -262,7 +259,7 @@ def create_thread(request, course_id, commentable_id):
'anonymous': anonymous,
'anonymous_to_peers': anonymous_to_peers,
'commentable_id': commentable_id,
'course_id': text_type(course_key),
'course_id': str(course_key),
'user_id': user.id,
'thread_type': post["thread_type"],
'body': post["body"],
@@ -380,7 +377,7 @@ def _create_comment(request, course_key, thread_id=None, parent_id=None):
anonymous=anonymous,
anonymous_to_peers=anonymous_to_peers,
user_id=user.id,
course_id=text_type(course_key),
course_id=str(course_key),
thread_id=thread_id,
parent_id=parent_id,
body=post["body"]
@@ -771,10 +768,10 @@ def upload(request, course_id): # ajax upload file to a question or answer # l
)
except exceptions.PermissionDenied as err:
error = six.text_type(err)
error = str(err)
except Exception as err: # pylint: disable=broad-except
print(err)
logging.critical(six.text_type(err))
logging.critical(str(err))
error = _('Error uploading file. Please contact the site administrator. Thank you.')
if error == '':

View File

@@ -3,7 +3,6 @@ import json
import logging
from django.utils.deprecation import MiddlewareMixin
from six import text_type
from lms.djangoapps.discussion.django_comment_client.utils import JsonError
from openedx.core.djangoapps.django_comment_common.comment_client import CommentClientRequestError
@@ -23,7 +22,7 @@ class AjaxExceptionMiddleware(MiddlewareMixin):
"""
if isinstance(exception, CommentClientRequestError) and request.is_ajax():
try:
return JsonError(json.loads(text_type(exception)), exception.status_code)
return JsonError(json.loads(str(exception)), exception.status_code)
except ValueError:
return JsonError(text_type(exception), exception.status_code)
return JsonError(str(exception), exception.status_code)
return None

View File

@@ -5,7 +5,6 @@ Module for checking permissions with the comment_client backend
import logging
import six
from edx_django_utils.cache import DEFAULT_REQUEST_CACHE
from opaque_keys.edx.keys import CourseKey
@@ -104,7 +103,7 @@ def _check_condition(user, condition, content):
try:
commentable_id = content['commentable_id']
request_cache_dict = DEFAULT_REQUEST_CACHE.data
cache_key = u"django_comment_client.check_team_member.{}.{}".format(user.id, commentable_id)
cache_key = f"django_comment_client.check_team_member.{user.id}.{commentable_id}"
if cache_key in request_cache_dict:
return request_cache_dict[cache_key]
team = get_team(commentable_id)
@@ -138,7 +137,7 @@ def _check_conditions_permissions(user, permissions, course_id, content, user_gr
"""
def test(user, per, operator="or"):
if isinstance(per, six.string_types):
if isinstance(per, str):
if per in CONDITIONS:
return _check_condition(user, per, content)
if 'group_' in per:

View File

@@ -5,7 +5,7 @@ from openedx.core.djangoapps.django_comment_common.models import Permission, Rol
class RoleFactory(DjangoModelFactory): # lint-amnesty, pylint: disable=missing-class-docstring
class Meta(object):
class Meta:
model = Role
name = 'Student'
@@ -13,7 +13,7 @@ class RoleFactory(DjangoModelFactory): # lint-amnesty, pylint: disable=missing-
class PermissionFactory(DjangoModelFactory):
class Meta(object):
class Meta:
model = Permission
name = 'create_comment'

View File

@@ -11,7 +11,7 @@ from openedx.core.djangoapps.django_comment_common.models import CourseDiscussio
from openedx.core.djangoapps.django_comment_common.utils import set_course_discussion_settings
class GroupIdAssertionMixin(object):
class GroupIdAssertionMixin:
def _data_or_params_cs_request(self, mock_request):
"""
Returns the data or params dict that `mock_request` was called with.

View File

@@ -30,7 +30,7 @@ class MockCommentServiceRequestHandler(BaseHTTPRequestHandler):
# Log the request
# pylint: disable=logging-format-interpolation
logger.debug(
u"Comment Service received POST request {0} to path {1}"
"Comment Service received POST request {} to path {}"
.format(json.dumps(post_dict), self.path)
)
@@ -38,7 +38,7 @@ class MockCommentServiceRequestHandler(BaseHTTPRequestHandler):
if 'X-Edx-Api-Key' in self.headers:
response = self.server._response_str
# Log the response
logger.debug(u"Comment Service: sending response %s", json.dumps(response))
logger.debug("Comment Service: sending response %s", json.dumps(response))
# Send a response back to the client
self.send_response(200)
@@ -68,7 +68,7 @@ class MockCommentServiceRequestHandler(BaseHTTPRequestHandler):
# Log the request
# pylint: disable=logging-format-interpolation
logger.debug(
u"Comment Service received PUT request {0} to path {1}"
"Comment Service received PUT request {} to path {}"
.format(json.dumps(post_dict), self.path)
)
@@ -76,7 +76,7 @@ class MockCommentServiceRequestHandler(BaseHTTPRequestHandler):
if 'X-Edx-Api-Key' in self.headers:
response = self.server._response_str
# Log the response
logger.debug(u"Comment Service: sending response %s", json.dumps(response))
logger.debug("Comment Service: sending response %s", json.dumps(response))
# Send a response back to the client
self.send_response(200)

View File

@@ -15,7 +15,7 @@ class MockCommentServiceServerTest(unittest.TestCase):
'''
def setUp(self):
super(MockCommentServiceServerTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# This is a test of the test setup,
# so it does not need to run as part of the unit test suite
@@ -44,8 +44,8 @@ class MockCommentServiceServerTest(unittest.TestCase):
of how you would create a new user
"""
# Send a request
values = {'username': u'user100',
'external_id': '4', 'email': u'user100@edx.org'}
values = {'username': 'user100',
'external_id': '4', 'email': 'user100@edx.org'}
data = json.dumps(values)
headers = {'Content-Type': 'application/json', 'Content-Length': len(data), 'X-Edx-Api-Key': 'TEST_API_KEY'}
req = six.moves.urllib.request.Request(self.server_url + '/api/v1/users/4', data, headers) # lint-amnesty, pylint: disable=undefined-variable

View File

@@ -3,7 +3,6 @@ import json
import django.http
from django.test import TestCase
from six import text_type
import lms.djangoapps.discussion.django_comment_client.middleware as middleware
import openedx.core.djangoapps.django_comment_common.comment_client as comment_client
@@ -12,7 +11,7 @@ import openedx.core.djangoapps.django_comment_common.comment_client as comment_c
class AjaxExceptionTestCase(TestCase): # lint-amnesty, pylint: disable=missing-class-docstring
def setUp(self):
super(AjaxExceptionTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.a = middleware.AjaxExceptionMiddleware()
self.request1 = django.http.HttpRequest()
self.request0 = django.http.HttpRequest()
@@ -26,12 +25,12 @@ class AjaxExceptionTestCase(TestCase): # lint-amnesty, pylint: disable=missing-
response1 = self.a.process_exception(self.request1, self.exception1)
assert isinstance(response1, middleware.JsonError)
assert self.exception1.status_code == response1.status_code
assert {'errors': json.loads(text_type(self.exception1))} == json.loads(response1.content.decode('utf-8'))
assert {'errors': json.loads(str(self.exception1))} == json.loads(response1.content.decode('utf-8'))
response2 = self.a.process_exception(self.request1, self.exception2)
assert isinstance(response2, middleware.JsonError)
assert self.exception2.status_code == response2.status_code
assert {'errors': [text_type(self.exception2)]} == json.loads(response2.content.decode('utf-8'))
assert {'errors': [str(self.exception2)]} == json.loads(response2.content.decode('utf-8'))
assert self.a.process_exception(self.request1, self.exception0) is None
assert self.a.process_exception(self.request0, self.exception1) is None

View File

@@ -18,7 +18,7 @@ class RoleClassTestCase(ModuleStoreTestCase):
MODULESTORE = TEST_DATA_MIXED_MODULESTORE
def setUp(self):
super(RoleClassTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# For course ID, syntax edx/classname/classdate is important
# because xmodel.course_module.id_to_location looks for a string to split
@@ -58,7 +58,7 @@ class PermissionClassTestCase(TestCase):
"""
def setUp(self):
super(PermissionClassTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.permission = models.Permission.objects.get_or_create(name="test")[0]
def test_unicode(self):

View File

@@ -1,26 +1,25 @@
# pylint: skip-file
# -*- coding: utf-8 -*-
import datetime
import json
import sys
from unittest import mock
from unittest.mock import Mock, patch
import ddt
import mock
import pytest
import six
from django.test import RequestFactory, TestCase
from django.urls import reverse
from edx_django_utils.cache import RequestCache
from mock import Mock, patch
from opaque_keys.edx.keys import CourseKey
from pytz import UTC
from six import text_type
import lms.djangoapps.discussion.django_comment_client.utils as utils
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from common.djangoapps.student.roles import CourseStaffRole
from common.djangoapps.student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory
from lms.djangoapps.courseware.tabs import get_course_tab_list
from lms.djangoapps.courseware.tests.factories import InstructorFactory
from lms.djangoapps.discussion.django_comment_client.constants import TYPE_ENTRY, TYPE_SUBCATEGORY
@@ -47,8 +46,6 @@ from openedx.core.djangoapps.django_comment_common.utils import (
set_course_discussion_settings
)
from openedx.core.djangoapps.util.testing import ContentGroupTestCase
from common.djangoapps.student.roles import CourseStaffRole
from common.djangoapps.student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import TEST_DATA_MIXED_MODULESTORE, ModuleStoreTestCase
@@ -81,7 +78,7 @@ class AccessUtilsTestCase(ModuleStoreTestCase):
CREATE_USER = False
def setUp(self):
super(AccessUtilsTestCase, self).setUp()
super().setUp()
self.course = CourseFactory.create()
self.course_id = self.course.id
@@ -105,7 +102,7 @@ class AccessUtilsTestCase(ModuleStoreTestCase):
def test_get_role_ids(self):
ret = utils.get_role_ids(self.course_id)
expected = {u'Moderator': [3], u'Community TA': [4, 5]}
expected = {'Moderator': [3], 'Community TA': [4, 5]}
assert ret == expected
def test_has_discussion_privileges(self):
@@ -134,7 +131,7 @@ class CoursewareContextTestCase(ModuleStoreTestCase):
comment client service integration
"""
def setUp(self):
super(CoursewareContextTestCase, self).setUp()
super().setUp()
self.course = CourseFactory.create(org="TestX", number="101", display_name="Test Course")
self.discussion1 = ItemFactory.create(
parent_location=self.course.location,
@@ -170,7 +167,7 @@ class CoursewareContextTestCase(ModuleStoreTestCase):
def assertThreadCorrect(thread, discussion, expected_title): # pylint: disable=invalid-name
"""Asserts that the given thread has the expected set of properties"""
assert set(thread.keys()) == set(['commentable_id', 'courseware_url', 'courseware_title'])
assert thread.get('courseware_url') == reverse('jump_to', kwargs={'course_id': text_type(self.course.id), 'location': text_type(discussion.location)})
assert thread.get('courseware_url') == reverse('jump_to', kwargs={'course_id': str(self.course.id), 'location': str(discussion.location)})
assert thread.get('courseware_title') == expected_title
assertThreadCorrect(threads[0], self.discussion1, "Chapter / Discussion 1")
@@ -228,7 +225,7 @@ class CachedDiscussionIdMapTestCase(ModuleStoreTestCase):
ENABLED_SIGNALS = ['course_published']
def setUp(self):
super(CachedDiscussionIdMapTestCase, self).setUp()
super().setUp()
self.course = CourseFactory.create(org='TestX', number='101', display_name='Test Course')
self.discussion = ItemFactory.create(
@@ -340,7 +337,7 @@ class CachedDiscussionIdMapTestCase(ModuleStoreTestCase):
assert not utils.discussion_category_id_access(self.course, user, 'private_discussion_id')
class CategoryMapTestMixin(object):
class CategoryMapTestMixin:
"""
Provides functionality for classes that test
`get_discussion_category_map`.
@@ -361,7 +358,7 @@ class CategoryMapTestCase(CategoryMapTestMixin, ModuleStoreTestCase):
comment client service integration
"""
def setUp(self):
super(CategoryMapTestCase, self).setUp()
super().setUp()
self.course = CourseFactory.create(
org="TestX", number="101", display_name="Test Course",
@@ -382,7 +379,7 @@ class CategoryMapTestCase(CategoryMapTestMixin, ModuleStoreTestCase):
return ItemFactory.create(
parent_location=self.course.location,
category="discussion",
discussion_id="discussion{}".format(self.discussion_num),
discussion_id=f"discussion{self.discussion_num}",
discussion_category=discussion_category,
discussion_target=discussion_target,
**kwargs
@@ -676,7 +673,7 @@ class CategoryMapTestCase(CategoryMapTestMixin, ModuleStoreTestCase):
def test_start_date_filter(self):
now = datetime.datetime.now()
self.create_discussion("Chapter 1", "Discussion 1", start=now)
self.create_discussion("Chapter 1", u"Discussion 2 обсуждение", start=self.later)
self.create_discussion("Chapter 1", "Discussion 2 обсуждение", start=self.later)
self.create_discussion("Chapter 2", "Discussion", start=now)
self.create_discussion("Chapter 2 / Section 1 / Subsection 1", "Discussion", start=self.later)
self.create_discussion("Chapter 2 / Section 1 / Subsection 2", "Discussion", start=self.later)
@@ -1003,11 +1000,8 @@ class CategoryMapTestCase(CategoryMapTestMixin, ModuleStoreTestCase):
"Topic B": {"id": "Topic_B"},
"Topic C": {"id": "Topic_C"}
}
six.assertCountEqual(
self,
utils.get_discussion_categories_ids(self.course, self.user),
["Topic_A", "Topic_B", "Topic_C"]
)
assert len(utils.get_discussion_categories_ids(self.course, self.user)) ==\
len(["Topic_A", "Topic_B", "Topic_C"])
def test_ids_inline(self):
self.create_discussion("Chapter 1", "Discussion 1")
@@ -1016,11 +1010,8 @@ class CategoryMapTestCase(CategoryMapTestMixin, ModuleStoreTestCase):
self.create_discussion("Chapter 2 / Section 1 / Subsection 1", "Discussion")
self.create_discussion("Chapter 2 / Section 1 / Subsection 2", "Discussion")
self.create_discussion("Chapter 3 / Section 1", "Discussion")
six.assertCountEqual(
self,
utils.get_discussion_categories_ids(self.course, self.user),
["discussion1", "discussion2", "discussion3", "discussion4", "discussion5", "discussion6"]
)
assert len(utils.get_discussion_categories_ids(self.course, self.user)) ==\
len(["discussion1", "discussion2", "discussion3", "discussion4", "discussion5", "discussion6"])
def test_ids_mixed(self):
self.course.discussion_topics = {
@@ -1031,11 +1022,8 @@ class CategoryMapTestCase(CategoryMapTestMixin, ModuleStoreTestCase):
self.create_discussion("Chapter 1", "Discussion 1")
self.create_discussion("Chapter 2", "Discussion")
self.create_discussion("Chapter 2 / Section 1 / Subsection 1", "Discussion")
six.assertCountEqual(
self,
utils.get_discussion_categories_ids(self.course, self.user),
["Topic_A", "Topic_B", "Topic_C", "discussion1", "discussion2", "discussion3"]
)
assert len(utils.get_discussion_categories_ids(self.course, self.user)) ==\
len(["Topic_A", "Topic_B", "Topic_C", "discussion1", "discussion2", "discussion3"])
class ContentGroupCategoryMapTestCase(CategoryMapTestMixin, ContentGroupTestCase):
@@ -1212,7 +1200,7 @@ class DiscussionTabTestCase(ModuleStoreTestCase):
""" Test visibility of the discussion tab. """
def setUp(self):
super(DiscussionTabTestCase, self).setUp()
super().setUp()
self.course = CourseFactory.create()
self.enrolled_user = UserFactory.create()
self.staff_user = AdminFactory.create()
@@ -1252,7 +1240,7 @@ class IsCommentableDividedTestCase(ModuleStoreTestCase):
"""
Make sure that course is reloaded every time--clear out the modulestore.
"""
super(IsCommentableDividedTestCase, self).setUp()
super().setUp()
self.toy_course_key = ToyCourseFactory.create().id
def test_is_commentable_divided(self):
@@ -1385,7 +1373,7 @@ class GroupIdForUserTestCase(ModuleStoreTestCase):
""" Test the get_group_id_for_user method. """
def setUp(self):
super(GroupIdForUserTestCase, self).setUp()
super().setUp()
self.course = CourseFactory.create()
CourseModeFactory.create(course_id=self.course.id, mode_slug=CourseMode.AUDIT)
CourseModeFactory.create(course_id=self.course.id, mode_slug=CourseMode.VERIFIED)
@@ -1425,7 +1413,7 @@ class CourseDiscussionDivisionEnabledTestCase(ModuleStoreTestCase):
""" Test the course_discussion_division_enabled and available_division_schemes methods. """
def setUp(self):
super(CourseDiscussionDivisionEnabledTestCase, self).setUp()
super().setUp()
self.course = CourseFactory.create()
CourseModeFactory.create(course_id=self.course.id, mode_slug=CourseMode.AUDIT)
self.test_cohort = CohortFactory(
@@ -1471,7 +1459,7 @@ class GroupNameTestCase(ModuleStoreTestCase):
""" Test the get_group_name and get_group_names_by_id methods. """
def setUp(self):
super(GroupNameTestCase, self).setUp()
super().setUp()
self.course = CourseFactory.create()
CourseModeFactory.create(course_id=self.course.id, mode_slug=CourseMode.AUDIT)
CourseModeFactory.create(course_id=self.course.id, mode_slug=CourseMode.VERIFIED)
@@ -1585,7 +1573,7 @@ class GroupModeratorPermissionsTestCase(ModuleStoreTestCase):
return True if condition == 'is_open' or condition == 'is_team_member_if_applicable' else False
def setUp(self):
super(GroupModeratorPermissionsTestCase, self).setUp()
super().setUp()
# Create course, seed permissions roles, and create team
self.course = CourseFactory.create()

View File

@@ -1,23 +1,23 @@
# lint-amnesty, pylint: disable=missing-module-docstring
class UnicodeTestMixin(object): # lint-amnesty, pylint: disable=missing-class-docstring
class UnicodeTestMixin: # lint-amnesty, pylint: disable=missing-class-docstring
def test_ascii(self):
self._test_unicode_data(u"This post contains ASCII.")
self._test_unicode_data("This post contains ASCII.")
def test_latin_1(self):
self._test_unicode_data(u"Thís pøst çòñtáins Lätin-1 tæxt")
self._test_unicode_data("Thís pøst çòñtáins Lätin-1 tæxt")
def test_CJK(self):
self._test_unicode_data(u"イんノ丂 アo丂イ co刀イムノ刀丂 cフズ")
self._test_unicode_data("イんノ丂 アo丂イ co刀イムノ刀丂 cフズ")
def test_non_BMP(self):
self._test_unicode_data(u"𝕋𝕙𝕚𝕤 𝕡𝕠𝕤𝕥 𝕔𝕠𝕟𝕥𝕒𝕚𝕟𝕤 𝕔𝕙𝕒𝕣𝕒𝕔𝕥𝕖𝕣𝕤 𝕠𝕦𝕥𝕤𝕚𝕕𝕖 𝕥𝕙𝕖 𝔹𝕄")
self._test_unicode_data("𝕋𝕙𝕚𝕤 𝕡𝕠𝕤𝕥 𝕔𝕠𝕟𝕥𝕒𝕚𝕟𝕤 𝕔𝕙𝕒𝕣𝕒𝕔𝕥𝕖𝕣𝕤 𝕠𝕦𝕥𝕤𝕚𝕕𝕖 𝕥𝕙𝕖 𝔹𝕄")
def test_special_chars(self):
self._test_unicode_data(
u"\" This , post > contains < delimiter ] and [ other } special { characters ; that & may ' break things"
"\" This , post > contains < delimiter ] and [ other } special { characters ; that & may ' break things"
)
def test_string_interp(self):
self._test_unicode_data(u"This post contains %s string interpolation #{syntax}")
self._test_unicode_data("This post contains %s string interpolation #{syntax}")

View File

@@ -3,8 +3,10 @@ Utilities for tests within the django_comment_client module.
"""
from mock import patch
from unittest.mock import patch
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from common.djangoapps.util.testing import UrlResetMixin
from openedx.core.djangoapps.course_groups.tests.helpers import CohortFactory
from openedx.core.djangoapps.django_comment_common.models import ForumsConfig, Role
from openedx.core.djangoapps.django_comment_common.utils import (
@@ -13,20 +15,18 @@ from openedx.core.djangoapps.django_comment_common.utils import (
set_course_discussion_settings
)
from openedx.core.lib.teams_config import TeamsConfig
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from common.djangoapps.util.testing import UrlResetMixin
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
class ForumsEnableMixin(object):
class ForumsEnableMixin:
"""
Ensures that the forums are enabled for a given test class.
"""
def setUp(self):
super(ForumsEnableMixin, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
config = ForumsConfig.current()
config.enabled = True
@@ -40,7 +40,7 @@ class CohortedTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStoreTestCa
@classmethod
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUpClass(cls):
super(CohortedTestCase, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create(
cohort_config={
"cohorted": True,
@@ -61,7 +61,7 @@ class CohortedTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStoreTestCa
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(CohortedTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
seed_permissions_roles(self.course.id)
self.student = UserFactory.create()
@@ -115,8 +115,8 @@ def config_course_discussions(
division_scheme=CourseDiscussionSettings.COHORT,
)
course.discussion_topics = dict((name, {"sort_key": "A", "id": to_id(name)})
for name in discussion_topics)
course.discussion_topics = {name: {"sort_key": "A", "id": to_id(name)}
for name in discussion_topics}
try:
# Not implemented for XMLModulestore, which is used by test_cohorts.
modulestore().update_item(course, ModuleStoreEnum.UserID.test)

View File

@@ -6,18 +6,17 @@ import logging
from collections import defaultdict
from datetime import datetime
import six
from django.conf import settings
from django.contrib.auth.models import User
from django.db import connection
from django.http import HttpResponse
from django.urls import reverse
from django.utils.deprecation import MiddlewareMixin
from opaque_keys.edx.keys import CourseKey, i4xEncoder, UsageKey
from opaque_keys.edx.keys import CourseKey, UsageKey, i4xEncoder
from pytz import UTC
from six import text_type
from six.moves import map
from common.djangoapps.student.models import get_user_by_username_or_email
from common.djangoapps.student.roles import GlobalStaff
from lms.djangoapps.courseware import courses
from lms.djangoapps.courseware.access import has_access
from lms.djangoapps.discussion.django_comment_client.constants import TYPE_ENTRY, TYPE_SUBCATEGORY
@@ -37,8 +36,6 @@ from openedx.core.djangoapps.django_comment_common.models import (
)
from openedx.core.djangoapps.django_comment_common.utils import get_course_discussion_settings
from openedx.core.lib.cache_utils import request_cached
from common.djangoapps.student.models import get_user_by_username_or_email
from common.djangoapps.student.roles import GlobalStaff
from xmodule.modulestore.django import modulestore
from xmodule.partitions.partitions import ENROLLMENT_TRACK_PARTITION_ID
from xmodule.partitions.partitions_service import PartitionService
@@ -57,7 +54,7 @@ def strip_none(dic):
"""
Returns a dictionary stripped of any keys having values of None
"""
return dict([(k, v) for k, v in six.iteritems(dic) if v is not None])
return {k: v for k, v in dic.items() if v is not None}
def strip_blank(dic):
@@ -69,7 +66,7 @@ def strip_blank(dic):
Determines if the provided value contains no information
"""
return isinstance(v, str) and len(v.strip()) == 0
return dict([(k, v) for k, v in six.iteritems(dic) if not _is_blank(v)])
return {k: v for k, v in dic.items() if not _is_blank(v)}
# TODO should we be checking if d1 and d2 have the same keys with different values?
@@ -79,7 +76,7 @@ def get_role_ids(course_id):
Returns a dictionary having role names as keys and a list of users as values
"""
roles = Role.objects.filter(course_id=course_id).exclude(name=FORUM_ROLE_STUDENT)
return dict([(role.name, list(role.users.values_list('id', flat=True))) for role in roles])
return {role.name: list(role.users.values_list('id', flat=True)) for role in roles}
def has_discussion_privileges(user, course_id):
@@ -129,7 +126,7 @@ def has_required_keys(xblock):
for key in ('discussion_id', 'discussion_category', 'discussion_target'):
if getattr(xblock, key, None) is None:
log.debug(
u"Required key '%s' not in discussion %s, leaving out of category map",
"Required key '%s' not in discussion %s, leaving out of category map",
key,
xblock.location
)
@@ -281,7 +278,7 @@ def _filter_unstarted_categories(category_map, course):
filtered_map["entries"][child][key] = unfiltered_map["entries"][child][key]
else:
log.debug(
u"Filtering out:%s with start_date: %s", child, unfiltered_map["entries"][child]["start_date"]
"Filtering out:%s with start_date: %s", child, unfiltered_map["entries"][child]["start_date"]
)
else:
if course.self_paced or unfiltered_map["subcategories"][child]["start_date"] < now:
@@ -425,7 +422,7 @@ def get_discussion_category_map(course, user, divided_only_if_explicit=False, ex
# If we've already seen this title, append an incrementing number to disambiguate
# the category from other categores sharing the same title in the course discussion UI.
dupe_counters[title] += 1
title = u"{title} ({counter})".format(title=title, counter=dupe_counters[title])
title = "{title} ({counter})".format(title=title, counter=dupe_counters[title])
node[level]["entries"][title] = {"id": entry["id"],
"sort_key": entry["sort_key"],
"start_date": entry["start_date"],
@@ -496,8 +493,7 @@ class JsonResponse(HttpResponse):
Object constructor, converts data (if provided) to JSON
"""
content = json.dumps(data, cls=i4xEncoder)
super(JsonResponse, self).__init__(content,
content_type='application/json; charset=utf-8')
super().__init__(content, content_type='application/json; charset=utf-8')
class JsonError(HttpResponse):
@@ -508,11 +504,10 @@ class JsonError(HttpResponse):
"""
Object constructor, returns an error response containing the provided exception messages
"""
if isinstance(error_messages, six.string_types):
if isinstance(error_messages, str):
error_messages = [error_messages]
content = json.dumps({'errors': error_messages}, indent=2, ensure_ascii=False)
super(JsonError, self).__init__(content,
content_type='application/json; charset=utf-8', status=status)
super().__init__(content, content_type='application/json; charset=utf-8', status=status)
class HtmlResponse(HttpResponse):
@@ -523,7 +518,7 @@ class HtmlResponse(HttpResponse):
"""
Object constructor, brokers provided HTML to caller
"""
super(HtmlResponse, self).__init__(html, content_type='text/plain')
super().__init__(html, content_type='text/plain')
class ViewNameMiddleware(MiddlewareMixin):
@@ -562,7 +557,7 @@ class QueryCountDebugMiddleware(MiddlewareMixin):
query_time = query.get('duration', 0) / 1000
total_time += float(query_time)
log.info(u'%s queries run, total %s seconds', len(connection.queries), total_time)
log.info('%s queries run, total %s seconds', len(connection.queries), total_time)
return response
@@ -687,7 +682,7 @@ def get_metadata_for_threads(course_id, threads, user, user_info):
def permalink(content):
if isinstance(content['course_id'], CourseKey):
course_id = text_type(content['course_id'])
course_id = str(content['course_id'])
else:
course_id = content['course_id']
if content['type'] == 'thread':
@@ -702,10 +697,10 @@ def extend_content(content):
if content.get('user_id'):
try:
user = User.objects.get(pk=content['user_id'])
roles = dict(('name', role.name.lower()) for role in user.roles.filter(course_id=content['course_id']))
roles = {'name': role.name.lower() for role in user.roles.filter(course_id=content['course_id'])}
except User.DoesNotExist:
log.error(
u'User ID %s in comment content %s but not in our DB.',
'User ID %s in comment content %s but not in our DB.',
content.get('user_id'),
content.get('id')
)
@@ -735,10 +730,10 @@ def add_courseware_context(content_list, course, user, id_map=None):
for content in content_list:
commentable_id = content['commentable_id']
if commentable_id in id_map:
location = text_type(id_map[commentable_id]["location"])
location = str(id_map[commentable_id]["location"])
title = id_map[commentable_id]["title"]
url = reverse('jump_to', kwargs={"course_id": text_type(course.id),
url = reverse('jump_to', kwargs={"course_id": str(course.id),
"location": location})
content.update({"courseware_url": url, "courseware_title": title})
@@ -787,7 +782,7 @@ def prepare_content(content, course_key, is_staff=False, discussion_division_ena
endorser = User.objects.get(pk=endorsement["user_id"])
except User.DoesNotExist:
log.error(
u"User ID %s in endorsement for comment %s but not in our DB.",
"User ID %s in endorsement for comment %s but not in our DB.",
content.get('user_id'),
content.get('id')
)
@@ -946,7 +941,7 @@ def is_commentable_divided(course_key, commentable_id, course_discussion_setting
# inline discussions are divided by default
ans = True
log.debug(u"is_commentable_divided(%s, %s) = {%s}", course_key, commentable_id, ans)
log.debug("is_commentable_divided(%s, %s) = {%s}", course_key, commentable_id, ans)
return ans

View File

@@ -8,8 +8,8 @@ Enrollments.
from django.core.management.base import BaseCommand
from openedx.core.djangoapps.django_comment_common.models import assign_default_role_on_enrollment
from common.djangoapps.student.models import CourseEnrollment
from openedx.core.djangoapps.django_comment_common.models import assign_default_role_on_enrollment
class Command(BaseCommand): # lint-amnesty, pylint: disable=missing-class-docstring
@@ -26,5 +26,5 @@ class Command(BaseCommand): # lint-amnesty, pylint: disable=missing-class-docst
for i, enrollment in enumerate(CourseEnrollment.objects.filter(course_id=course_id, is_active=1), start=1):
assign_default_role_on_enrollment(None, enrollment)
if i % 1000 == 0:
print('{0}...'.format(i), end=' ')
print(f'{i}...', end=' ')
print()

View File

@@ -8,8 +8,8 @@ Enrollments.
from django.core.management.base import BaseCommand
from openedx.core.djangoapps.django_comment_common.models import assign_default_role_on_enrollment
from common.djangoapps.student.models import CourseEnrollment
from openedx.core.djangoapps.django_comment_common.models import assign_default_role_on_enrollment
class Command(BaseCommand): # lint-amnesty, pylint: disable=missing-class-docstring
@@ -20,5 +20,5 @@ class Command(BaseCommand): # lint-amnesty, pylint: disable=missing-class-docst
for i, enrollment in enumerate(CourseEnrollment.objects.filter(is_active=1), start=1):
assign_default_role_on_enrollment(None, enrollment)
if i % 1000 == 0:
print('{0}...'.format(i), end=' ')
print(f'{i}...', end=' ')
print()

View File

@@ -19,7 +19,7 @@ class Command(BaseCommand): # lint-amnesty, pylint: disable=missing-class-docst
course = get_course(course_key)
if not course:
raise CommandError(u'Invalid course id: {}'.format(course_id))
raise CommandError(f'Invalid course id: {course_id}')
if course.discussion_link:
self.stdout.write(course.discussion_link)

View File

@@ -24,7 +24,7 @@ class Command(BaseCommand): # lint-amnesty, pylint: disable=missing-class-docst
cc_user = cc.User.from_django_user(user)
cc_user.save()
except Exception as err: # lint-amnesty, pylint: disable=broad-except
print(u'update user info to discussion failed for user with id: {}, error={}'.format(user, str(err)))
print('update user info to discussion failed for user with id: {}, error={}'.format(user, str(err)))
def handle(self, *args, **options):
if len(options['usernames']) >= 1:

View File

@@ -20,16 +20,16 @@ class Command(BaseCommand): # lint-amnesty, pylint: disable=missing-class-docst
else:
user = User.objects.get(username=email_or_username)
except User.DoesNotExist:
print(u'User {} does not exist. '.format(email_or_username))
print(f'User {email_or_username} does not exist. ')
print('Available users: ')
print(User.objects.all())
return
roles = user.roles.all()
print(u'{} has %d roles:'.format(user, len(roles)))
print('{} has %d roles:'.format(user, len(roles)))
for role in roles:
print(u'\t{}'.format(role))
print(f'\t{role}')
for role in roles:
print(u'{} has permissions: '.format(role))
print(f'{role} has permissions: ')
print(role.permissions.all())

View File

@@ -1,9 +1,6 @@
# pylint: disable=consider-iterating-dictionary, missing-module-docstring
import json
import six
from django.contrib.auth.models import AnonymousUser
from django.core.exceptions import PermissionDenied
from django.http import Http404
@@ -13,6 +10,8 @@ from django.test.utils import override_settings
from django.urls import reverse
from mock import patch
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.util.testing import UrlResetMixin
from lms.djangoapps.discussion.notification_prefs import NOTIFICATION_PREF_KEY
from lms.djangoapps.discussion.notification_prefs.views import (
UsernameCipher,
@@ -22,8 +21,6 @@ from lms.djangoapps.discussion.notification_prefs.views import (
set_subscription
)
from openedx.core.djangoapps.user_api.models import UserPreference
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.util.testing import UrlResetMixin
@override_settings(SECRET_KEY="test secret key")
@@ -32,7 +29,7 @@ class NotificationPrefViewTest(UrlResetMixin, TestCase): # lint-amnesty, pylint
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(NotificationPrefViewTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.user = UserFactory.create(username="testuser")
# Tokens are intentionally hard-coded instead of computed to help us
# avoid breaking existing links.
@@ -45,7 +42,7 @@ class NotificationPrefViewTest(UrlResetMixin, TestCase): # lint-amnesty, pylint
UserFactory.create(username="thisusernameissoveryverylong"):
"AAAAAAAAAAAAAAAAAAAAAPECbYqPI7_W4mRF8LbTaHuHt3tNXPggZ1Bke-zDyEiZ",
# Non-ASCII username
UserFactory.create(username=u"\u4e2d\u56fd"):
UserFactory.create(username="\u4e2d\u56fd"):
"AAAAAAAAAAAAAAAAAAAAAMjfGAhZKIZsI3L-Z7nflTA="
}
self.request_factory = RequestFactory()
@@ -63,7 +60,7 @@ class NotificationPrefViewTest(UrlResetMixin, TestCase): # lint-amnesty, pylint
# now coerce username to utf-8 encoded str, since we test with non-ascii unicdoe above and
# the unittest framework has hard time coercing to unicode.
# decrypt also can't take a unicode input, so coerce its input to str
assert six.binary_type(user.username.encode('utf-8')) == UsernameCipher().decrypt(str(pref.value))
assert bytes(user.username.encode('utf-8')) == UsernameCipher().decrypt(str(pref.value))
def assertNotPrefExists(self, user):
"""Ensure that the user does not have a persisted preference"""
@@ -189,7 +186,7 @@ class NotificationPrefViewTest(UrlResetMixin, TestCase): # lint-amnesty, pylint
def test_unsubscribe_invalid_token(self):
def test_invalid_token(token, message):
request = self.request_factory.get("dummy")
self.assertRaisesRegex(Http404, "^{}$".format(message), set_subscription, request, token, False)
self.assertRaisesRegex(Http404, f"^{message}$", set_subscription, request, token, False)
# Invalid base64 encoding
test_invalid_token("ZOMG INVALID BASE64 CHARS!!!", "base64url")

View File

@@ -1,14 +1,13 @@
"""
Views to support notification preferences.
"""
import json
import os
from base64 import urlsafe_b64decode, urlsafe_b64encode
from binascii import Error
from hashlib import sha256
import six
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers.algorithms import AES
@@ -19,8 +18,6 @@ from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imp
from django.core.exceptions import PermissionDenied
from django.http import Http404, HttpResponse
from django.views.decorators.http import require_GET, require_POST
import six
from six import text_type
from common.djangoapps.edxmako.shortcuts import render_to_response
from lms.djangoapps.discussion.notification_prefs import NOTIFICATION_PREF_KEY
@@ -34,7 +31,7 @@ class UsernameDecryptionException(Exception):
pass
class UsernameCipher(object):
class UsernameCipher:
"""
A transformation of a username to/from an opaque token
@@ -191,7 +188,7 @@ def set_subscription(request, token, subscribe):
except UnicodeDecodeError:
raise Http404("base64url") # lint-amnesty, pylint: disable=raise-missing-from
except UsernameDecryptionException as exn:
raise Http404(text_type(exn)) # lint-amnesty, pylint: disable=raise-missing-from
raise Http404(str(exn)) # lint-amnesty, pylint: disable=raise-missing-from
except User.DoesNotExist:
raise Http404("username") # lint-amnesty, pylint: disable=raise-missing-from

View File

@@ -29,7 +29,7 @@ class DiscussionTab(TabFragmentViewMixin, EnrolledTab):
@classmethod
def is_enabled(cls, course, user=None):
if not super(DiscussionTab, cls).is_enabled(course, user):
if not super().is_enabled(course, user):
return False
# Disable the regular discussion tab if LTI-based external Discussion forum is enabled
if DiscussionLtiCourseTab.is_enabled(course, user):

View File

@@ -7,7 +7,6 @@ import itertools
from collections import defaultdict
from enum import Enum
import six
from django.core.exceptions import ValidationError
from django.http import Http404
from django.urls import reverse
@@ -64,11 +63,12 @@ from openedx.core.djangoapps.django_comment_common.signals import (
)
from openedx.core.djangoapps.django_comment_common.utils import get_course_discussion_settings
from openedx.core.djangoapps.user_api.accounts.api import get_account_settings
from openedx.core.djangoapps.user_api.accounts.views import AccountViewSet # lint-amnesty, pylint: disable=unused-import
from openedx.core.djangoapps.user_api.accounts.views import \
AccountViewSet # lint-amnesty, pylint: disable=unused-import
from openedx.core.lib.exceptions import CourseNotFoundError, DiscussionNotFoundError, PageNotFoundError
class DiscussionTopic(object):
class DiscussionTopic:
"""
Class for discussion topic structure
"""
@@ -177,7 +177,7 @@ def get_thread_list_url(request, course_key, topic_id_list=None, following=False
"""
path = reverse("thread-list")
query_list = (
[("course_id", six.text_type(course_key))] +
[("course_id", str(course_key))] +
[("topic_id", topic_id) for topic_id in topic_id_list or []] +
([("following", following)] if following else [])
)
@@ -228,7 +228,7 @@ def get_course(request, course_key):
course = _get_course(course_key, request.user)
return {
"id": six.text_type(course_key),
"id": str(course_key),
"blackouts": [
{
"start": _format_datetime(blackout["start"]),
@@ -369,7 +369,7 @@ def get_course_topics(request, course_key, topic_ids=None):
not_found_topic_ids = topic_ids - (existing_courseware_topic_ids | existing_non_courseware_topic_ids)
if not_found_topic_ids:
raise DiscussionNotFoundError(
u"Discussion not found for '{}'.".format(", ".join(str(id) for id in not_found_topic_ids))
"Discussion not found for '{}'.".format(", ".join(str(id) for id in not_found_topic_ids))
)
return {
@@ -576,18 +576,18 @@ def get_thread_list(
if order_by not in cc_map:
raise ValidationError({
"order_by":
[u"Invalid value. '{}' must be 'last_activity_at', 'comment_count', or 'vote_count'".format(order_by)]
[f"Invalid value. '{order_by}' must be 'last_activity_at', 'comment_count', or 'vote_count'"]
})
if order_direction != "desc":
raise ValidationError({
"order_direction": [u"Invalid value. '{}' must be 'desc'".format(order_direction)]
"order_direction": [f"Invalid value. '{order_direction}' must be 'desc'"]
})
course = _get_course(course_key, request.user)
context = get_context(course, request)
query_params = {
"user_id": six.text_type(request.user.id),
"user_id": str(request.user.id),
"group_id": (
None if context["is_requester_privileged"] else
get_group_id_for_user(request.user, get_course_discussion_settings(course.id))
@@ -603,13 +603,13 @@ def get_thread_list(
query_params[view] = "true"
else:
ValidationError({
"view": [u"Invalid value. '{}' must be 'unread' or 'unanswered'".format(view)]
"view": [f"Invalid value. '{view}' must be 'unread' or 'unanswered'"]
})
if following:
paginated_results = context["cc_requester"].subscribed_threads(query_params)
else:
query_params["course_id"] = six.text_type(course.id)
query_params["course_id"] = str(course.id)
query_params["commentable_ids"] = ",".join(topic_id_list) if topic_id_list else None
query_params["text"] = text_search
paginated_results = Thread.search(query_params)
@@ -1046,7 +1046,7 @@ def get_thread(request, thread_id, requested_fields=None):
thread_id,
retrieve_kwargs={
"with_responses": True,
"user_id": six.text_type(request.user.id),
"user_id": str(request.user.id),
}
)
return _serialize_discussion_entities(request, context, [cc_thread], requested_fields, DiscussionEntity.thread)[0]

View File

@@ -11,7 +11,6 @@ from django.forms import BooleanField, CharField, ChoiceField, Form, IntegerFiel
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import CourseLocator
from six import text_type
from lms.djangoapps.courseware.courses import get_course_with_access
from openedx.core.djangoapps.django_comment_common.models import (
@@ -75,7 +74,7 @@ class ThreadListGetForm(_PaginationForm):
try:
return CourseLocator.from_string(value)
except InvalidKeyError:
raise ValidationError(u"'{}' is not a valid course id".format(value)) # lint-amnesty, pylint: disable=raise-missing-from
raise ValidationError(f"'{value}' is not a valid course id") # lint-amnesty, pylint: disable=raise-missing-from
def clean_following(self):
"""Validate following"""
@@ -86,13 +85,13 @@ class ThreadListGetForm(_PaginationForm):
return value
def clean(self):
cleaned_data = super(ThreadListGetForm, self).clean() # lint-amnesty, pylint: disable=super-with-arguments
cleaned_data = super().clean()
exclusive_params_count = sum(
1 for param in self.EXCLUSIVE_PARAMS if cleaned_data.get(param)
)
if exclusive_params_count > 1:
raise ValidationError(
u"The following query parameters are mutually exclusive: {}".format(
"The following query parameters are mutually exclusive: {}".format(
", ".join(self.EXCLUSIVE_PARAMS)
)
)
@@ -143,7 +142,7 @@ class CourseDiscussionSettingsForm(Form):
def __init__(self, *args, **kwargs):
self.request_user = kwargs.pop('request_user')
super(CourseDiscussionSettingsForm, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
super().__init__(*args, **kwargs)
def clean_course_id(self):
"""Validate the 'course_id' value"""
@@ -154,7 +153,7 @@ class CourseDiscussionSettingsForm(Form):
self.cleaned_data['course_key'] = course_key
return course_id
except InvalidKeyError:
raise ValidationError(u"'{}' is not a valid course key".format(text_type(course_id))) # lint-amnesty, pylint: disable=raise-missing-from
raise ValidationError("'{}' is not a valid course key".format(str(course_id))) # lint-amnesty, pylint: disable=raise-missing-from
class CourseDiscussionRolesForm(CourseDiscussionSettingsForm):
@@ -168,7 +167,7 @@ class CourseDiscussionRolesForm(CourseDiscussionSettingsForm):
)
rolename = ChoiceField(
choices=ROLE_CHOICES,
error_messages={u"invalid_choice": u"Role '%(value)s' does not exist"}
error_messages={"invalid_choice": "Role '%(value)s' does not exist"}
)
def clean_rolename(self):
@@ -179,7 +178,7 @@ class CourseDiscussionRolesForm(CourseDiscussionSettingsForm):
try:
role = Role.objects.get(name=rolename, course_id=course_id)
except Role.DoesNotExist:
raise ValidationError(u"Role '{}' does not exist".format(rolename)) # lint-amnesty, pylint: disable=raise-missing-from
raise ValidationError(f"Role '{rolename}' does not exist") # lint-amnesty, pylint: disable=raise-missing-from
self.cleaned_data['role'] = role
return rolename

View File

@@ -7,7 +7,7 @@ from edx_rest_framework_extensions.paginators import NamespacedPageNumberPaginat
from rest_framework.utils.urls import replace_query_param
class _Page(object):
class _Page:
"""
Implements just enough of the django.core.paginator.Page interface to allow
PaginationSerializer to work.
@@ -51,7 +51,7 @@ class DiscussionAPIPagination(NamespacedPageNumberPagination):
self.base_url = request.build_absolute_uri()
self.count = result_count
super(DiscussionAPIPagination, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments
super().__init__()
def get_result_count(self):
"""

View File

@@ -9,6 +9,7 @@ from django.urls import reverse
from rest_framework import serializers
from six.moves.urllib.parse import urlencode, urlunparse
from common.djangoapps.student.models import get_user_by_username_or_email
from lms.djangoapps.discussion.django_comment_client.utils import (
course_discussion_division_enabled,
get_group_id_for_user,
@@ -34,7 +35,6 @@ from openedx.core.djangoapps.django_comment_common.models import (
Role
)
from openedx.core.djangoapps.django_comment_common.utils import get_course_discussion_settings
from common.djangoapps.student.models import get_user_by_username_or_email
def get_context(course, request, thread=None):
@@ -103,10 +103,10 @@ class _ContentSerializer(serializers.Serializer):
non_updatable_fields = set()
def __init__(self, *args, **kwargs):
super(_ContentSerializer, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
super().__init__(*args, **kwargs)
for field in self.non_updatable_fields:
setattr(self, "validate_{}".format(field), self._validate_non_updatable)
setattr(self, f"validate_{field}", self._validate_non_updatable)
def _validate_non_updatable(self, value):
"""Ensure that a field is not edited in an update operation."""
@@ -223,7 +223,7 @@ class ThreadSerializer(_ContentSerializer):
non_updatable_fields = NON_UPDATABLE_THREAD_FIELDS
def __init__(self, *args, **kwargs):
super(ThreadSerializer, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
super().__init__(*args, **kwargs)
# Compensate for the fact that some threads in the comments service do
# not have the pinned field set
if self.instance and self.instance.get("pinned") is None:
@@ -329,7 +329,7 @@ class CommentSerializer(_ContentSerializer):
def __init__(self, *args, **kwargs):
remove_fields = kwargs.pop('remove_fields', None)
super(CommentSerializer, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
super().__init__(*args, **kwargs)
if remove_fields:
# for multiple fields in a list
@@ -379,7 +379,7 @@ class CommentSerializer(_ContentSerializer):
def to_representation(self, data):
# pylint: disable=arguments-differ
data = super(CommentSerializer, self).to_representation(data) # lint-amnesty, pylint: disable=super-with-arguments
data = super().to_representation(data)
# Django Rest Framework v3 no longer includes None values
# in the representation. To maintain the previous behavior,
@@ -478,7 +478,7 @@ class DiscussionSettingsSerializer(serializers.Serializer):
def __init__(self, *args, **kwargs):
self.course = kwargs.pop('course')
self.discussion_settings = kwargs.pop('discussion_settings')
super(DiscussionSettingsSerializer, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
super().__init__(*args, **kwargs)
def validate(self, attrs):
"""
@@ -532,7 +532,7 @@ class DiscussionRolesSerializer(serializers.Serializer):
user_id = serializers.CharField()
def __init__(self, *args, **kwargs):
super(DiscussionRolesSerializer, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
super().__init__(*args, **kwargs)
self.user = None
def validate_user_id(self, user_id): # lint-amnesty, pylint: disable=missing-function-docstring
@@ -540,7 +540,7 @@ class DiscussionRolesSerializer(serializers.Serializer):
self.user = get_user_by_username_or_email(user_id)
return user_id
except DjangoUser.DoesNotExist:
raise ValidationError(u"'{}' is not a valid student identifier".format(user_id)) # lint-amnesty, pylint: disable=raise-missing-from
raise ValidationError(f"'{user_id}' is not a valid student identifier") # lint-amnesty, pylint: disable=raise-missing-from
def validate(self, attrs):
"""Validate the data at an object level."""
@@ -574,7 +574,7 @@ class DiscussionRolesMemberSerializer(serializers.Serializer):
group_name = serializers.SerializerMethodField()
def __init__(self, *args, **kwargs):
super(DiscussionRolesMemberSerializer, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
super().__init__(*args, **kwargs)
self.course_discussion_settings = self.context['course_discussion_settings']
def get_group_name(self, instance):

View File

@@ -5,19 +5,20 @@ Tests for Discussion API internal interface
import itertools
from datetime import datetime, timedelta
import pytest
from unittest import mock
import ddt
import httpretty
import mock
import six
import pytest
from django.core.exceptions import ValidationError
from django.test.client import RequestFactory
from opaque_keys.edx.locator import CourseLocator
from pytz import UTC
from rest_framework.exceptions import PermissionDenied
from six.moves import range
from six.moves.urllib.parse import parse_qs, urlencode, urlparse, urlunparse
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from common.djangoapps.util.testing import UrlResetMixin
from common.test.utils import MockSignalHandlerMixin, disable_signal
from lms.djangoapps.courseware.tests.factories import BetaTesterFactory, StaffFactory
from lms.djangoapps.discussion.django_comment_client.tests.utils import ForumsEnableMixin
@@ -57,8 +58,6 @@ from openedx.core.djangoapps.django_comment_common.models import (
Role
)
from openedx.core.lib.exceptions import CourseNotFoundError, PageNotFoundError
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from common.djangoapps.util.testing import UrlResetMixin
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
@@ -141,12 +140,12 @@ class GetCourseTest(ForumsEnableMixin, UrlResetMixin, SharedModuleStoreTestCase)
@classmethod
def setUpClass(cls):
super(GetCourseTest, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create(org="x", course="y", run="z")
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(GetCourseTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.user = UserFactory.create()
CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id)
self.request = RequestFactory().get("/dummy")
@@ -168,7 +167,7 @@ class GetCourseTest(ForumsEnableMixin, UrlResetMixin, SharedModuleStoreTestCase)
def test_basic(self):
assert get_course(self.request, self.course.id) == {
'id': six.text_type(self.course.id),
'id': str(self.course.id),
'blackouts': [],
'thread_list_url': 'http://testserver/api/discussion/v1/threads/?course_id=x%2Fy%2Fz',
'following_thread_list_url':
@@ -186,7 +185,7 @@ class GetCourseTestBlackouts(ForumsEnableMixin, UrlResetMixin, ModuleStoreTestCa
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(GetCourseTestBlackouts, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = CourseFactory.create(org="x", course="y", run="z")
self.user = UserFactory.create()
CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id)
@@ -223,7 +222,7 @@ class GetCourseTopicsTest(ForumsEnableMixin, UrlResetMixin, ModuleStoreTestCase)
"""Test for get_course_topics"""
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(GetCourseTopicsTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.maxDiff = None # pylint: disable=invalid-name
self.partition = UserPartition(
0,
@@ -266,7 +265,7 @@ class GetCourseTopicsTest(ForumsEnableMixin, UrlResetMixin, ModuleStoreTestCase)
"""
path = "http://testserver/api/discussion/v1/threads/"
topic_ids_to_query = [("topic_id", topic_id) for topic_id in topic_id_list]
query_list = [("course_id", six.text_type(self.course.id))] + topic_ids_to_query
query_list = [("course_id", str(self.course.id))] + topic_ids_to_query
return urlunparse(("", "", path, "", urlencode(query_list), ""))
def get_course_topics(self):
@@ -592,12 +591,12 @@ class GetThreadListTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMix
@classmethod
def setUpClass(cls):
super(GetThreadListTest, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create()
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(GetThreadListTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
httpretty.reset()
httpretty.enable()
self.addCleanup(httpretty.reset)
@@ -662,8 +661,8 @@ class GetThreadListTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMix
self.get_thread_list([], topic_id_list=["topic_x", "topic_meow"])
assert urlparse(httpretty.last_request().path).path == '/api/v1/threads' # lint-amnesty, pylint: disable=no-member
self.assert_last_query_params({
"user_id": [six.text_type(self.user.id)],
"course_id": [six.text_type(self.course.id)],
"user_id": [str(self.user.id)],
"course_id": [str(self.course.id)],
"sort_key": ["activity"],
"page": ["1"],
"per_page": ["1"],
@@ -673,8 +672,8 @@ class GetThreadListTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMix
def test_basic_query_params(self):
self.get_thread_list([], page=6, page_size=14)
self.assert_last_query_params({
"user_id": [six.text_type(self.user.id)],
"course_id": [six.text_type(self.course.id)],
"user_id": [str(self.user.id)],
"course_id": [str(self.course.id)],
"sort_key": ["activity"],
"page": ["6"],
"per_page": ["14"],
@@ -686,7 +685,7 @@ class GetThreadListTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMix
source_threads = [
make_minimal_cs_thread({
"id": "test_thread_id_0",
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"commentable_id": "topic_x",
"username": self.author.username,
"user_id": str(self.author.id),
@@ -702,7 +701,7 @@ class GetThreadListTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMix
}),
make_minimal_cs_thread({
"id": "test_thread_id_1",
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"commentable_id": "topic_y",
"group_id": self.cohort.id,
"username": self.author.username,
@@ -828,8 +827,8 @@ class GetThreadListTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMix
text_search='test search string'
).data == expected_result
self.assert_last_query_params({
"user_id": [six.text_type(self.user.id)],
"course_id": [six.text_type(self.course.id)],
"user_id": [str(self.user.id)],
"course_id": [str(self.course.id)],
"sort_key": ["activity"],
"page": ["1"],
"per_page": ["10"],
@@ -853,10 +852,10 @@ class GetThreadListTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMix
assert result == expected_result
assert urlparse(
httpretty.last_request().path # lint-amnesty, pylint: disable=no-member
).path == '/api/v1/users/{}/subscribed_threads'.format(self.user.id)
).path == f"/api/v1/users/{self.user.id}/subscribed_threads"
self.assert_last_query_params({
"user_id": [six.text_type(self.user.id)],
"course_id": [six.text_type(self.course.id)],
"user_id": [str(self.user.id)],
"course_id": [str(self.course.id)],
"sort_key": ["activity"],
"page": ["1"],
"per_page": ["11"],
@@ -880,8 +879,8 @@ class GetThreadListTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMix
assert result == expected_result
assert urlparse(httpretty.last_request().path).path == '/api/v1/threads' # lint-amnesty, pylint: disable=no-member
self.assert_last_query_params({
"user_id": [six.text_type(self.user.id)],
"course_id": [six.text_type(self.course.id)],
"user_id": [str(self.user.id)],
"course_id": [str(self.course.id)],
"sort_key": ["activity"],
"page": ["1"],
"per_page": ["11"],
@@ -918,8 +917,8 @@ class GetThreadListTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMix
assert result == expected_result
assert urlparse(httpretty.last_request().path).path == '/api/v1/threads' # lint-amnesty, pylint: disable=no-member
self.assert_last_query_params({
"user_id": [six.text_type(self.user.id)],
"course_id": [six.text_type(self.course.id)],
"user_id": [str(self.user.id)],
"course_id": [str(self.course.id)],
"sort_key": [cc_query],
"page": ["1"],
"per_page": ["11"],
@@ -946,8 +945,8 @@ class GetThreadListTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMix
assert result == expected_result
assert urlparse(httpretty.last_request().path).path == '/api/v1/threads' # lint-amnesty, pylint: disable=no-member
self.assert_last_query_params({
"user_id": [six.text_type(self.user.id)],
"course_id": [six.text_type(self.course.id)],
"user_id": [str(self.user.id)],
"course_id": [str(self.course.id)],
"sort_key": ["activity"],
"page": ["1"],
"per_page": ["11"],
@@ -976,12 +975,12 @@ class GetCommentListTest(ForumsEnableMixin, CommentsServiceMockMixin, SharedModu
@classmethod
def setUpClass(cls):
super(GetCommentListTest, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create()
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(GetCommentListTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
httpretty.reset()
httpretty.enable()
self.addCleanup(httpretty.reset)
@@ -1000,7 +999,7 @@ class GetCommentListTest(ForumsEnableMixin, CommentsServiceMockMixin, SharedModu
already in overrides.
"""
overrides = overrides.copy() if overrides else {}
overrides.setdefault("course_id", six.text_type(self.course.id))
overrides.setdefault("course_id", str(self.course.id))
return make_minimal_cs_thread(overrides)
def get_comment_list(self, thread, endorsed=None, page=1, page_size=1):
@@ -1031,7 +1030,7 @@ class GetCommentListTest(ForumsEnableMixin, CommentsServiceMockMixin, SharedModu
with pytest.raises(DiscussionDisabledError):
self.get_comment_list(
self.make_minimal_cs_thread(
overrides={"course_id": six.text_type(disabled_course.id)}
overrides={"course_id": str(disabled_course.id)}
)
)
@@ -1067,7 +1066,7 @@ class GetCommentListTest(ForumsEnableMixin, CommentsServiceMockMixin, SharedModu
cohort = CohortFactory.create(course_id=cohort_course.id, users=[self.user])
_assign_role_to_user(user=self.user, course_id=cohort_course.id, role=role_name)
thread = self.make_minimal_cs_thread({
"course_id": six.text_type(cohort_course.id),
"course_id": str(cohort_course.id),
"commentable_id": "test_topic",
"group_id": (
None if thread_group_state == "no_group" else
@@ -1319,7 +1318,7 @@ class GetCommentListTest(ForumsEnableMixin, CommentsServiceMockMixin, SharedModu
thread = self.make_minimal_cs_thread({
"thread_type": "question",
"endorsed_responses": [make_minimal_cs_comment({
"id": "comment_{}".format(i),
"id": f"comment_{i}",
"username": self.user.username
}) for i in range(10)]
})
@@ -1331,12 +1330,12 @@ class GetCommentListTest(ForumsEnableMixin, CommentsServiceMockMixin, SharedModu
"""
actual = self.get_comment_list(thread, endorsed=True, page=page, page_size=page_size).data
result_ids = [result["id"] for result in actual["results"]]
assert result_ids == ['comment_{}'.format(i) for i in range(expected_start, expected_stop)]
assert result_ids == [f"comment_{i}" for i in range(expected_start, expected_stop)]
assert actual['pagination']['next'] == (
'http://testserver/test_path?page={}'.format(expected_next) if expected_next else None
f"http://testserver/test_path?page={expected_next}" if expected_next else None
)
assert actual['pagination']['previous'] == (
'http://testserver/test_path?page={}'.format(expected_prev) if expected_prev else None
f"http://testserver/test_path?page={expected_prev}" if expected_prev else None
)
# Only page
@@ -1437,7 +1436,7 @@ class CreateThreadTest(
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(CreateThreadTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = CourseFactory.create()
httpretty.reset()
httpretty.enable()
@@ -1449,7 +1448,7 @@ class CreateThreadTest(
self.request.user = self.user
CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id)
self.minimal_data = {
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"topic_id": "test_topic",
"type": "discussion",
"title": "Test Title",
@@ -1468,13 +1467,13 @@ class CreateThreadTest(
actual = create_thread(self.request, self.minimal_data)
expected = self.expected_thread_data({
"id": "test_id",
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"comment_list_url": "http://testserver/api/discussion/v1/comments/?thread_id=test_id",
"read": True,
})
assert actual == expected
assert httpretty.last_request().parsed_body == { # lint-amnesty, pylint: disable=no-member
'course_id': [six.text_type(self.course.id)],
'course_id': [str(self.course.id)],
'commentable_id': ['test_topic'],
'thread_type': ['discussion'],
'title': ['Test Title'],
@@ -1532,7 +1531,7 @@ class CreateThreadTest(
expected = self.expected_thread_data({
"author_label": "Staff",
"id": "test_id",
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"comment_list_url": "http://testserver/api/discussion/v1/comments/?thread_id=test_id",
"read": True,
})
@@ -1540,7 +1539,7 @@ class CreateThreadTest(
self.assertEqual(
httpretty.last_request().parsed_body, # lint-amnesty, pylint: disable=no-member
{
"course_id": [six.text_type(self.course.id)],
"course_id": [str(self.course.id)],
"commentable_id": ["test_topic"],
"thread_type": ["discussion"],
"title": ["Test Title"],
@@ -1639,7 +1638,7 @@ class CreateThreadTest(
_assign_role_to_user(user=self.user, course_id=cohort_course.id, role=role_name)
self.register_post_thread_response({"username": self.user.username})
data = self.minimal_data.copy()
data["course_id"] = six.text_type(cohort_course.id)
data["course_id"] = str(cohort_course.id)
if data_group_state == "group_is_none":
data["group_id"] = None
elif data_group_state == "group_is_set":
@@ -1663,7 +1662,7 @@ class CreateThreadTest(
assert 'group_id' not in actual_post_data
except ValidationError as ex:
if not expected_error:
self.fail(u"Unexpected validation error: {}".format(ex))
self.fail(f"Unexpected validation error: {ex}")
def test_following(self):
self.register_post_thread_response({"id": "test_id", "username": self.user.username})
@@ -1673,7 +1672,7 @@ class CreateThreadTest(
result = create_thread(self.request, data)
assert result['following'] is True
cs_request = httpretty.last_request()
assert urlparse(cs_request.path).path == '/api/v1/users/{}/subscriptions'.format(self.user.id) # lint-amnesty, pylint: disable=no-member
assert urlparse(cs_request.path).path == f"/api/v1/users/{self.user.id}/subscriptions" # lint-amnesty, pylint: disable=no-member
assert cs_request.method == 'POST'
assert cs_request.parsed_body == {'source_type': ['thread'], 'source_id': ['test_id']} # lint-amnesty, pylint: disable=no-member
@@ -1723,7 +1722,7 @@ class CreateThreadTest(
def test_discussions_disabled(self):
disabled_course = _discussion_disabled_course_for(self.user)
self.minimal_data["course_id"] = six.text_type(disabled_course.id)
self.minimal_data["course_id"] = str(disabled_course.id)
with pytest.raises(DiscussionDisabledError):
create_thread(self.request, self.minimal_data)
@@ -1746,10 +1745,14 @@ class CreateCommentTest(
MockSignalHandlerMixin
):
"""Tests for create_comment"""
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.course = CourseFactory.create()
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(CreateCommentTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
httpretty.reset()
httpretty.enable()
self.course = CourseFactory.create()
@@ -1763,7 +1766,7 @@ class CreateCommentTest(
self.register_get_thread_response(
make_minimal_cs_thread({
"id": "test_thread",
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"commentable_id": "test_topic",
})
)
@@ -1815,12 +1818,12 @@ class CreateCommentTest(
}
assert actual == expected
expected_url = (
"/api/v1/comments/{}".format(parent_id) if parent_id else
f"/api/v1/comments/{parent_id}" if parent_id else
"/api/v1/threads/test_thread/comments"
)
assert urlparse(httpretty.last_request().path).path == expected_url # lint-amnesty, pylint: disable=no-member
assert httpretty.last_request().parsed_body == { # lint-amnesty, pylint: disable=no-member
'course_id': [six.text_type(self.course.id)],
'course_id': [str(self.course.id)],
'body': ['Test body'],
'user_id': [str(self.user.id)]
}
@@ -1905,7 +1908,7 @@ class CreateCommentTest(
self.assertEqual(
httpretty.last_request().parsed_body, # lint-amnesty, pylint: disable=no-member
{
"course_id": [six.text_type(self.course.id)],
"course_id": [str(self.course.id)],
"body": ["Test body"],
"user_id": [str(self.user.id)]
}
@@ -1960,7 +1963,7 @@ class CreateCommentTest(
self.register_get_thread_response(
make_minimal_cs_thread({
"id": "test_thread",
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"thread_type": thread_type,
"user_id": str(self.user.id) if is_thread_author else str(self.user.id + 1),
})
@@ -2031,7 +2034,7 @@ class CreateCommentTest(
self.register_get_thread_response(
make_minimal_cs_thread({
"id": "test_thread",
"course_id": six.text_type(disabled_course.id),
"course_id": str(disabled_course.id),
"commentable_id": "test_topic",
})
)
@@ -2055,7 +2058,7 @@ class CreateCommentTest(
cohort_course, cohort = _create_course_and_cohort_with_user_role(course_is_cohorted, self.user, role_name)
self.register_get_thread_response(make_minimal_cs_thread({
"id": "cohort_thread",
"course_id": six.text_type(cohort_course.id),
"course_id": str(cohort_course.id),
"group_id": (
None if thread_group_state == "no_group" else
cohort.id if thread_group_state == "match_group" else
@@ -2097,12 +2100,12 @@ class UpdateThreadTest(
"""Tests for update_thread"""
@classmethod
def setUpClass(cls):
super(UpdateThreadTest, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create()
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(UpdateThreadTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
httpretty.reset()
httpretty.enable()
self.addCleanup(httpretty.reset)
@@ -2122,7 +2125,7 @@ class UpdateThreadTest(
"""
cs_data = make_minimal_cs_thread({
"id": "test_thread",
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"commentable_id": "original_topic",
"username": self.user.username,
"user_id": str(self.user.id),
@@ -2156,7 +2159,7 @@ class UpdateThreadTest(
'title': 'Original Title'
})
assert httpretty.last_request().parsed_body == { # lint-amnesty, pylint: disable=no-member
'course_id': [six.text_type(self.course.id)],
'course_id': [str(self.course.id)],
'commentable_id': ['original_topic'],
'thread_type': ['discussion'],
'title': ['Original Title'],
@@ -2187,7 +2190,7 @@ class UpdateThreadTest(
def test_discussions_disabled(self):
disabled_course = _discussion_disabled_course_for(self.user)
self.register_thread(overrides={"course_id": six.text_type(disabled_course.id)})
self.register_thread(overrides={"course_id": str(disabled_course.id)})
with pytest.raises(DiscussionDisabledError):
update_thread(self.request, "test_thread", {})
@@ -2207,7 +2210,7 @@ class UpdateThreadTest(
def test_group_access(self, role_name, course_is_cohorted, thread_group_state):
cohort_course, cohort = _create_course_and_cohort_with_user_role(course_is_cohorted, self.user, role_name)
self.register_thread({
"course_id": six.text_type(cohort_course.id),
"course_id": str(cohort_course.id),
"group_id": (
None if thread_group_state == "no_group" else
cohort.id if thread_group_state == "match_group" else
@@ -2264,7 +2267,7 @@ class UpdateThreadTest(
result = update_thread(self.request, "test_thread", data)
assert result['following'] == new_following
last_request_path = urlparse(httpretty.last_request().path).path # lint-amnesty, pylint: disable=no-member
subscription_url = "/api/v1/users/{}/subscriptions".format(self.user.id)
subscription_url = f"/api/v1/users/{self.user.id}/subscriptions"
if old_following == new_following:
assert last_request_path != subscription_url
else:
@@ -2450,12 +2453,12 @@ class UpdateCommentTest(
@classmethod
def setUpClass(cls):
super(UpdateCommentTest, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create()
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(UpdateCommentTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
httpretty.reset()
httpretty.enable()
@@ -2479,7 +2482,7 @@ class UpdateCommentTest(
cs_thread_data = make_minimal_cs_thread({
"id": "test_thread",
"course_id": six.text_type(course.id)
"course_id": str(course.id)
})
cs_thread_data.update(thread_overrides or {})
self.register_get_thread_response(cs_thread_data)
@@ -2533,7 +2536,7 @@ class UpdateCommentTest(
assert actual == expected
assert httpretty.last_request().parsed_body == { # lint-amnesty, pylint: disable=no-member
'body': ['Edited body'],
'course_id': [six.text_type(self.course.id)],
'course_id': [str(self.course.id)],
'user_id': [str(self.user.id)],
'anonymous': ['False'],
'anonymous_to_peers': ['False'],
@@ -2581,7 +2584,7 @@ class UpdateCommentTest(
{"thread_id": "test_thread"},
thread_overrides={
"id": "test_thread",
"course_id": six.text_type(cohort_course.id),
"course_id": str(cohort_course.id),
"group_id": (
None if thread_group_state == "no_group" else
cohort.id if thread_group_state == "match_group" else
@@ -2827,12 +2830,12 @@ class DeleteThreadTest(
"""Tests for delete_thread"""
@classmethod
def setUpClass(cls):
super(DeleteThreadTest, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create()
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(DeleteThreadTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
httpretty.reset()
httpretty.enable()
self.addCleanup(httpretty.reset)
@@ -2852,7 +2855,7 @@ class DeleteThreadTest(
"""
cs_data = make_minimal_cs_thread({
"id": self.thread_id,
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"user_id": str(self.user.id),
})
cs_data.update(overrides or {})
@@ -2863,7 +2866,7 @@ class DeleteThreadTest(
self.register_thread()
with self.assert_signal_sent(api, 'thread_deleted', sender=None, user=self.user, exclude_args=('post',)):
assert delete_thread(self.request, self.thread_id) is None
assert urlparse(httpretty.last_request().path).path == '/api/v1/threads/{}'.format(self.thread_id) # lint-amnesty, pylint: disable=no-member
assert urlparse(httpretty.last_request().path).path == f"/api/v1/threads/{self.thread_id}" # lint-amnesty, pylint: disable=no-member
assert httpretty.last_request().method == 'DELETE'
def test_thread_id_not_found(self):
@@ -2884,7 +2887,7 @@ class DeleteThreadTest(
def test_discussions_disabled(self):
disabled_course = _discussion_disabled_course_for(self.user)
self.register_thread(overrides={"course_id": six.text_type(disabled_course.id)})
self.register_thread(overrides={"course_id": str(disabled_course.id)})
with pytest.raises(DiscussionDisabledError):
delete_thread(self.request, self.thread_id)
@@ -2928,7 +2931,7 @@ class DeleteThreadTest(
"""
cohort_course, cohort = _create_course_and_cohort_with_user_role(course_is_cohorted, self.user, role_name)
self.register_thread({
"course_id": six.text_type(cohort_course.id),
"course_id": str(cohort_course.id),
"group_id": (
None if thread_group_state == "no_group" else
cohort.id if thread_group_state == "match_group" else
@@ -2960,12 +2963,12 @@ class DeleteCommentTest(
"""Tests for delete_comment"""
@classmethod
def setUpClass(cls):
super(DeleteCommentTest, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create()
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(DeleteCommentTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
httpretty.reset()
httpretty.enable()
self.addCleanup(httpretty.reset)
@@ -2986,7 +2989,7 @@ class DeleteCommentTest(
"""
cs_thread_data = make_minimal_cs_thread({
"id": self.thread_id,
"course_id": six.text_type(self.course.id)
"course_id": str(self.course.id)
})
cs_thread_data.update(thread_overrides or {})
self.register_get_thread_response(cs_thread_data)
@@ -3005,7 +3008,7 @@ class DeleteCommentTest(
self.register_comment_and_thread()
with self.assert_signal_sent(api, 'comment_deleted', sender=None, user=self.user, exclude_args=('post',)):
assert delete_comment(self.request, self.comment_id) is None
assert urlparse(httpretty.last_request().path).path == '/api/v1/comments/{}'.format(self.comment_id) # lint-amnesty, pylint: disable=no-member
assert urlparse(httpretty.last_request().path).path == f"/api/v1/comments/{self.comment_id}" # lint-amnesty, pylint: disable=no-member
assert httpretty.last_request().method == 'DELETE'
def test_comment_id_not_found(self):
@@ -3029,8 +3032,8 @@ class DeleteCommentTest(
def test_discussions_disabled(self):
disabled_course = _discussion_disabled_course_for(self.user)
self.register_comment_and_thread(
thread_overrides={"course_id": six.text_type(disabled_course.id)},
overrides={"course_id": six.text_type(disabled_course.id)}
thread_overrides={"course_id": str(disabled_course.id)},
overrides={"course_id": str(disabled_course.id)}
)
with pytest.raises(DiscussionDisabledError):
delete_comment(self.request, self.comment_id)
@@ -3079,7 +3082,7 @@ class DeleteCommentTest(
self.register_comment_and_thread(
overrides={"thread_id": "test_thread"},
thread_overrides={
"course_id": six.text_type(cohort_course.id),
"course_id": str(cohort_course.id),
"group_id": (
None if thread_group_state == "no_group" else
cohort.id if thread_group_state == "match_group" else
@@ -3110,12 +3113,12 @@ class RetrieveThreadTest(
"""Tests for get_thread"""
@classmethod
def setUpClass(cls):
super(RetrieveThreadTest, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create()
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(RetrieveThreadTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
httpretty.reset()
httpretty.enable()
self.addCleanup(httpretty.reset)
@@ -3135,7 +3138,7 @@ class RetrieveThreadTest(
"""
cs_data = make_minimal_cs_thread({
"id": self.thread_id,
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"commentable_id": "test_topic",
"username": self.user.username,
"user_id": str(self.user.id),
@@ -3202,7 +3205,7 @@ class RetrieveThreadTest(
"""
cohort_course, cohort = _create_course_and_cohort_with_user_role(course_is_cohorted, self.user, role_name)
self.register_thread({
"course_id": six.text_type(cohort_course.id),
"course_id": str(cohort_course.id),
"group_id": (
None if thread_group_state == "no_group" else
cohort.id if thread_group_state == "match_group" else

View File

@@ -15,7 +15,7 @@ from lms.djangoapps.discussion.rest_api.forms import CommentListGetForm, ThreadL
from openedx.core.djangoapps.util.test_forms import FormTestMixin
class PaginationTestMixin(object):
class PaginationTestMixin:
"""A mixin for testing forms with pagination fields"""
def test_missing_page(self):
@@ -45,7 +45,7 @@ class ThreadListGetFormTest(FormTestMixin, PaginationTestMixin, TestCase):
FORM_CLASS = ThreadListGetForm
def setUp(self):
super(ThreadListGetFormTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.form_data = QueryDict(
urlencode(
{
@@ -159,7 +159,7 @@ class CommentListGetFormTest(FormTestMixin, PaginationTestMixin, TestCase):
FORM_CLASS = CommentListGetForm
def setUp(self):
super(CommentListGetFormTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.form_data = {
"thread_id": "deadbeef",
"endorsed": "False",

View File

@@ -12,7 +12,7 @@ from lms.djangoapps.discussion.rest_api.render import render_body
def _add_p_tags(raw_body):
"""Return raw_body surrounded by p tags"""
return "<p>{raw_body}</p>".format(raw_body=raw_body)
return f"<p>{raw_body}</p>"
@ddt.ddt
@@ -29,8 +29,8 @@ class RenderBodyTest(TestCase):
)
@ddt.unpack
def test_markdown_inline(self, delimiter, tag):
assert render_body(u'{delimiter}some text{delimiter}'.format(delimiter=delimiter)) == \
u'<p><{tag}>some text</{tag}></p>'.format(tag=tag)
assert render_body('{delimiter}some text{delimiter}'.format(delimiter=delimiter)) == \
'<p><{tag}>some text</{tag}></p>'.format(tag=tag)
@ddt.data(
"b", "blockquote", "code", "del", "dd", "dl", "dt", "em", "h1", "h2", "h3", "i", "kbd",
@@ -44,14 +44,14 @@ class RenderBodyTest(TestCase):
@ddt.data("br", "hr")
def test_selfclosing_tag(self, tag):
raw_body = "<{tag}>".format(tag=tag)
raw_body = f"<{tag}>"
is_inline_tag = tag == "br"
rendered_body = _add_p_tags(raw_body) if is_inline_tag else raw_body
assert render_body(raw_body) == rendered_body
@ddt.data("http", "https", "ftp")
def test_allowed_a_tag(self, protocol):
raw_body = '<a href="{protocol}://foo" title="bar">baz</a>'.format(protocol=protocol)
raw_body = f'<a href="{protocol}://foo" title="bar">baz</a>'
assert render_body(raw_body) == _add_p_tags(raw_body)
def test_disallowed_a_tag(self):
@@ -75,7 +75,7 @@ class RenderBodyTest(TestCase):
@ddt.data("p", "br", "li", "hr") # img is tested above
def test_allowed_unpaired_tags(self, tag):
raw_body = "foo<{tag}>bar".format(tag=tag)
raw_body = f"foo<{tag}>bar"
assert render_body(raw_body) == _add_p_tags(raw_body)
def test_unpaired_start_tag(self):

View File

@@ -4,14 +4,15 @@ Tests for Discussion API serializers
import itertools
from unittest import mock
import ddt
import httpretty
import mock
import six
from django.test.client import RequestFactory
from six.moves.urllib.parse import urlparse
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.util.testing import UrlResetMixin
from lms.djangoapps.discussion.django_comment_client.tests.utils import ForumsEnableMixin
from lms.djangoapps.discussion.rest_api.serializers import CommentSerializer, ThreadSerializer, get_context
from lms.djangoapps.discussion.rest_api.tests.utils import (
@@ -29,8 +30,6 @@ from openedx.core.djangoapps.django_comment_common.models import (
FORUM_ROLE_STUDENT,
Role
)
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.util.testing import UrlResetMixin
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
@@ -45,12 +44,12 @@ class SerializerTestMixin(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetM
@classmethod
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUpClass(cls):
super(SerializerTestMixin, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create()
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(SerializerTestMixin, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
httpretty.reset()
httpretty.enable()
self.addCleanup(httpretty.reset)
@@ -149,7 +148,7 @@ class ThreadSerializerSerializationTest(SerializerTestMixin, SharedModuleStoreTe
Create a thread with the given overrides, plus some useful test data.
"""
merged_overrides = {
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"user_id": str(self.author.id),
"username": self.author.username,
"read": True,
@@ -169,7 +168,7 @@ class ThreadSerializerSerializationTest(SerializerTestMixin, SharedModuleStoreTe
def test_basic(self):
thread = make_minimal_cs_thread({
"id": "test_thread",
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"commentable_id": "test_topic",
"user_id": str(self.author.id),
"username": self.author.username,
@@ -246,7 +245,7 @@ class ThreadSerializerSerializationTest(SerializerTestMixin, SharedModuleStoreTe
class CommentSerializerTest(SerializerTestMixin, SharedModuleStoreTestCase):
"""Tests for CommentSerializer."""
def setUp(self):
super(CommentSerializerTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.endorser = UserFactory.create()
self.endorsed_at = "2015-05-18T12:34:56Z"
@@ -412,12 +411,12 @@ class ThreadSerializerDeserializationTest(
@classmethod
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUpClass(cls):
super(ThreadSerializerDeserializationTest, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create()
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(ThreadSerializerDeserializationTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
httpretty.reset()
httpretty.enable()
self.addCleanup(httpretty.reset)
@@ -427,7 +426,7 @@ class ThreadSerializerDeserializationTest(
self.request = RequestFactory().get("/dummy")
self.request.user = self.user
self.minimal_data = {
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"topic_id": "test_topic",
"type": "discussion",
"title": "Test Title",
@@ -435,7 +434,7 @@ class ThreadSerializerDeserializationTest(
}
self.existing_thread = Thread(**make_minimal_cs_thread({
"id": "existing_thread",
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"commentable_id": "original_topic",
"thread_type": "discussion",
"title": "Original Title",
@@ -468,7 +467,7 @@ class ThreadSerializerDeserializationTest(
assert urlparse(httpretty.last_request().path).path ==\
'/api/v1/test_topic/threads' # lint-amnesty, pylint: disable=no-member
assert httpretty.last_request().parsed_body == { # lint-amnesty, pylint: disable=no-member
'course_id': [six.text_type(self.course.id)],
'course_id': [str(self.course.id)],
'commentable_id': ['test_topic'],
'thread_type': ['discussion'],
'title': ['Test Title'],
@@ -483,7 +482,7 @@ class ThreadSerializerDeserializationTest(
data["group_id"] = 42
self.save_and_reserialize(data)
assert httpretty.last_request().parsed_body == { # lint-amnesty, pylint: disable=no-member
'course_id': [six.text_type(self.course.id)],
'course_id': [str(self.course.id)],
'commentable_id': ['test_topic'],
'thread_type': ['discussion'],
'title': ['Test Title'],
@@ -524,7 +523,7 @@ class ThreadSerializerDeserializationTest(
self.register_put_thread_response(self.existing_thread.attributes)
self.save_and_reserialize({}, self.existing_thread)
assert httpretty.last_request().parsed_body == { # lint-amnesty, pylint: disable=no-member
'course_id': [six.text_type(self.course.id)],
'course_id': [str(self.course.id)],
'commentable_id': ['original_topic'],
'thread_type': ['discussion'],
'title': ['Original Title'],
@@ -549,7 +548,7 @@ class ThreadSerializerDeserializationTest(
}
saved = self.save_and_reserialize(data, self.existing_thread)
assert httpretty.last_request().parsed_body == { # lint-amnesty, pylint: disable=no-member
'course_id': [six.text_type(self.course.id)],
'course_id': [str(self.course.id)],
'commentable_id': ['edited_topic'],
'thread_type': ['question'],
'title': ['Edited Title'],
@@ -593,11 +592,11 @@ class CommentSerializerDeserializationTest(ForumsEnableMixin, CommentsServiceMoc
"""Tests for ThreadSerializer deserialization."""
@classmethod
def setUpClass(cls):
super(CommentSerializerDeserializationTest, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create()
def setUp(self):
super(CommentSerializerDeserializationTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
httpretty.reset()
httpretty.enable()
self.addCleanup(httpretty.reset)
@@ -616,7 +615,7 @@ class CommentSerializerDeserializationTest(ForumsEnableMixin, CommentsServiceMoc
"body": "Original body",
"user_id": str(self.user.id),
"username": self.user.username,
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
}))
def save_and_reserialize(self, data, instance=None):
@@ -627,7 +626,7 @@ class CommentSerializerDeserializationTest(ForumsEnableMixin, CommentsServiceMoc
context = get_context(
self.course,
self.request,
make_minimal_cs_thread({"course_id": six.text_type(self.course.id)})
make_minimal_cs_thread({"course_id": str(self.course.id)})
)
serializer = CommentSerializer(
instance,
@@ -652,12 +651,12 @@ class CommentSerializerDeserializationTest(ForumsEnableMixin, CommentsServiceMoc
)
saved = self.save_and_reserialize(data)
expected_url = (
"/api/v1/comments/{}".format(parent_id) if parent_id else
f"/api/v1/comments/{parent_id}" if parent_id else
"/api/v1/threads/test_thread/comments"
)
assert urlparse(httpretty.last_request().path).path == expected_url # lint-amnesty, pylint: disable=no-member
assert httpretty.last_request().parsed_body == { # lint-amnesty, pylint: disable=no-member
'course_id': [six.text_type(self.course.id)],
'course_id': [str(self.course.id)],
'body': ['Test body'],
'user_id': [str(self.user.id)]
}
@@ -676,7 +675,7 @@ class CommentSerializerDeserializationTest(ForumsEnableMixin, CommentsServiceMoc
)
self.save_and_reserialize(data)
assert httpretty.last_request().parsed_body == { # lint-amnesty, pylint: disable=no-member
'course_id': [six.text_type(self.course.id)],
'course_id': [str(self.course.id)],
'body': ['Test body'],
'user_id': [str(self.user.id)],
'endorsed': ['True']
@@ -754,7 +753,7 @@ class CommentSerializerDeserializationTest(ForumsEnableMixin, CommentsServiceMoc
data["endorsed"] = True
saved = self.save_and_reserialize(data)
assert httpretty.last_request().parsed_body == { # lint-amnesty, pylint: disable=no-member
'course_id': [six.text_type(self.course.id)],
'course_id': [str(self.course.id)],
'body': ['Test body'],
'user_id': [str(self.user.id)],
'endorsed': ['True']
@@ -769,7 +768,7 @@ class CommentSerializerDeserializationTest(ForumsEnableMixin, CommentsServiceMoc
self.save_and_reserialize({}, instance=self.existing_comment)
assert httpretty.last_request().parsed_body == { # lint-amnesty, pylint: disable=no-member
'body': ['Original body'],
'course_id': [six.text_type(self.course.id)],
'course_id': [str(self.course.id)],
'user_id': [str(self.user.id)],
'anonymous': ['False'],
'anonymous_to_peers': ['False'],
@@ -787,7 +786,7 @@ class CommentSerializerDeserializationTest(ForumsEnableMixin, CommentsServiceMoc
saved = self.save_and_reserialize(data, instance=self.existing_comment)
assert httpretty.last_request().parsed_body == { # lint-amnesty, pylint: disable=no-member
'body': ['Edited body'],
'course_id': [six.text_type(self.course.id)],
'course_id': [str(self.course.id)],
'user_id': [str(self.user.id)],
'anonymous': ['False'],
'anonymous_to_peers': ['False'],

View File

@@ -5,22 +5,23 @@ Tests for Discussion API views
import json
from datetime import datetime
from unittest import mock
import ddt
import httpretty
import mock
from django.urls import reverse
from opaque_keys.edx.keys import CourseKey
from pytz import UTC
from rest_framework.parsers import JSONParser
from rest_framework.test import APIClient, APITestCase
from six import text_type
from six.moves import range
from six.moves.urllib.parse import urlparse
from common.test.utils import disable_signal
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from common.djangoapps.student.models import get_retired_username_by_username
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, SuperuserFactory, UserFactory
from common.djangoapps.util.testing import PatchMediaTypeMixin, UrlResetMixin
from common.test.utils import disable_signal
from lms.djangoapps.discussion.django_comment_client.tests.utils import (
ForumsEnableMixin,
config_course_discussions,
@@ -38,12 +39,9 @@ from openedx.core.djangoapps.course_groups.tests.helpers import config_course_co
from openedx.core.djangoapps.django_comment_common.models import CourseDiscussionSettings, Role
from openedx.core.djangoapps.django_comment_common.utils import seed_permissions_roles
from openedx.core.djangoapps.oauth_dispatch.jwt import create_jwt_for_user
from openedx.core.djangoapps.oauth_dispatch.tests.factories import ApplicationFactory, AccessTokenFactory
from openedx.core.djangoapps.oauth_dispatch.tests.factories import AccessTokenFactory, ApplicationFactory
from openedx.core.djangoapps.user_api.accounts.image_helpers import get_profile_image_storage
from openedx.core.djangoapps.user_api.models import RetirementState, UserRetirementStatus
from common.djangoapps.student.models import get_retired_username_by_username
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, SuperuserFactory, UserFactory
from common.djangoapps.util.testing import PatchMediaTypeMixin, UrlResetMixin
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
@@ -61,7 +59,7 @@ class DiscussionAPIViewTestMixin(ForumsEnableMixin, CommentsServiceMockMixin, Ur
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(DiscussionAPIViewTestMixin, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.maxDiff = None # pylint: disable=invalid-name
self.course = CourseFactory.create(
org="x",
@@ -92,7 +90,7 @@ class DiscussionAPIViewTestMixin(ForumsEnableMixin, CommentsServiceMockMixin, Ur
"""
cs_thread = make_minimal_cs_thread({
"id": "test_thread",
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"commentable_id": "test_topic",
"username": self.user.username,
"user_id": str(self.user.id),
@@ -110,7 +108,7 @@ class DiscussionAPIViewTestMixin(ForumsEnableMixin, CommentsServiceMockMixin, Ur
"""
cs_comment = make_minimal_cs_comment({
"id": "test_comment",
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"thread_id": "test_thread",
"username": self.user.username,
"user_id": str(self.user.id),
@@ -139,8 +137,8 @@ class DiscussionAPIViewTestMixin(ForumsEnableMixin, CommentsServiceMockMixin, Ur
class CourseViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
"""Tests for CourseView"""
def setUp(self):
super(CourseViewTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
self.url = reverse("discussion_course", kwargs={"course_id": text_type(self.course.id)})
super().setUp()
self.url = reverse("discussion_course", kwargs={"course_id": str(self.course.id)})
def test_404(self):
response = self.client.get(
@@ -158,7 +156,7 @@ class CourseViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
response,
200,
{
"id": text_type(self.course.id),
"id": str(self.course.id),
"blackouts": [],
"thread_list_url": "http://testserver/api/discussion/v1/threads/?course_id=x%2Fy%2Fz",
"following_thread_list_url": (
@@ -174,7 +172,7 @@ class CourseViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
class RetireViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
"""Tests for CourseView"""
def setUp(self):
super(RetireViewTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
RetirementState.objects.create(state_name='PENDING', state_execution_order=1)
self.retire_forums_state = RetirementState.objects.create(state_name='RETIRE_FORUMS', state_execution_order=11)
@@ -248,7 +246,7 @@ class RetireViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
class ReplaceUsernamesViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
"""Tests for ReplaceUsernamesView"""
def setUp(self):
super(ReplaceUsernamesViewTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.client_user = UserFactory()
self.client_user.username = "test_replace_username_service_worker"
self.new_username = "test_username_replacement"
@@ -261,7 +259,7 @@ class ReplaceUsernamesViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
assert response.status_code == expected_status
if expected_content:
assert text_type(response.content) == expected_content
assert str(response.content) == expected_content
def build_jwt_headers(self, user):
"""
@@ -342,8 +340,8 @@ class CourseTopicsViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
Tests for CourseTopicsView
"""
def setUp(self):
super(CourseTopicsViewTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
self.url = reverse("course_topics", kwargs={"course_id": text_type(self.course.id)})
super().setUp()
self.url = reverse("course_topics", kwargs={"course_id": str(self.course.id)})
def create_course(self, modules_count, module_store, topics):
"""
@@ -358,15 +356,15 @@ class CourseTopicsViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
discussion_topics=topics
)
CourseEnrollmentFactory.create(user=self.user, course_id=course.id)
course_url = reverse("course_topics", kwargs={"course_id": text_type(course.id)})
course_url = reverse("course_topics", kwargs={"course_id": str(course.id)})
# add some discussion xblocks
for i in range(modules_count):
ItemFactory.create(
parent_location=course.location,
category='discussion',
discussion_id='id_module_{}'.format(i),
discussion_category='Category {}'.format(i),
discussion_target='Discussion {}'.format(i),
discussion_id=f'id_module_{i}',
discussion_category=f'Category {i}',
discussion_target=f'Discussion {i}',
publish_item=False,
)
return course_url
@@ -433,7 +431,7 @@ class CourseTopicsViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
"""
topic_id = "courseware-topic-id"
self.make_discussion_xblock(topic_id, "test_category", "test_target")
url = "{}?topic_id=invalid_topic_id".format(self.url)
url = f"{self.url}?topic_id=invalid_topic_id"
response = self.client.get(url)
self.assert_response_correct(
response,
@@ -449,7 +447,7 @@ class CourseTopicsViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
topic_id_2 = "topic_id_2"
self.make_discussion_xblock(topic_id_1, "test_category_1", "test_target_1")
self.make_discussion_xblock(topic_id_2, "test_category_2", "test_target_2")
url = "{}?topic_id=topic_id_1,topic_id_2".format(self.url)
url = f"{self.url}?topic_id=topic_id_1,topic_id_2"
response = self.client.get(url)
self.assert_response_correct(
response,
@@ -495,7 +493,7 @@ class CourseTopicsViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, ProfileImageTestMixin):
"""Tests for ThreadViewSet list"""
def setUp(self):
super(ThreadViewSetListTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.author = UserFactory.create()
self.url = reverse("thread-list")
@@ -505,7 +503,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
"""
thread = make_minimal_cs_thread({
"id": "test_thread",
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"commentable_id": "test_topic",
"user_id": str(self.user.id),
"username": self.user.username,
@@ -530,7 +528,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
)
def test_404(self):
response = self.client.get(self.url, {"course_id": text_type("non/existent/course")})
response = self.client.get(self.url, {"course_id": "non/existent/course"})
self.assert_response_correct(
response,
404,
@@ -553,7 +551,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
"editable_fields": ["abuse_flagged", "following", "read", "voted"],
})]
self.register_get_threads_response(source_threads, page=1, num_pages=2)
response = self.client.get(self.url, {"course_id": text_type(self.course.id), "following": ""})
response = self.client.get(self.url, {"course_id": str(self.course.id), "following": ""})
expected_response = make_paginated_api_response(
results=expected_threads,
count=1,
@@ -568,8 +566,8 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
expected_response
)
self.assert_last_query_params({
"user_id": [text_type(self.user.id)],
"course_id": [text_type(self.course.id)],
"user_id": [str(self.user.id)],
"course_id": [str(self.course.id)],
"sort_key": ["activity"],
"page": ["1"],
"per_page": ["10"],
@@ -583,13 +581,13 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
self.client.get(
self.url,
{
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"view": query,
}
)
self.assert_last_query_params({
"user_id": [text_type(self.user.id)],
"course_id": [text_type(self.course.id)],
"user_id": [str(self.user.id)],
"course_id": [str(self.course.id)],
"sort_key": ["activity"],
"page": ["1"],
"per_page": ["10"],
@@ -601,7 +599,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
self.register_get_threads_response([], page=1, num_pages=1)
response = self.client.get(
self.url,
{"course_id": text_type(self.course.id), "page": "18", "page_size": "4"}
{"course_id": str(self.course.id), "page": "18", "page_size": "4"}
)
self.assert_response_correct(
response,
@@ -609,8 +607,8 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
{"developer_message": "Page not found (No results on this page)."}
)
self.assert_last_query_params({
"user_id": [text_type(self.user.id)],
"course_id": [text_type(self.course.id)],
"user_id": [str(self.user.id)],
"course_id": [str(self.course.id)],
"sort_key": ["activity"],
"page": ["18"],
"per_page": ["4"],
@@ -621,7 +619,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
self.register_get_threads_search_response([], None, num_pages=0)
response = self.client.get(
self.url,
{"course_id": text_type(self.course.id), "text_search": "test search string"}
{"course_id": str(self.course.id), "text_search": "test search string"}
)
expected_response = make_paginated_api_response(
@@ -634,8 +632,8 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
expected_response
)
self.assert_last_query_params({
"user_id": [text_type(self.user.id)],
"course_id": [text_type(self.course.id)],
"user_id": [str(self.user.id)],
"course_id": [str(self.course.id)],
"sort_key": ["activity"],
"page": ["1"],
"per_page": ["10"],
@@ -649,7 +647,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
response = self.client.get(
self.url,
{
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"following": following,
}
)
@@ -665,14 +663,14 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
)
assert urlparse(
httpretty.last_request().path # lint-amnesty, pylint: disable=no-member
).path == '/api/v1/users/{}/subscribed_threads'.format(self.user.id)
).path == f"/api/v1/users/{self.user.id}/subscribed_threads"
@ddt.data(False, "false", "0")
def test_following_false(self, following):
response = self.client.get(
self.url,
{
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"following": following,
}
)
@@ -688,7 +686,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
response = self.client.get(
self.url,
{
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"following": "invalid-boolean",
}
)
@@ -720,13 +718,13 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
self.client.get(
self.url,
{
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"order_by": http_query,
}
)
self.assert_last_query_params({
"user_id": [text_type(self.user.id)],
"course_id": [text_type(self.course.id)],
"user_id": [str(self.user.id)],
"course_id": [str(self.course.id)],
"page": ["1"],
"per_page": ["10"],
"sort_key": [cc_query],
@@ -743,13 +741,13 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
self.client.get(
self.url,
{
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"order_direction": "desc",
}
)
self.assert_last_query_params({
"user_id": [text_type(self.user.id)],
"course_id": [text_type(self.course.id)],
"user_id": [str(self.user.id)],
"course_id": [str(self.course.id)],
"sort_key": ["activity"],
"page": ["1"],
"per_page": ["10"],
@@ -762,7 +760,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
self.register_get_user_response(self.user)
self.register_get_threads_search_response([], None, num_pages=0)
response = self.client.get(self.url, {
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"text_search": "test search string",
"topic_id": "topic1, topic2",
})
@@ -795,7 +793,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
response = self.client.get(
self.url,
{"course_id": text_type(self.course.id), "requested_fields": "profile_image"},
{"course_id": str(self.course.id), "requested_fields": "profile_image"},
)
assert response.status_code == 200
response_threads = json.loads(response.content.decode('utf-8'))['results']
@@ -820,7 +818,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
response = self.client.get(
self.url,
{"course_id": text_type(self.course.id), "requested_fields": "profile_image"},
{"course_id": str(self.course.id), "requested_fields": "profile_image"},
)
assert response.status_code == 200
response_thread = json.loads(response.content.decode('utf-8'))['results'][0]
@@ -834,7 +832,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
class ThreadViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
"""Tests for ThreadViewSet create"""
def setUp(self):
super(ThreadViewSetCreateTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.url = reverse("thread-list")
def test_basic(self):
@@ -846,7 +844,7 @@ class ThreadViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
})
self.register_post_thread_response(cs_thread)
request_data = {
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"topic_id": "test_topic",
"type": "discussion",
"title": "Test Title",
@@ -861,7 +859,7 @@ class ThreadViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
response_data = json.loads(response.content.decode('utf-8'))
assert response_data == self.expected_thread_data({'read': True})
assert httpretty.last_request().parsed_body == { # lint-amnesty, pylint: disable=no-member
'course_id': [text_type(self.course.id)],
'course_id': [str(self.course.id)],
'commentable_id': ['test_topic'],
'thread_type': ['discussion'],
'title': ['Test Title'],
@@ -897,7 +895,7 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest
"""Tests for ThreadViewSet partial_update"""
def setUp(self):
self.unsupported_media_type = JSONParser.media_type
super(ThreadViewSetPartialUpdateTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.url = reverse("thread-detail", kwargs={"thread_id": "test_thread"})
def test_basic(self):
@@ -923,7 +921,7 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest
'response_count': 2
})
assert httpretty.last_request().parsed_body == { # lint-amnesty, pylint: disable=no-member
'course_id': [text_type(self.course.id)],
'course_id': [str(self.course.id)],
'commentable_id': ['test_topic'],
'thread_type': ['discussion'],
'title': ['Test Title'],
@@ -1030,7 +1028,7 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest
class ThreadViewSetDeleteTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
"""Tests for ThreadViewSet delete"""
def setUp(self):
super(ThreadViewSetDeleteTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.url = reverse("thread-detail", kwargs={"thread_id": "test_thread"})
self.thread_id = "test_thread"
@@ -1038,7 +1036,7 @@ class ThreadViewSetDeleteTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
self.register_get_user_response(self.user)
cs_thread = make_minimal_cs_thread({
"id": self.thread_id,
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"username": self.user.username,
"user_id": str(self.user.id),
})
@@ -1047,7 +1045,7 @@ class ThreadViewSetDeleteTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
response = self.client.delete(self.url)
assert response.status_code == 204
assert response.content == b''
assert urlparse(httpretty.last_request().path).path == '/api/v1/threads/{}'.format(self.thread_id) # lint-amnesty, pylint: disable=no-member
assert urlparse(httpretty.last_request().path).path == f"/api/v1/threads/{self.thread_id}" # lint-amnesty, pylint: disable=no-member
assert httpretty.last_request().method == 'DELETE'
def test_delete_nonexistent_thread(self):
@@ -1062,7 +1060,7 @@ class ThreadViewSetDeleteTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
class CommentViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, ProfileImageTestMixin):
"""Tests for CommentViewSet list"""
def setUp(self):
super(CommentViewSetListTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.author = UserFactory.create()
self.url = reverse("comment-list")
self.thread_id = "test_thread"
@@ -1092,7 +1090,7 @@ class CommentViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pr
already in overrides.
"""
overrides = overrides.copy() if overrides else {}
overrides.setdefault("course_id", text_type(self.course.id))
overrides.setdefault("course_id", str(self.course.id))
return make_minimal_cs_thread(overrides)
def expected_response_comment(self, overrides=None):
@@ -1155,7 +1153,7 @@ class CommentViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pr
})]
self.register_get_thread_response({
"id": self.thread_id,
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"thread_type": "discussion",
"children": source_comments,
"resp_total": 100,
@@ -1192,7 +1190,7 @@ class CommentViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pr
self.register_get_user_response(self.user)
self.register_get_thread_response(make_minimal_cs_thread({
"id": self.thread_id,
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"thread_type": "discussion",
"resp_total": 10,
}))
@@ -1301,7 +1299,7 @@ class CommentViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pr
})
thread = self.make_minimal_cs_thread({
"id": self.thread_id,
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"thread_type": "discussion",
"children": [response_1, response_2],
"resp_total": 2,
@@ -1336,7 +1334,7 @@ class CommentViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pr
source_comments = [self.create_source_comment()]
self.register_get_thread_response({
"id": self.thread_id,
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"thread_type": "discussion",
"children": source_comments,
"resp_total": 100,
@@ -1437,7 +1435,7 @@ class CommentViewSetDeleteTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
"""Tests for ThreadViewSet delete"""
def setUp(self):
super(CommentViewSetDeleteTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.url = reverse("comment-detail", kwargs={"comment_id": "test_comment"})
self.comment_id = "test_comment"
@@ -1445,7 +1443,7 @@ class CommentViewSetDeleteTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
self.register_get_user_response(self.user)
cs_thread = make_minimal_cs_thread({
"id": "test_thread",
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
})
self.register_get_thread_response(cs_thread)
cs_comment = make_minimal_cs_comment({
@@ -1460,7 +1458,7 @@ class CommentViewSetDeleteTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
response = self.client.delete(self.url)
assert response.status_code == 204
assert response.content == b''
assert urlparse(httpretty.last_request().path).path == '/api/v1/comments/{}'.format(self.comment_id) # lint-amnesty, pylint: disable=no-member
assert urlparse(httpretty.last_request().path).path == f"/api/v1/comments/{self.comment_id}" # lint-amnesty, pylint: disable=no-member
assert httpretty.last_request().method == 'DELETE'
def test_delete_nonexistent_comment(self):
@@ -1475,7 +1473,7 @@ class CommentViewSetDeleteTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
class CommentViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
"""Tests for CommentViewSet create"""
def setUp(self):
super(CommentViewSetCreateTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.url = reverse("comment-list")
def test_basic(self):
@@ -1517,7 +1515,7 @@ class CommentViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
assert response_data == expected_response_data
assert urlparse(httpretty.last_request().path).path == '/api/v1/threads/test_thread/comments' # lint-amnesty, pylint: disable=no-member
assert httpretty.last_request().parsed_body == { # lint-amnesty, pylint: disable=no-member
'course_id': [text_type(self.course.id)],
'course_id': [str(self.course.id)],
'body': ['Test body'],
'user_id': [str(self.user.id)]
}
@@ -1558,7 +1556,7 @@ class CommentViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTes
"""Tests for CommentViewSet partial_update"""
def setUp(self):
self.unsupported_media_type = JSONParser.media_type
super(CommentViewSetPartialUpdateTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
httpretty.reset()
httpretty.enable()
self.addCleanup(httpretty.reset)
@@ -1610,7 +1608,7 @@ class CommentViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTes
})
assert httpretty.last_request().parsed_body == { # lint-amnesty, pylint: disable=no-member
'body': ['Edited body'],
'course_id': [text_type(self.course.id)],
'course_id': [str(self.course.id)],
'user_id': [str(self.user.id)],
'anonymous': ['False'],
'anonymous_to_peers': ['False'],
@@ -1666,7 +1664,7 @@ class CommentViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTes
class ThreadViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, ProfileImageTestMixin):
"""Tests for ThreadViewSet Retrieve"""
def setUp(self):
super(ThreadViewSetRetrieveTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.url = reverse("thread-detail", kwargs={"thread_id": "test_thread"})
self.thread_id = "test_thread"
@@ -1674,7 +1672,7 @@ class ThreadViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase,
self.register_get_user_response(self.user)
cs_thread = make_minimal_cs_thread({
"id": self.thread_id,
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"commentable_id": "test_topic",
"username": self.user.username,
"user_id": str(self.user.id),
@@ -1699,7 +1697,7 @@ class ThreadViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase,
self.register_get_user_response(self.user)
cs_thread = make_minimal_cs_thread({
"id": self.thread_id,
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"username": self.user.username,
"user_id": str(self.user.id),
})
@@ -1717,7 +1715,7 @@ class ThreadViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase,
class CommentViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, ProfileImageTestMixin):
"""Tests for CommentViewSet Retrieve"""
def setUp(self):
super(CommentViewSetRetrieveTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.url = reverse("comment-detail", kwargs={"comment_id": "test_comment"})
self.thread_id = "test_thread"
self.comment_id = "test_comment"
@@ -1729,7 +1727,7 @@ class CommentViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase
return make_minimal_cs_comment({
"id": comment_id,
"parent_id": parent_id,
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"thread_id": self.thread_id,
"thread_type": "discussion",
"username": self.user.username,
@@ -1746,7 +1744,7 @@ class CommentViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase
cs_comment = self.make_comment_data(self.comment_id, None, [cs_comment_child])
cs_thread = make_minimal_cs_thread({
"id": self.thread_id,
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"children": [cs_comment],
})
self.register_get_thread_response(cs_thread)
@@ -1794,7 +1792,7 @@ class CommentViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase
cs_comment = self.make_comment_data(self.comment_id, None, [cs_comment_child])
cs_thread = make_minimal_cs_thread({
"id": self.thread_id,
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"children": [cs_comment],
})
self.register_get_thread_response(cs_thread)
@@ -1818,7 +1816,7 @@ class CommentViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase
cs_comment = self.make_comment_data(self.comment_id, None, [cs_comment_child])
cs_thread = make_minimal_cs_thread({
'id': self.thread_id,
'course_id': text_type(self.course.id),
'course_id': str(self.course.id),
'children': [cs_comment],
})
self.register_get_thread_response(cs_thread)
@@ -1842,7 +1840,7 @@ class CourseDiscussionSettingsAPIViewTest(APITestCase, UrlResetMixin, ModuleStor
"""
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(CourseDiscussionSettingsAPIViewTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = CourseFactory.create(
org="x",
course="y",
@@ -1850,7 +1848,7 @@ class CourseDiscussionSettingsAPIViewTest(APITestCase, UrlResetMixin, ModuleStor
start=datetime.now(UTC),
discussion_topics={"Test Topic": {"id": "test_topic"}}
)
self.path = reverse('discussion_course_settings', kwargs={'course_id': text_type(self.course.id)})
self.path = reverse('discussion_course_settings', kwargs={'course_id': str(self.course.id)})
self.password = 'edx'
self.user = UserFactory(username='staff', password=self.password, is_staff=True)
@@ -1894,12 +1892,12 @@ class CourseDiscussionSettingsAPIViewTest(APITestCase, UrlResetMixin, ModuleStor
def _get_expected_response(self):
"""Return the default expected response before any changes to the discussion settings."""
return {
u'always_divide_inline_discussions': False,
u'divided_inline_discussions': [],
u'divided_course_wide_discussions': [],
u'id': 1,
u'division_scheme': u'cohort',
u'available_division_schemes': [u'cohort']
'always_divide_inline_discussions': False,
'divided_inline_discussions': [],
'divided_course_wide_discussions': [],
'id': 1,
'division_scheme': 'cohort',
'available_division_schemes': ['cohort']
}
def patch_request(self, data, headers=None):
@@ -2085,7 +2083,7 @@ class CourseDiscussionRolesAPIViewTest(APITestCase, UrlResetMixin, ModuleStoreTe
"""
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(CourseDiscussionRolesAPIViewTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = CourseFactory.create(
org="x",
course="y",
@@ -2100,7 +2098,7 @@ class CourseDiscussionRolesAPIViewTest(APITestCase, UrlResetMixin, ModuleStoreTe
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def path(self, course_id=None, role=None):
"""Return the URL path to the endpoint based on the provided arguments."""
course_id = text_type(self.course.id) if course_id is None else course_id
course_id = str(self.course.id) if course_id is None else course_id
role = 'Moderator' if role is None else role
return reverse(
'discussion_course_roles',

View File

@@ -10,7 +10,6 @@ from contextlib import closing
from datetime import datetime
import httpretty
import six
from PIL import Image
from pytz import UTC
@@ -68,7 +67,7 @@ def _get_comment_callback(comment_data, thread_id, parent_id):
return callback
class CommentsServiceMockMixin(object):
class CommentsServiceMockMixin:
"""Mixin with utility methods for mocking the comments service"""
def register_get_threads_response(self, threads, page, num_pages):
"""Register a mock response for GET on the CS thread list endpoint"""
@@ -128,7 +127,7 @@ class CommentsServiceMockMixin(object):
assert httpretty.is_enabled(), 'httpretty must be enabled to mock calls.'
httpretty.register_uri(
httpretty.GET,
"http://localhost:4567/api/v1/threads/{id}".format(id=thread_id),
f"http://localhost:4567/api/v1/threads/{thread_id}",
body="",
status=status_code
)
@@ -152,9 +151,9 @@ class CommentsServiceMockMixin(object):
specified.
"""
if parent_id:
url = "http://localhost:4567/api/v1/comments/{}".format(parent_id)
url = f"http://localhost:4567/api/v1/comments/{parent_id}"
else:
url = "http://localhost:4567/api/v1/threads/{}/comments".format(thread_id)
url = f"http://localhost:4567/api/v1/threads/{thread_id}/comments"
assert httpretty.is_enabled(), 'httpretty must be enabled to mock calls.'
httpretty.register_uri(
@@ -185,7 +184,7 @@ class CommentsServiceMockMixin(object):
assert httpretty.is_enabled(), 'httpretty must be enabled to mock calls.'
httpretty.register_uri(
httpretty.GET,
"http://localhost:4567/api/v1/comments/{id}".format(id=comment_id),
f"http://localhost:4567/api/v1/comments/{comment_id}",
body="",
status=status_code
)
@@ -208,7 +207,7 @@ class CommentsServiceMockMixin(object):
assert httpretty.is_enabled(), 'httpretty must be enabled to mock calls.'
httpretty.register_uri(
httpretty.GET,
"http://localhost:4567/api/v1/users/{id}".format(id=user.id),
f"http://localhost:4567/api/v1/users/{user.id}",
body=json.dumps({
"id": str(user.id),
"subscribed_thread_ids": subscribed_thread_ids or [],
@@ -222,7 +221,7 @@ class CommentsServiceMockMixin(object):
assert httpretty.is_enabled(), 'httpretty must be enabled to mock calls.'
httpretty.register_uri(
httpretty.POST,
"http://localhost:4567/api/v1/users/{id}/retire".format(id=user.id),
f"http://localhost:4567/api/v1/users/{user.id}/retire",
body=body,
status=status
)
@@ -231,7 +230,7 @@ class CommentsServiceMockMixin(object):
assert httpretty.is_enabled(), 'httpretty must be enabled to mock calls.'
httpretty.register_uri(
httpretty.POST,
"http://localhost:4567/api/v1/users/{id}/replace_username".format(id=user.id),
f"http://localhost:4567/api/v1/users/{user.id}/replace_username",
body=body,
status=status
)
@@ -241,7 +240,7 @@ class CommentsServiceMockMixin(object):
assert httpretty.is_enabled(), 'httpretty must be enabled to mock calls.'
httpretty.register_uri(
httpretty.GET,
"http://localhost:4567/api/v1/users/{}/subscribed_threads".format(user.id),
f"http://localhost:4567/api/v1/users/{user.id}/subscribed_threads",
body=json.dumps({
"collection": threads,
"page": page,
@@ -260,7 +259,7 @@ class CommentsServiceMockMixin(object):
for method in [httpretty.POST, httpretty.DELETE]:
httpretty.register_uri(
method,
"http://localhost:4567/api/v1/users/{id}/subscriptions".format(id=user.id),
f"http://localhost:4567/api/v1/users/{user.id}/subscriptions",
body=json.dumps({}), # body is unused
status=200
)
@@ -274,7 +273,7 @@ class CommentsServiceMockMixin(object):
for method in [httpretty.PUT, httpretty.DELETE]:
httpretty.register_uri(
method,
"http://localhost:4567/api/v1/threads/{}/votes".format(thread_id),
f"http://localhost:4567/api/v1/threads/{thread_id}/votes",
body=json.dumps({}), # body is unused
status=200
)
@@ -288,7 +287,7 @@ class CommentsServiceMockMixin(object):
for method in [httpretty.PUT, httpretty.DELETE]:
httpretty.register_uri(
method,
"http://localhost:4567/api/v1/comments/{}/votes".format(comment_id),
f"http://localhost:4567/api/v1/comments/{comment_id}/votes",
body=json.dumps({}), # body is unused
status=200
)
@@ -315,7 +314,7 @@ class CommentsServiceMockMixin(object):
assert httpretty.is_enabled(), 'httpretty must be enabled to mock calls.'
httpretty.register_uri(
httpretty.POST,
"http://localhost:4567/api/v1/users/{id}/read".format(id=user.id),
f"http://localhost:4567/api/v1/users/{user.id}/read",
params={'source_type': content_type, 'source_id': content_id},
body=json.dumps({}), # body is unused
status=200
@@ -336,7 +335,7 @@ class CommentsServiceMockMixin(object):
assert httpretty.is_enabled(), 'httpretty must be enabled to mock calls.'
httpretty.register_uri(
httpretty.DELETE,
"http://localhost:4567/api/v1/threads/{id}".format(id=thread_id),
f"http://localhost:4567/api/v1/threads/{thread_id}",
body=json.dumps({}), # body is unused
status=200
)
@@ -348,7 +347,7 @@ class CommentsServiceMockMixin(object):
assert httpretty.is_enabled(), 'httpretty must be enabled to mock calls.'
httpretty.register_uri(
httpretty.DELETE,
"http://localhost:4567/api/v1/comments/{id}".format(id=comment_id),
f"http://localhost:4567/api/v1/comments/{comment_id}",
body=json.dumps({}), # body is unused
status=200
)
@@ -392,7 +391,7 @@ class CommentsServiceMockMixin(object):
"voted": False,
"vote_count": 0,
"editable_fields": ["abuse_flagged", "following", "raw_body", "read", "title", "topic_id", "type", "voted"],
"course_id": six.text_type(self.course.id),
"course_id": str(self.course.id),
"topic_id": "test_topic",
"group_id": None,
"group_name": None,
@@ -494,7 +493,7 @@ def make_paginated_api_response(results=None, count=0, num_pages=0, next_link=No
}
class ProfileImageTestMixin(object):
class ProfileImageTestMixin:
"""
Mixin with utility methods for user profile image
"""

View File

@@ -39,14 +39,14 @@ urlpatterns = [
name="discussion_course_roles",
),
url(
r"^v1/courses/{}".format(settings.COURSE_ID_PATTERN),
fr"^v1/courses/{settings.COURSE_ID_PATTERN}",
CourseView.as_view(),
name="discussion_course"
),
url(r"^v1/accounts/retire_forum", RetireUserView.as_view(), name="retire_discussion_user"),
url(r"^v1/accounts/replace_username", ReplaceUsernamesView.as_view(), name="replace_discussion_username"),
url(
r"^v1/course_topics/{}".format(settings.COURSE_ID_PATTERN),
fr"^v1/course_topics/{settings.COURSE_ID_PATTERN}",
CourseTopicsView.as_view(),
name="course_topics"
),

View File

@@ -17,10 +17,8 @@ from rest_framework.parsers import JSONParser
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSet
from six import text_type
from lms.djangoapps.discussion.views import get_divided_discussions
from lms.djangoapps.instructor.access import update_forum_role
from common.djangoapps.util.json_request import JsonResponse
from lms.djangoapps.discussion.django_comment_client.utils import available_division_schemes
from lms.djangoapps.discussion.rest_api.api import (
create_comment,
@@ -48,6 +46,8 @@ from lms.djangoapps.discussion.rest_api.serializers import (
DiscussionRolesSerializer,
DiscussionSettingsSerializer
)
from lms.djangoapps.discussion.views import get_divided_discussions
from lms.djangoapps.instructor.access import update_forum_role
from openedx.core.djangoapps.django_comment_common import comment_client
from openedx.core.djangoapps.django_comment_common.models import Role
from openedx.core.djangoapps.django_comment_common.utils import (
@@ -57,10 +57,8 @@ from openedx.core.djangoapps.django_comment_common.utils import (
from openedx.core.djangoapps.user_api.accounts.permissions import CanReplaceUsername, CanRetireUser
from openedx.core.djangoapps.user_api.models import UserRetirementStatus
from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser
from openedx.core.lib.api.parsers import MergePatchParser
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, view_auth_classes
from common.djangoapps.util.json_request import JsonResponse
from xmodule.modulestore.django import modulestore
log = logging.getLogger(__name__)
@@ -594,7 +592,7 @@ class RetireUserView(APIView):
return Response(status=status.HTTP_404_NOT_FOUND)
raise
except Exception as exc: # pylint: disable=broad-except
return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR)
return Response(str(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR)
return Response(status=status.HTTP_204_NO_CONTENT)
@@ -661,7 +659,7 @@ class ReplaceUsernamesView(APIView):
cc_user.replace_username(new_username)
except User.DoesNotExist:
log.warning(
u"Unable to change username from %s to %s in forums because %s doesn't exist in LMS DB.",
"Unable to change username from %s to %s in forums because %s doesn't exist in LMS DB.",
current_username,
new_username,
new_username,
@@ -670,14 +668,14 @@ class ReplaceUsernamesView(APIView):
except comment_client.CommentClientRequestError as exc:
if exc.status_code == 404:
log.info(
u"Unable to change username from %s to %s in forums because user doesn't exist in forums",
"Unable to change username from %s to %s in forums because user doesn't exist in forums",
current_username,
new_username,
)
return True
else:
log.exception(
u"Unable to change username from %s to %s in forums because forums API call failed with: %s.",
"Unable to change username from %s to %s in forums because forums API call failed with: %s.",
current_username,
new_username,
exc,
@@ -685,7 +683,7 @@ class ReplaceUsernamesView(APIView):
return False
log.info(
u"Successfully changed username from %s to %s in forums.",
"Successfully changed username from %s to %s in forums.",
current_username,
new_username,
)
@@ -824,7 +822,7 @@ class CourseDiscussionSettingsAPIView(DeveloperErrorViewMixin, APIView):
try:
discussion_settings = set_course_discussion_settings(course_key, **settings_to_change)
except ValueError as e:
raise ValidationError(text_type(e)) # lint-amnesty, pylint: disable=raise-missing-from
raise ValidationError(str(e)) # lint-amnesty, pylint: disable=raise-missing-from
return Response(status=status.HTTP_204_NO_CONTENT)
@@ -937,7 +935,7 @@ class CourseDiscussionRolesAPIView(DeveloperErrorViewMixin, APIView):
try:
update_forum_role(course_id, user, rolename, action)
except Role.DoesNotExist:
raise ValidationError(u"Role '{}' does not exist".format(rolename)) # lint-amnesty, pylint: disable=raise-missing-from
raise ValidationError(f"Role '{rolename}' does not exist") # lint-amnesty, pylint: disable=raise-missing-from
role = form.cleaned_data['role']
data = {'course_id': course_id, 'users': role.users.all()}

View File

@@ -5,7 +5,6 @@ Signal handlers related to discussions.
import logging
import six
from django.conf import settings
from django.dispatch import receiver
from opaque_keys.edx.locator import LibraryLocator
@@ -33,7 +32,7 @@ def update_discussions_on_course_publish(sender, course_key, **kwargs): # pylin
return
context = {
'course_id': six.text_type(course_key),
'course_id': str(course_key),
}
tasks.update_discussions_map.apply_async(
args=[context],
@@ -45,16 +44,16 @@ def update_discussions_on_course_publish(sender, course_key, **kwargs): # pylin
def send_discussion_email_notification(sender, user, post, **kwargs): # lint-amnesty, pylint: disable=missing-function-docstring, unused-argument
current_site = get_current_site()
if current_site is None:
log.info(u'Discussion: No current site, not sending notification about post: %s.', post.id)
log.info('Discussion: No current site, not sending notification about post: %s.', post.id)
return
try:
if not current_site.configuration.get_value(ENABLE_FORUM_NOTIFICATIONS_FOR_SITE_KEY, False):
log_message = u'Discussion: notifications not enabled for site: %s. Not sending message about post: %s.'
log_message = 'Discussion: notifications not enabled for site: %s. Not sending message about post: %s.'
log.info(log_message, current_site, post.id)
return
except SiteConfiguration.DoesNotExist:
log_message = u'Discussion: No SiteConfiguration for site %s. Not sending message about post: %s.'
log_message = 'Discussion: No SiteConfiguration for site %s. Not sending message about post: %s.'
log.info(log_message, current_site, post.id)
return
@@ -64,7 +63,7 @@ def send_discussion_email_notification(sender, user, post, **kwargs): # lint-am
def send_message(comment, site): # lint-amnesty, pylint: disable=missing-function-docstring
thread = comment.thread
context = {
'course_id': six.text_type(thread.course_id),
'course_id': str(thread.course_id),
'comment_id': comment.id,
'comment_body': comment.body,
'comment_author_id': comment.user_id,

View File

@@ -6,7 +6,6 @@ pertaining to new discussion forum comments.
import logging
import six
from celery import shared_task
from celery_utils.logged_task import LoggedTask
from django.conf import settings # lint-amnesty, pylint: disable=unused-import
@@ -21,6 +20,7 @@ from opaque_keys.edx.keys import CourseKey
from six.moves.urllib.parse import urljoin
import openedx.core.djangoapps.django_comment_common.comment_client as cc
from common.djangoapps.track import segment
from lms.djangoapps.discussion.django_comment_client.utils import (
get_accessible_discussion_xblocks_by_course_id,
permalink
@@ -30,7 +30,6 @@ from openedx.core.djangoapps.ace_common.template_context import get_base_templat
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.django_comment_common.models import DiscussionsIdMapping
from openedx.core.lib.celery.task_utils import emulate_http_request
from common.djangoapps.track import segment
log = logging.getLogger(__name__)
@@ -51,7 +50,7 @@ def update_discussions_map(context):
course_key = CourseKey.from_string(context['course_id'])
discussion_blocks = get_accessible_discussion_xblocks_by_course_id(course_key, include_all=True)
discussions_id_map = {
discussion_block.discussion_id: six.text_type(discussion_block.location)
discussion_block.discussion_id: str(discussion_block.location)
for discussion_block in discussion_blocks
}
DiscussionsIdMapping.update_mapping(course_key, discussions_id_map)
@@ -76,7 +75,7 @@ def send_ace_message(context): # lint-amnesty, pylint: disable=missing-function
_get_course_language(context['course_id']),
message_context
)
log.info(u'Sending forum comment email notification with context %s', message_context)
log.info('Sending forum comment email notification with context %s', message_context)
ace.send(message)
_track_notification_sent(message, context)
@@ -89,10 +88,10 @@ def _track_notification_sent(message, context):
'app_label': 'discussion',
'name': 'responsenotification', # This is 'Campaign' in GA
'language': message.language,
'uuid': six.text_type(message.uuid),
'send_uuid': six.text_type(message.send_uuid),
'uuid': str(message.uuid),
'send_uuid': str(message.send_uuid),
'thread_id': context['thread_id'],
'course_id': six.text_type(context['course_id']),
'course_id': str(context['course_id']),
'thread_created_at': date.deserialize(context['thread_created_at']),
'nonInteraction': 1,
}

View File

@@ -1,7 +1,6 @@
## mako
<%!
import six
from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_string
%>
@@ -48,7 +47,7 @@ from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_str
$(function() {
require(['discussion/js/discussion_board_factory'], function (DiscussionBoardFactory) {
DiscussionBoardFactory({
courseId: '${six.text_type(course.id) | n, js_escaped_string}',
courseId: '${str(course.id) | n, js_escaped_string}',
$el: $(".discussion-board"),
rootUrl: '${root_url | n, js_escaped_string}',
userInfo: ${user_info | n, dump_js_escaped_json},

View File

@@ -6,7 +6,6 @@
<%!
import json
import six
from django.utils.translation import ugettext as _, ungettext
from django.template.defaultfilters import escapejs
@@ -21,7 +20,7 @@ from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_str
<%static:require_module module_name="discussion/js/discussion_profile_page_factory" class_name="DiscussionProfilePageFactory">
profile_page_context = {
'courseSettings': ${course_settings | n, dump_js_escaped_json},
'courseId': '${six.text_type(course.id) | n, js_escaped_string}',
'courseId': '${str(course.id) | n, js_escaped_string}',
'courseName': '${course.display_name_with_default | n, js_escaped_string}',
'contentInfo': ${annotated_content_info | n, dump_js_escaped_json},
'userInfo': ${user_info | n, dump_js_escaped_json},

View File

@@ -1,8 +1,6 @@
"""
Tests the forum notification signals.
"""
import mock
from django.test import TestCase
from edx_django_utils.cache import RequestCache

View File

@@ -6,10 +6,9 @@ Tests the execution of forum notification tasks.
import json
import math
from datetime import datetime, timedelta
from unittest import mock
import ddt
import mock
import six
from django.contrib.sites.models import Site
from edx_ace.channel import ChannelType, get_channel_for_message
from edx_ace.recipient import Recipient
@@ -17,6 +16,7 @@ from edx_ace.renderers import EmailRenderer
from edx_ace.utils import date
import openedx.core.djangoapps.django_comment_common.comment_client as cc
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from lms.djangoapps.discussion.signals.handlers import ENABLE_FORUM_NOTIFICATIONS_FOR_SITE_KEY
from lms.djangoapps.discussion.tasks import _should_send_message, _track_notification_sent
from openedx.core.djangoapps.ace_common.template_context import get_base_template_context
@@ -25,7 +25,6 @@ from openedx.core.djangoapps.django_comment_common.models import ForumsConfig
from openedx.core.djangoapps.django_comment_common.signals import comment_created
from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory
from openedx.core.lib.celery.task_utils import emulate_http_request
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
NOW = datetime.utcnow()
@@ -72,7 +71,7 @@ class TaskTestCase(ModuleStoreTestCase): # lint-amnesty, pylint: disable=missin
@classmethod
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUpClass(cls):
super(TaskTestCase, cls).setUpClass()
super().setUpClass()
cls.discussion_id = 'dummy_discussion_id'
cls.course = CourseOverviewFactory.create(language='fr')
@@ -109,7 +108,7 @@ class TaskTestCase(ModuleStoreTestCase): # lint-amnesty, pylint: disable=missin
def create_thread_and_comments(cls): # lint-amnesty, pylint: disable=missing-function-docstring
cls.thread = {
'id': cls.discussion_id,
'course_id': six.text_type(cls.course.id),
'course_id': str(cls.course.id),
'created_at': date.serialize(TWO_HOURS_AGO),
'title': 'thread-title',
'user_id': cls.thread_author.id,
@@ -147,7 +146,7 @@ class TaskTestCase(ModuleStoreTestCase): # lint-amnesty, pylint: disable=missin
cls.comment['child_count'] = 1
cls.thread2 = {
'id': cls.discussion_id,
'course_id': six.text_type(cls.course.id),
'course_id': str(cls.course.id),
'created_at': date.serialize(TWO_HOURS_AGO),
'title': 'thread-title',
'user_id': cls.thread_author.id,
@@ -156,7 +155,7 @@ class TaskTestCase(ModuleStoreTestCase): # lint-amnesty, pylint: disable=missin
}
def setUp(self):
super(TaskTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.request_patcher = mock.patch('requests.request')
self.mock_request = self.request_patcher.start()
@@ -168,7 +167,7 @@ class TaskTestCase(ModuleStoreTestCase): # lint-amnesty, pylint: disable=missin
self.mock_permalink = self.permalink_patcher.start()
def tearDown(self):
super(TaskTestCase, self).tearDown() # lint-amnesty, pylint: disable=super-with-arguments
super().tearDown()
self.request_patcher.stop()
self.ace_send_patcher.stop()
self.permalink_patcher.stop()

View File

@@ -6,22 +6,22 @@ Tests the forum notification views.
import json
import logging
from datetime import datetime
from unittest.mock import ANY, Mock, call, patch
import ddt
import pytest
import six
from django.http import Http404
from django.test.client import Client, RequestFactory
from django.test.utils import override_settings
from django.urls import reverse
from django.utils import translation
from edx_django_utils.cache import RequestCache
from mock import ANY, Mock, call, patch
from six import text_type
from six.moves import range
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from common.djangoapps.student.roles import CourseStaffRole, UserBasedRole
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from common.djangoapps.util.testing import EventTestMixin, UrlResetMixin
from lms.djangoapps.courseware.exceptions import CourseAccessRedirect
from lms.djangoapps.discussion import views
from lms.djangoapps.discussion.django_comment_client.constants import TYPE_ENTRY, TYPE_SUBCATEGORY
@@ -56,9 +56,6 @@ from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES
from openedx.core.lib.teams_config import TeamsConfig
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseTestConsentRequired
from common.djangoapps.student.roles import CourseStaffRole, UserBasedRole
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from common.djangoapps.util.testing import EventTestMixin, UrlResetMixin
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import (
@@ -81,7 +78,7 @@ class ViewsExceptionTestCase(UrlResetMixin, ModuleStoreTestCase): # lint-amnest
# Patching the ENABLE_DISCUSSION_SERVICE value affects the contents of urls.py,
# so we need to call super.setUp() which reloads urls.py (because
# of the UrlResetMixin)
super(ViewsExceptionTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# create a course
self.course = CourseFactory.create(org='MITx', course='999',
@@ -121,7 +118,7 @@ class ViewsExceptionTestCase(UrlResetMixin, ModuleStoreTestCase): # lint-amnest
mock_from_django_user.return_value = Mock()
url = reverse('user_profile',
kwargs={'course_id': text_type(self.course.id), 'user_id': '12345'}) # There is no user 12345
kwargs={'course_id': str(self.course.id), 'user_id': '12345'}) # There is no user 12345
response = self.client.get(url)
assert response.status_code == 404
@@ -138,7 +135,7 @@ class ViewsExceptionTestCase(UrlResetMixin, ModuleStoreTestCase): # lint-amnest
mock_from_django_user.return_value = Mock()
url = reverse('followed_threads',
kwargs={'course_id': text_type(self.course.id), 'user_id': '12345'}) # There is no user 12345
kwargs={'course_id': str(self.course.id), 'user_id': '12345'}) # There is no user 12345
response = self.client.get(url)
assert response.status_code == 404
@@ -176,7 +173,7 @@ def make_mock_thread_data( # lint-amnesty, pylint: disable=missing-function-doc
thread_data['is_commentable_divided'] = is_commentable_divided
if num_children is not None:
thread_data["children"] = [{
"id": "dummy_comment_id_{}".format(i),
"id": f"dummy_comment_id_{i}",
"type": "comment",
"body": text,
} for i in range(num_children)]
@@ -283,7 +280,7 @@ def make_mock_request_impl( # lint-amnesty, pylint: disable=missing-function-do
return mock_request_impl
class StringEndsWithMatcher(object): # lint-amnesty, pylint: disable=missing-class-docstring
class StringEndsWithMatcher: # lint-amnesty, pylint: disable=missing-class-docstring,eq-without-hash
def __init__(self, suffix):
self.suffix = suffix
@@ -291,14 +288,14 @@ class StringEndsWithMatcher(object): # lint-amnesty, pylint: disable=missing-cl
return other.endswith(self.suffix)
class PartialDictMatcher(object): # lint-amnesty, pylint: disable=missing-class-docstring
class PartialDictMatcher: # lint-amnesty, pylint: disable=missing-class-docstring,eq-without-hash
def __init__(self, expected_values):
self.expected_values = expected_values
def __eq__(self, other):
return all([
key in other and other[key] == value
for key, value in six.iteritems(self.expected_values)
for key, value in self.expected_values.items()
])
@@ -308,7 +305,7 @@ class SingleThreadTestCase(ForumsEnableMixin, ModuleStoreTestCase): # lint-amne
CREATE_USER = False
def setUp(self):
super(SingleThreadTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = CourseFactory.create(discussion_topics={'dummy discussion': {'id': 'dummy_discussion_id'}})
self.student = UserFactory.create()
@@ -326,7 +323,7 @@ class SingleThreadTestCase(ForumsEnableMixin, ModuleStoreTestCase): # lint-amne
request.user = self.student
response = views.single_thread(
request,
text_type(self.course.id),
str(self.course.id),
"dummy_discussion_id",
"test_thread_id"
)
@@ -365,7 +362,7 @@ class SingleThreadTestCase(ForumsEnableMixin, ModuleStoreTestCase): # lint-amne
request.user = self.student
response = views.single_thread(
request,
text_type(self.course.id),
str(self.course.id),
"dummy_discussion_id",
"test_thread_id"
)
@@ -398,7 +395,7 @@ class SingleThreadTestCase(ForumsEnableMixin, ModuleStoreTestCase): # lint-amne
request = RequestFactory().post("dummy_url")
response = views.single_thread(
request,
text_type(self.course.id),
str(self.course.id),
"dummy_discussion_id",
"dummy_thread_id"
)
@@ -413,7 +410,7 @@ class SingleThreadTestCase(ForumsEnableMixin, ModuleStoreTestCase): # lint-amne
Http404,
views.single_thread,
request,
text_type(self.course.id),
str(self.course.id),
"test_discussion_id",
"test_thread_id"
)
@@ -436,7 +433,7 @@ class SingleThreadTestCase(ForumsEnableMixin, ModuleStoreTestCase): # lint-amne
mocked.return_value = True
response = self.client.get(
reverse('single_thread', kwargs={
'course_id': six.text_type(self.course.id),
'course_id': str(self.course.id),
'discussion_id': discussion_topic_id,
'thread_id': thread_id,
})
@@ -464,7 +461,7 @@ class AllowPlusOrMinusOneInt(int): # pylint: disable=eq-without-hash
return other in self.values
def __repr__(self):
return "({} +/- 1)".format(self.value)
return f"({self.value} +/- 1)"
@ddt.ddt
@@ -532,7 +529,7 @@ class SingleThreadQueryCountTestCase(ForumsEnableMixin, ModuleStoreTestCase):
with patch.dict("django.conf.settings.FEATURES", dict(ENABLE_ENTERPRISE_INTEGRATION=enterprise_enabled)):
response = views.single_thread(
request,
text_type(course.id),
str(course.id),
"dummy_discussion_id",
test_thread_id
)
@@ -577,7 +574,7 @@ class SingleCohortedThreadTestCase(CohortedTestCase): # lint-amnesty, pylint: d
request.user = self.student
response = views.single_thread(
request,
text_type(self.course.id),
str(self.course.id),
"cohorted_topic",
mock_thread_id
)
@@ -601,7 +598,7 @@ class SingleCohortedThreadTestCase(CohortedTestCase): # lint-amnesty, pylint: d
self.client.login(username=self.student.username, password='test')
response = self.client.get(
reverse('single_thread', kwargs={
'course_id': six.text_type(self.course.id),
'course_id': str(self.course.id),
'discussion_id': "cohorted_topic",
'thread_id': mock_thread_id,
})
@@ -635,7 +632,7 @@ class SingleThreadAccessTestCase(CohortedTestCase): # lint-amnesty, pylint: dis
request.user = user
return views.single_thread(
request,
text_type(self.course.id),
str(self.course.id),
commentable_id,
thread_id
)
@@ -735,7 +732,7 @@ class SingleThreadGroupIdTestCase(CohortedTestCase, GroupIdAssertionMixin): # l
self.client.login(username=user.username, password='test')
return self.client.get(
reverse('single_thread', args=[six.text_type(self.course.id), commentable_id, "dummy_thread_id"]),
reverse('single_thread', args=[str(self.course.id), commentable_id, "dummy_thread_id"]),
data=request_data,
**headers
)
@@ -774,7 +771,7 @@ class ForumFormDiscussionContentGroupTestCase(ForumsEnableMixin, ContentGroupTes
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(ForumFormDiscussionContentGroupTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.thread_list = [
{"thread_id": "test_general_thread_id"},
{"thread_id": "test_global_group_thread_id", "commentable_id": self.global_module.discussion_id},
@@ -800,7 +797,7 @@ class ForumFormDiscussionContentGroupTestCase(ForumsEnableMixin, ContentGroupTes
)
self.client.login(username=user.username, password='test')
return self.client.get(
reverse("forum_form_discussion", args=[six.text_type(self.course.id)]),
reverse("forum_form_discussion", args=[str(self.course.id)]),
HTTP_X_REQUESTED_WITH="XMLHttpRequest"
)
@@ -854,7 +851,7 @@ class SingleThreadContentGroupTestCase(ForumsEnableMixin, UrlResetMixin, Content
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(SingleThreadContentGroupTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
def assert_can_access(self, user, discussion_id, thread_id, should_have_access):
"""
@@ -865,7 +862,7 @@ class SingleThreadContentGroupTestCase(ForumsEnableMixin, UrlResetMixin, Content
def call_single_thread():
self.client.login(username=user.username, password='test')
return self.client.get(
reverse('single_thread', args=[six.text_type(self.course.id), discussion_id, thread_id])
reverse('single_thread', args=[str(self.course.id), discussion_id, thread_id])
)
if should_have_access:
@@ -963,7 +960,7 @@ class SingleThreadContentGroupTestCase(ForumsEnableMixin, UrlResetMixin, Content
class InlineDiscussionContextTestCase(ForumsEnableMixin, ModuleStoreTestCase): # lint-amnesty, pylint: disable=missing-class-docstring
def setUp(self):
super(InlineDiscussionContextTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = CourseFactory.create()
CourseEnrollmentFactory(user=self.user, course_id=self.course.id)
self.discussion_topic_id = "dummy_topic"
@@ -989,7 +986,7 @@ class InlineDiscussionContextTestCase(ForumsEnableMixin, ModuleStoreTestCase):
response = views.inline_discussion(
request,
six.text_type(self.course.id),
str(self.course.id),
self.discussion_topic_id,
)
@@ -1012,7 +1009,7 @@ class InlineDiscussionContextTestCase(ForumsEnableMixin, ModuleStoreTestCase):
mocked.return_value = True
response = views.inline_discussion(
request,
six.text_type(self.course.id),
str(self.course.id),
self.discussion_topic_id,
)
assert response.status_code == 403
@@ -1028,7 +1025,7 @@ class InlineDiscussionGroupIdTestCase( # lint-amnesty, pylint: disable=missing-
cs_endpoint = "/threads"
def setUp(self):
super(InlineDiscussionGroupIdTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.cohorted_commentable_id = 'cohorted_topic'
def call_view(self, mock_request, commentable_id, user, group_id, pass_group_id=True):
@@ -1054,7 +1051,7 @@ class InlineDiscussionGroupIdTestCase( # lint-amnesty, pylint: disable=missing-
request.user = user
return views.inline_discussion(
request,
text_type(self.course.id),
str(self.course.id),
commentable_id
)
@@ -1089,7 +1086,7 @@ class ForumFormDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupIdT
self.client.login(username=user.username, password='test')
return self.client.get(
reverse("forum_form_discussion", args=[six.text_type(self.course.id)]),
reverse("forum_form_discussion", args=[str(self.course.id)]),
data=request_data,
**headers
)
@@ -1141,7 +1138,7 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
self.client.login(username=requesting_user.username, password='test')
return self.client.get(
reverse('user_profile', args=[six.text_type(self.course.id), profiled_user.id]),
reverse('user_profile', args=[str(self.course.id), profiled_user.id]),
data=request_data,
**headers
)
@@ -1295,7 +1292,7 @@ class FollowedThreadsDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGr
request.user = user
return views.followed_threads(
request,
text_type(self.course.id),
str(self.course.id),
user.id
)
@@ -1315,7 +1312,7 @@ class FollowedThreadsDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGr
class InlineDiscussionTestCase(ForumsEnableMixin, ModuleStoreTestCase): # lint-amnesty, pylint: disable=missing-class-docstring
def setUp(self):
super(InlineDiscussionTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = CourseFactory.create(
org="TestX",
@@ -1351,7 +1348,7 @@ class InlineDiscussionTestCase(ForumsEnableMixin, ModuleStoreTestCase): # lint-
course=self.course, text="dummy content", commentable_id=self.discussion1.discussion_id
)
return views.inline_discussion(
request, text_type(self.course.id), self.discussion1.discussion_id
request, str(self.course.id), self.discussion1.discussion_id
)
def test_context(self, mock_request):
@@ -1376,7 +1373,7 @@ class UserProfileTestCase(ForumsEnableMixin, UrlResetMixin, ModuleStoreTestCase)
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(UserProfileTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = CourseFactory.create()
self.student = UserFactory.create()
@@ -1392,7 +1389,7 @@ class UserProfileTestCase(ForumsEnableMixin, UrlResetMixin, ModuleStoreTestCase)
response = self.client.get(
reverse('user_profile', kwargs={
'course_id': six.text_type(self.course.id),
'course_id': str(self.course.id),
'user_id': self.profiled_user.id,
}),
data=params,
@@ -1400,10 +1397,10 @@ class UserProfileTestCase(ForumsEnableMixin, UrlResetMixin, ModuleStoreTestCase)
)
mock_request.assert_any_call(
"get",
StringEndsWithMatcher('/users/{}/active_threads'.format(self.profiled_user.id)),
StringEndsWithMatcher(f'/users/{self.profiled_user.id}/active_threads'),
data=None,
params=PartialDictMatcher({
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"page": params.get("page", 1),
"per_page": views.THREADS_PER_PAGE
}),
@@ -1421,13 +1418,10 @@ class UserProfileTestCase(ForumsEnableMixin, UrlResetMixin, ModuleStoreTestCase)
self.assertRegex(html, r'data-num-pages="1"')
self.assertRegex(html, r'<span class="discussion-count">1</span> discussion started')
self.assertRegex(html, r'<span class="discussion-count">2</span> comments')
self.assertRegex(html, u'&#39;id&#39;: &#39;{}&#39;'.format(self.TEST_THREAD_ID))
self.assertRegex(html, u'&#39;title&#39;: &#39;{}&#39;'.format(self.TEST_THREAD_TEXT))
self.assertRegex(html, u'&#39;body&#39;: &#39;{}&#39;'.format(self.TEST_THREAD_TEXT))
if six.PY2:
self.assertRegex(html, u'&#39;username&#39;: u&#39;{}&#39;'.format(self.student.username))
else:
self.assertRegex(html, u'&#39;username&#39;: &#39;{}&#39;'.format(self.student.username))
self.assertRegex(html, f'&#39;id&#39;: &#39;{self.TEST_THREAD_ID}&#39;')
self.assertRegex(html, f'&#39;title&#39;: &#39;{self.TEST_THREAD_TEXT}&#39;')
self.assertRegex(html, f'&#39;body&#39;: &#39;{self.TEST_THREAD_TEXT}&#39;')
self.assertRegex(html, f'&#39;username&#39;: &#39;{self.student.username}&#39;')
def check_ajax(self, mock_request, **params): # lint-amnesty, pylint: disable=missing-function-docstring
response = self.get_response(mock_request, params, HTTP_X_REQUESTED_WITH="XMLHttpRequest")
@@ -1459,7 +1453,7 @@ class UserProfileTestCase(ForumsEnableMixin, UrlResetMixin, ModuleStoreTestCase)
with pytest.raises(Http404):
views.user_profile(
request,
text_type(self.course.id),
str(self.course.id),
unenrolled_user.id
)
@@ -1469,7 +1463,7 @@ class UserProfileTestCase(ForumsEnableMixin, UrlResetMixin, ModuleStoreTestCase)
with pytest.raises(Http404):
views.user_profile(
request,
text_type(self.course.id),
str(self.course.id),
-999
)
@@ -1491,7 +1485,7 @@ class UserProfileTestCase(ForumsEnableMixin, UrlResetMixin, ModuleStoreTestCase)
request.user = self.student
response = views.user_profile(
request,
text_type(self.course.id),
str(self.course.id),
self.profiled_user.id
)
assert response.status_code == 405
@@ -1504,13 +1498,13 @@ class CommentsServiceRequestHeadersTestCase(ForumsEnableMixin, UrlResetMixin, Mo
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(CommentsServiceRequestHeadersTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
username = "foo"
password = "bar"
# Invoke UrlResetMixin
super(CommentsServiceRequestHeadersTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = CourseFactory.create(discussion_topics={'dummy discussion': {'id': 'dummy_discussion_id'}})
self.student = UserFactory.create(username=username, password=password)
CourseEnrollmentFactory.create(user=self.student, course_id=self.course.id)
@@ -1540,7 +1534,7 @@ class CommentsServiceRequestHeadersTestCase(ForumsEnableMixin, UrlResetMixin, Mo
reverse(
"single_thread",
kwargs={
"course_id": text_type(self.course.id),
"course_id": str(self.course.id),
"discussion_id": "dummy_discussion_id",
"thread_id": thread_id,
}
@@ -1556,7 +1550,7 @@ class CommentsServiceRequestHeadersTestCase(ForumsEnableMixin, UrlResetMixin, Mo
self.client.get(
reverse(
"forum_form_discussion",
kwargs={"course_id": text_type(self.course.id)}
kwargs={"course_id": str(self.course.id)}
),
)
self.assert_all_calls_have_header(mock_request, "X-Edx-Api-Key", "test_api_key")
@@ -1567,12 +1561,12 @@ class InlineDiscussionUnicodeTestCase(ForumsEnableMixin, SharedModuleStoreTestCa
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
with super(InlineDiscussionUnicodeTestCase, cls).setUpClassAndTestData():
with super().setUpClassAndTestData():
cls.course = CourseFactory.create()
@classmethod
def setUpTestData(cls):
super(InlineDiscussionUnicodeTestCase, cls).setUpTestData()
super().setUpTestData()
cls.student = UserFactory.create()
CourseEnrollmentFactory(user=cls.student, course_id=cls.course.id)
@@ -1584,7 +1578,7 @@ class InlineDiscussionUnicodeTestCase(ForumsEnableMixin, SharedModuleStoreTestCa
request.user = self.student
response = views.inline_discussion(
request, text_type(self.course.id), self.course.discussion_topics['General']['id']
request, str(self.course.id), self.course.discussion_topics['General']['id']
)
assert response.status_code == 200
response_data = json.loads(response.content.decode('utf-8'))
@@ -1597,12 +1591,12 @@ class ForumFormDiscussionUnicodeTestCase(ForumsEnableMixin, SharedModuleStoreTes
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
with super(ForumFormDiscussionUnicodeTestCase, cls).setUpClassAndTestData():
with super().setUpClassAndTestData():
cls.course = CourseFactory.create()
@classmethod
def setUpTestData(cls):
super(ForumFormDiscussionUnicodeTestCase, cls).setUpTestData()
super().setUpTestData()
cls.student = UserFactory.create()
CourseEnrollmentFactory(user=cls.student, course_id=cls.course.id)
@@ -1614,7 +1608,7 @@ class ForumFormDiscussionUnicodeTestCase(ForumsEnableMixin, SharedModuleStoreTes
request.user = self.student
request.META["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" # so request.is_ajax() == True
response = views.forum_form_discussion(request, text_type(self.course.id))
response = views.forum_form_discussion(request, str(self.course.id))
assert response.status_code == 200
response_data = json.loads(response.content.decode('utf-8'))
assert response_data['discussion_data'][0]['title'] == text
@@ -1627,7 +1621,7 @@ class ForumDiscussionXSSTestCase(ForumsEnableMixin, UrlResetMixin, ModuleStoreTe
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(ForumDiscussionXSSTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
username = "foo"
password = "bar"
@@ -1645,11 +1639,11 @@ class ForumDiscussionXSSTestCase(ForumsEnableMixin, UrlResetMixin, ModuleStoreTe
"""
mock_user.return_value.to_dict.return_value = {}
mock_req.return_value.status_code = 200
reverse_url = "%s%s" % (reverse(
reverse_url = "{}{}".format(reverse(
"forum_form_discussion",
kwargs={"course_id": six.text_type(self.course.id)}), '/forum_form_discussion')
kwargs={"course_id": str(self.course.id)}), '/forum_form_discussion')
# Test that malicious code does not appear in html
url = "%s?%s=%s" % (reverse_url, 'sort_key', malicious_code)
url = "{}?{}={}".format(reverse_url, 'sort_key', malicious_code)
resp = self.client.get(url)
self.assertNotContains(resp, malicious_code)
@@ -1669,9 +1663,9 @@ class ForumDiscussionXSSTestCase(ForumsEnableMixin, UrlResetMixin, ModuleStoreTe
mock_request.side_effect = make_mock_request_impl(course=self.course, text='dummy')
url = reverse('user_profile',
kwargs={'course_id': six.text_type(self.course.id), 'user_id': str(self.student.id)})
kwargs={'course_id': str(self.course.id), 'user_id': str(self.student.id)})
# Test that malicious code does not appear in html
url_string = "%s?%s=%s" % (url, 'page', malicious_code)
url_string = "{}?{}={}".format(url, 'page', malicious_code)
resp = self.client.get(url_string)
self.assertNotContains(resp, malicious_code)
@@ -1681,12 +1675,12 @@ class ForumDiscussionSearchUnicodeTestCase(ForumsEnableMixin, SharedModuleStoreT
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
with super(ForumDiscussionSearchUnicodeTestCase, cls).setUpClassAndTestData():
with super().setUpClassAndTestData():
cls.course = CourseFactory.create()
@classmethod
def setUpTestData(cls):
super(ForumDiscussionSearchUnicodeTestCase, cls).setUpTestData()
super().setUpTestData()
cls.student = UserFactory.create()
CourseEnrollmentFactory(user=cls.student, course_id=cls.course.id)
@@ -1702,7 +1696,7 @@ class ForumDiscussionSearchUnicodeTestCase(ForumsEnableMixin, SharedModuleStoreT
request.user = self.student
request.META["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" # so request.is_ajax() == True
response = views.forum_form_discussion(request, text_type(self.course.id))
response = views.forum_form_discussion(request, str(self.course.id))
assert response.status_code == 200
response_data = json.loads(response.content.decode('utf-8'))
assert response_data['discussion_data'][0]['title'] == text
@@ -1714,12 +1708,12 @@ class SingleThreadUnicodeTestCase(ForumsEnableMixin, SharedModuleStoreTestCase,
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
with super(SingleThreadUnicodeTestCase, cls).setUpClassAndTestData():
with super().setUpClassAndTestData():
cls.course = CourseFactory.create(discussion_topics={'dummy_discussion_id': {'id': 'dummy_discussion_id'}})
@classmethod
def setUpTestData(cls):
super(SingleThreadUnicodeTestCase, cls).setUpTestData()
super().setUpTestData()
cls.student = UserFactory.create()
CourseEnrollmentFactory(user=cls.student, course_id=cls.course.id)
@@ -1732,7 +1726,7 @@ class SingleThreadUnicodeTestCase(ForumsEnableMixin, SharedModuleStoreTestCase,
request.user = self.student
request.META["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" # so request.is_ajax() == True
response = views.single_thread(request, text_type(self.course.id), "dummy_discussion_id", thread_id)
response = views.single_thread(request, str(self.course.id), "dummy_discussion_id", thread_id)
assert response.status_code == 200
response_data = json.loads(response.content.decode('utf-8'))
assert response_data['content']['title'] == text
@@ -1744,12 +1738,12 @@ class UserProfileUnicodeTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, U
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
with super(UserProfileUnicodeTestCase, cls).setUpClassAndTestData():
with super().setUpClassAndTestData():
cls.course = CourseFactory.create()
@classmethod
def setUpTestData(cls):
super(UserProfileUnicodeTestCase, cls).setUpTestData()
super().setUpTestData()
cls.student = UserFactory.create()
CourseEnrollmentFactory(user=cls.student, course_id=cls.course.id)
@@ -1761,7 +1755,7 @@ class UserProfileUnicodeTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, U
request.user = self.student
request.META["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" # so request.is_ajax() == True
response = views.user_profile(request, text_type(self.course.id), str(self.student.id))
response = views.user_profile(request, str(self.course.id), str(self.student.id))
assert response.status_code == 200
response_data = json.loads(response.content.decode('utf-8'))
assert response_data['discussion_data'][0]['title'] == text
@@ -1773,12 +1767,12 @@ class FollowedThreadsUnicodeTestCase(ForumsEnableMixin, SharedModuleStoreTestCas
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
with super(FollowedThreadsUnicodeTestCase, cls).setUpClassAndTestData():
with super().setUpClassAndTestData():
cls.course = CourseFactory.create()
@classmethod
def setUpTestData(cls):
super(FollowedThreadsUnicodeTestCase, cls).setUpTestData()
super().setUpTestData()
cls.student = UserFactory.create()
CourseEnrollmentFactory(user=cls.student, course_id=cls.course.id)
@@ -1790,7 +1784,7 @@ class FollowedThreadsUnicodeTestCase(ForumsEnableMixin, SharedModuleStoreTestCas
request.user = self.student
request.META["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" # so request.is_ajax() == True
response = views.followed_threads(request, text_type(self.course.id), str(self.student.id))
response = views.followed_threads(request, str(self.course.id), str(self.student.id))
assert response.status_code == 200
response_data = json.loads(response.content.decode('utf-8'))
assert response_data['discussion_data'][0]['title'] == text
@@ -1805,7 +1799,7 @@ class EnrollmentTestCase(ForumsEnableMixin, ModuleStoreTestCase):
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super(EnrollmentTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = CourseFactory.create()
self.student = UserFactory.create()
@@ -1816,7 +1810,7 @@ class EnrollmentTestCase(ForumsEnableMixin, ModuleStoreTestCase):
request = RequestFactory().get('dummy_url')
request.user = self.student
with pytest.raises(CourseAccessRedirect):
views.forum_form_discussion(request, course_id=text_type(self.course.id)) # pylint: disable=no-value-for-parameter, unexpected-keyword-arg
views.forum_form_discussion(request, course_id=str(self.course.id)) # pylint: disable=no-value-for-parameter, unexpected-keyword-arg
@patch('requests.request', autospec=True)
@@ -1829,7 +1823,7 @@ class EnterpriseConsentTestCase(EnterpriseTestConsentRequired, ForumsEnableMixin
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
# Invoke UrlResetMixin setUp
super(EnterpriseConsentTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
username = "foo"
password = "bar"
@@ -1851,7 +1845,7 @@ class EnterpriseConsentTestCase(EnterpriseTestConsentRequired, ForumsEnableMixin
mock_enterprise_customer_for_request.return_value = None
thread_id = 'dummy'
course_id = six.text_type(self.course.id)
course_id = str(self.course.id)
mock_request.side_effect = make_mock_request_impl(course=self.course, text='dummy', thread_id=thread_id)
for url in (
@@ -1909,7 +1903,7 @@ class CourseDiscussionTopicsTestCase(DividedDiscussionsTestCase):
"""
Verify that we cannot access divide_discussion_topics if we're a non-staff user.
"""
self._verify_non_staff_cannot_access(views.discussion_topics, "GET", [six.text_type(self.course.id)])
self._verify_non_staff_cannot_access(views.discussion_topics, "GET", [str(self.course.id)])
def test_get_discussion_topics(self):
"""
@@ -1965,12 +1959,12 @@ class CourseDiscussionsHandlerTestCase(DividedDiscussionsTestCase):
Returns the static response dict.
"""
return {
u'always_divide_inline_discussions': False,
u'divided_inline_discussions': [],
u'divided_course_wide_discussions': [],
u'id': 1,
u'division_scheme': u'cohort',
u'available_division_schemes': [u'cohort']
'always_divide_inline_discussions': False,
'divided_inline_discussions': [],
'divided_course_wide_discussions': [],
'id': 1,
'division_scheme': 'cohort',
'available_division_schemes': ['cohort']
}
def test_non_staff(self):
@@ -1978,10 +1972,10 @@ class CourseDiscussionsHandlerTestCase(DividedDiscussionsTestCase):
Verify that we cannot access course_discussions_settings_handler if we're a non-staff user.
"""
self._verify_non_staff_cannot_access(
course_discussions_settings_handler, "GET", [six.text_type(self.course.id)]
course_discussions_settings_handler, "GET", [str(self.course.id)]
)
self._verify_non_staff_cannot_access(
course_discussions_settings_handler, "PATCH", [six.text_type(self.course.id)]
course_discussions_settings_handler, "PATCH", [str(self.course.id)]
)
def test_update_always_divide_inline_discussion_settings(self):
@@ -2161,7 +2155,7 @@ class ThreadViewedEventTestCase(EventTestMixin, ForumsEnableMixin, UrlResetMixin
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self): # pylint: disable=arguments-differ
super(ThreadViewedEventTestCase, self).setUp('eventtracking.tracker') # lint-amnesty, pylint: disable=super-with-arguments
super().setUp('eventtracking.tracker')
self.course = CourseFactory.create(
teams_configuration=TeamsConfig({
@@ -2206,8 +2200,8 @@ class ThreadViewedEventTestCase(EventTestMixin, ForumsEnableMixin, UrlResetMixin
thread_id=self.DUMMY_THREAD_ID,
commentable_id=self.category.discussion_id,
)
url = '/courses/{0}/discussion/forum/{1}/threads/{2}'.format(
six.text_type(self.course.id),
url = '/courses/{}/discussion/forum/{}/threads/{}'.format(
str(self.course.id),
self.category.discussion_id,
self.DUMMY_THREAD_ID
)

View File

@@ -6,7 +6,6 @@ Views handling read (GET) requests for the Discussion tab and inline discussions
import logging
from functools import wraps
import six
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
@@ -28,6 +27,8 @@ from web_fragments.fragment import Fragment
import lms.djangoapps.discussion.django_comment_client.utils as utils
import openedx.core.djangoapps.django_comment_common.comment_client as cc
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.util.json_request import JsonResponse, expect_json
from lms.djangoapps.courseware.access import has_access
from lms.djangoapps.courseware.courses import get_course_with_access
from lms.djangoapps.courseware.views.views import CourseTabView
@@ -57,8 +58,6 @@ from openedx.core.djangoapps.django_comment_common.utils import (
)
from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
from openedx.features.course_duration_limits.access import generate_course_expired_fragment
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.util.json_request import JsonResponse, expect_json
from xmodule.modulestore.django import modulestore
log = logging.getLogger("edx.discussions")
@@ -84,7 +83,7 @@ def make_course_settings(course, user, include_category_map=True):
'allow_anonymous': course.allow_anonymous,
'allow_anonymous_to_peers': course.allow_anonymous_to_peers,
'groups': [
{"id": str(group_id), "name": group_name} for group_id, group_name in six.iteritems(group_names_by_id)
{"id": str(group_id), "name": group_name} for group_id, group_name in group_names_by_id.items()
]
}
if include_category_map:
@@ -115,7 +114,7 @@ def get_threads(request, course, user_info, discussion_id=None, per_page=THREADS
'per_page': per_page,
'sort_key': 'activity',
'text': '',
'course_id': six.text_type(course.id),
'course_id': str(course.id),
'user_id': request.user.id,
'context': ThreadContext.COURSE,
'group_id': get_group_id_for_comments_service(request, course.id, discussion_id), # may raise ValueError
@@ -290,7 +289,7 @@ def forum_form_discussion(request, course_key):
'corrected_text': query_params['corrected_text'],
})
else:
course_id = six.text_type(course.id)
course_id = str(course.id)
tab_view = CourseTabView()
return tab_view.get(request, course_id, 'discussion')
@@ -344,7 +343,7 @@ def single_thread(request, course_key, discussion_id, thread_id):
'annotated_content_info': annotated_content_info,
})
else:
course_id = six.text_type(course.id)
course_id = str(course.id)
tab_view = CourseTabView()
return tab_view.get(request, course_id, 'discussion', discussion_id=discussion_id, thread_id=thread_id)
@@ -475,7 +474,7 @@ def _create_discussion_board_context(request, base_context, thread=None):
if "pinned" not in thread:
thread["pinned"] = False
thread_pages = 1
root_url = reverse('forum_form_discussion', args=[six.text_type(course.id)])
root_url = reverse('forum_form_discussion', args=[str(course.id)])
else:
threads, query_params = get_threads(request, course, user_info) # This might process a search query
thread_pages = query_params['num_pages']
@@ -608,7 +607,7 @@ def user_profile(request, course_key, user_id):
# 'user_profile' page
context['load_mathjax'] = False
return tab_view.get(request, six.text_type(course_key), 'discussion', profile_page_context=context)
return tab_view.get(request, str(course_key), 'discussion', profile_page_context=context)
except User.DoesNotExist:
raise Http404 # lint-amnesty, pylint: disable=raise-missing-from
except ValueError:
@@ -768,7 +767,7 @@ class DiscussionBoardFragmentView(EdxFragmentView):
return fragment
except TeamDiscussionHiddenFromUserException:
log.warning(
u'User with id={user_id} tried to view private discussion with id={discussion_id}'.format(
'User with id={user_id} tried to view private discussion with id={discussion_id}'.format(
user_id=request.user.id,
discussion_id=discussion_id
)
@@ -938,7 +937,7 @@ def course_discussions_settings_handler(request, course_key_string):
)
if not settings_to_change:
return JsonResponse({"error": six.text_type("Bad Request")}, 400)
return JsonResponse({"error": "Bad Request"}, 400)
try:
if settings_to_change:
@@ -946,7 +945,7 @@ def course_discussions_settings_handler(request, course_key_string):
except ValueError as err:
# Note: error message not translated because it is not exposed to the user (UI prevents this state).
return JsonResponse({"error": six.text_type(err)}, 400)
return JsonResponse({"error": str(err)}, 400)
divided_course_wide_discussions, divided_inline_discussions = get_divided_discussions(
course, discussion_settings