pyupgrade on survey, teams and tests apps (#26647)
This commit is contained in:
@@ -12,7 +12,7 @@ from lms.djangoapps.survey.models import SurveyForm
|
||||
class SurveyFormAdminForm(forms.ModelForm):
|
||||
"""Form providing validation of SurveyForm content."""
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = SurveyForm
|
||||
fields = ('name', 'form')
|
||||
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
from django.conf import settings
|
||||
|
||||
@@ -13,9 +13,9 @@ from lxml import etree
|
||||
from model_utils.models import TimeStampedModel
|
||||
from opaque_keys.edx.django.models import CourseKeyField
|
||||
|
||||
from openedx.core.djangolib.markup import HTML
|
||||
from common.djangoapps.student.models import User
|
||||
from lms.djangoapps.survey.exceptions import SurveyFormNameAlreadyExists, SurveyFormNotFound
|
||||
from openedx.core.djangolib.markup import HTML
|
||||
|
||||
log = logging.getLogger("edx.survey")
|
||||
|
||||
@@ -33,7 +33,7 @@ class SurveyForm(TimeStampedModel):
|
||||
name = models.CharField(max_length=255, db_index=True, unique=True)
|
||||
form = models.TextField()
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
app_label = 'survey'
|
||||
|
||||
def __str__(self):
|
||||
@@ -48,7 +48,7 @@ class SurveyForm(TimeStampedModel):
|
||||
self.validate_form_html(self.form)
|
||||
|
||||
# now call the actual save method
|
||||
super(SurveyForm, self).save(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def validate_form_html(cls, html):
|
||||
@@ -58,8 +58,8 @@ class SurveyForm(TimeStampedModel):
|
||||
try:
|
||||
fields = cls.get_field_names_from_html(html)
|
||||
except Exception as ex:
|
||||
log.exception(u"Cannot parse SurveyForm html: {}".format(ex))
|
||||
raise ValidationError(u"Cannot parse SurveyForm as HTML: {}".format(ex)) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
log.exception(f"Cannot parse SurveyForm html: {ex}")
|
||||
raise ValidationError(f"Cannot parse SurveyForm as HTML: {ex}") # lint-amnesty, pylint: disable=raise-missing-from
|
||||
|
||||
if not len(fields): # lint-amnesty, pylint: disable=len-as-condition
|
||||
raise ValidationError("SurveyForms must contain at least one form input field")
|
||||
@@ -152,7 +152,7 @@ class SurveyForm(TimeStampedModel):
|
||||
# make sure the form is wrap in some outer single element
|
||||
# otherwise lxml can't parse it
|
||||
# NOTE: This wrapping doesn't change the ability to query it
|
||||
tree = etree.fromstring(HTML(u'<div>{}</div>').format(HTML(html)))
|
||||
tree = etree.fromstring(HTML('<div>{}</div>').format(HTML(html)))
|
||||
|
||||
input_fields = (
|
||||
tree.findall('.//input') + tree.findall('.//select') +
|
||||
@@ -183,7 +183,7 @@ class SurveyAnswer(TimeStampedModel):
|
||||
# since it didn't exist in the beginning, it is nullable
|
||||
course_key = CourseKeyField(max_length=255, db_index=True, null=True)
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
app_label = 'survey'
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -5,8 +5,8 @@ Signal handlers for the survey app
|
||||
|
||||
from django.dispatch.dispatcher import receiver
|
||||
|
||||
from openedx.core.djangoapps.user_api.accounts.signals import USER_RETIRE_LMS_MISC
|
||||
from lms.djangoapps.survey.models import SurveyAnswer
|
||||
from openedx.core.djangoapps.user_api.accounts.signals import USER_RETIRE_LMS_MISC
|
||||
|
||||
|
||||
@receiver(USER_RETIRE_LMS_MISC)
|
||||
|
||||
@@ -6,7 +6,7 @@ from lms.djangoapps.survey.models import SurveyAnswer, SurveyForm
|
||||
|
||||
|
||||
class SurveyFormFactory(factory.DjangoModelFactory): # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = SurveyForm
|
||||
|
||||
name = 'Test Survey Form'
|
||||
@@ -14,7 +14,7 @@ class SurveyFormFactory(factory.DjangoModelFactory): # lint-amnesty, pylint: di
|
||||
|
||||
|
||||
class SurveyAnswerFactory(factory.DjangoModelFactory): # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = SurveyAnswer
|
||||
|
||||
user = factory.SubFactory(UserFactory)
|
||||
|
||||
@@ -4,8 +4,9 @@ Python tests for the Survey models
|
||||
|
||||
|
||||
from collections import OrderedDict
|
||||
import pytest
|
||||
|
||||
import ddt
|
||||
import pytest
|
||||
import six
|
||||
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
|
||||
from django.core.exceptions import ValidationError
|
||||
@@ -26,7 +27,7 @@ class SurveyModelsTests(TestCase):
|
||||
"""
|
||||
Set up the test data used in the specific tests
|
||||
"""
|
||||
super(SurveyModelsTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.client = Client()
|
||||
|
||||
# Create two accounts
|
||||
@@ -96,7 +97,7 @@ class SurveyModelsTests(TestCase):
|
||||
"""
|
||||
survey = self._create_test_survey()
|
||||
assert survey is not None
|
||||
assert six.text_type(survey) == self.test_survey_name
|
||||
assert str(survey) == self.test_survey_name
|
||||
|
||||
def test_create_form_with_malformed_html(self):
|
||||
"""
|
||||
@@ -189,7 +190,7 @@ class SurveyModelsTests(TestCase):
|
||||
|
||||
for answer_obj in answer_objs:
|
||||
if course_id:
|
||||
assert six.text_type(answer_obj.course_key) == course_id
|
||||
assert str(answer_obj.course_key) == course_id
|
||||
else:
|
||||
assert answer_obj.course_key is None
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ Test signal handlers for the survey app
|
||||
"""
|
||||
|
||||
|
||||
from lms.djangoapps.survey.signals import _listen_for_lms_retire
|
||||
from openedx.core.djangoapps.user_api.accounts.tests.retirement_helpers import fake_completed_retirement
|
||||
from common.djangoapps.student.tests.factories import UserFactory
|
||||
from lms.djangoapps.survey.models import SurveyAnswer
|
||||
from lms.djangoapps.survey.signals import _listen_for_lms_retire
|
||||
from lms.djangoapps.survey.tests.factories import SurveyAnswerFactory
|
||||
from openedx.core.djangoapps.user_api.accounts.tests.retirement_helpers import fake_completed_retirement
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class SurveyModelsTests(ModuleStoreTestCase):
|
||||
"""
|
||||
Set up the test data used in the specific tests
|
||||
"""
|
||||
super(SurveyModelsTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.client = Client()
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class SurveyViewsTests(ModuleStoreTestCase):
|
||||
"""
|
||||
Set up the test data used in the specific tests
|
||||
"""
|
||||
super(SurveyViewsTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.client = Client()
|
||||
|
||||
@@ -40,10 +40,10 @@ class SurveyViewsTests(ModuleStoreTestCase):
|
||||
'''
|
||||
|
||||
self.student_answers = OrderedDict({
|
||||
u'field1': u'value1',
|
||||
u'field2': u'value2',
|
||||
u'ddl': u'1',
|
||||
u'textarea': u'textarea'
|
||||
'field1': 'value1',
|
||||
'field2': 'value2',
|
||||
'ddl': '1',
|
||||
'textarea': 'textarea'
|
||||
})
|
||||
|
||||
self.course = CourseFactory.create(
|
||||
@@ -130,7 +130,7 @@ class SurveyViewsTests(ModuleStoreTestCase):
|
||||
|
||||
data['csrfmiddlewaretoken'] = 'foo'
|
||||
data['_redirect_url'] = 'bar'
|
||||
data['course_id'] = six.text_type(self.course.id)
|
||||
data['course_id'] = str(self.course.id)
|
||||
|
||||
resp = self.client.post(
|
||||
self.postback_url,
|
||||
@@ -149,7 +149,7 @@ class SurveyViewsTests(ModuleStoreTestCase):
|
||||
)
|
||||
|
||||
for answer_obj in answer_objs:
|
||||
assert six.text_type(answer_obj.course_key) == data['course_id']
|
||||
assert str(answer_obj.course_key) == data['course_id']
|
||||
|
||||
def test_encoding_answers(self):
|
||||
"""
|
||||
|
||||
@@ -16,9 +16,9 @@ class SurveyRequiredAccessError(AccessError):
|
||||
"""
|
||||
def __init__(self):
|
||||
error_code = "survey_required"
|
||||
developer_message = u"User must complete a survey"
|
||||
user_message = _(u"You must complete a survey")
|
||||
super(SurveyRequiredAccessError, self).__init__(error_code, developer_message, user_message) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
developer_message = "User must complete a survey"
|
||||
user_message = _("You must complete a survey")
|
||||
super().__init__(error_code, developer_message, user_message)
|
||||
|
||||
|
||||
def is_survey_required_for_course(course_descriptor):
|
||||
|
||||
@@ -15,8 +15,8 @@ from django.views.decorators.http import require_POST
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from common.djangoapps.edxmako.shortcuts import render_to_response
|
||||
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
|
||||
from lms.djangoapps.survey.models import SurveyForm
|
||||
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
|
||||
|
||||
log = logging.getLogger("edx.survey")
|
||||
|
||||
|
||||
@@ -11,12 +11,12 @@ from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from common.djangoapps.course_modes.models import CourseMode
|
||||
from common.djangoapps.student.models import CourseEnrollment, anonymous_id_for_user
|
||||
from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole
|
||||
from lms.djangoapps.courseware.courses import has_access
|
||||
from lms.djangoapps.discussion.django_comment_client.utils import has_discussion_privileges
|
||||
from lms.djangoapps.teams.models import CourseTeam, CourseTeamMembership
|
||||
from openedx.core.lib.teams_config import TeamsetType
|
||||
from common.djangoapps.student.models import CourseEnrollment, anonymous_id_for_user
|
||||
from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -349,7 +349,7 @@ def get_team_for_user_course_topic(user, course_id, topic_id):
|
||||
try:
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
except InvalidKeyError:
|
||||
raise ValueError(u"The supplied course id {course_id} is not valid.".format( # lint-amnesty, pylint: disable=raise-missing-from
|
||||
raise ValueError("The supplied course id {course_id} is not valid.".format( # lint-amnesty, pylint: disable=raise-missing-from
|
||||
course_id=course_id
|
||||
))
|
||||
try:
|
||||
|
||||
@@ -8,15 +8,16 @@ from collections import Counter
|
||||
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
|
||||
from django.db.models import Prefetch
|
||||
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from lms.djangoapps.program_enrollments.models import ProgramCourseEnrollment, ProgramEnrollment
|
||||
from lms.djangoapps.teams.api import (
|
||||
ORGANIZATION_PROTECTED_MODES,
|
||||
OrganizationProtectionStatus,
|
||||
user_organization_protection_status,
|
||||
ORGANIZATION_PROTECTED_MODES,
|
||||
user_protection_status_matches_team
|
||||
)
|
||||
from lms.djangoapps.teams.models import CourseTeam, CourseTeamMembership
|
||||
from lms.djangoapps.program_enrollments.models import ProgramCourseEnrollment, ProgramEnrollment
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
|
||||
from .utils import emit_team_event
|
||||
|
||||
|
||||
@@ -141,7 +142,7 @@ def _group_teamset_memberships_by_user(course_team_memberships):
|
||||
return teamset_memberships_by_user
|
||||
|
||||
|
||||
class TeamMembershipImportManager(object):
|
||||
class TeamMembershipImportManager:
|
||||
"""
|
||||
A manager class that is responsible the import process of csv file including validation and creation of
|
||||
team_courseteam and teams_courseteammembership objects.
|
||||
@@ -170,7 +171,7 @@ class TeamMembershipImportManager(object):
|
||||
"""
|
||||
Parse an input CSV file and pass to `set_team_memberships` for processing
|
||||
"""
|
||||
csv_reader = csv.DictReader((line.decode('utf-8-sig').strip() for line in input_file.readlines()))
|
||||
csv_reader = csv.DictReader(line.decode('utf-8-sig').strip() for line in input_file.readlines())
|
||||
return self.set_team_memberships(csv_reader)
|
||||
|
||||
def set_team_memberships(self, csv_reader):
|
||||
@@ -299,7 +300,7 @@ class TeamMembershipImportManager(object):
|
||||
Ensures that username exists only once in an input file
|
||||
"""
|
||||
if username in usernames_found_so_far:
|
||||
error_message = 'Username {} listed more than once in file.'.format(username)
|
||||
error_message = f'Username {username} listed more than once in file.'
|
||||
if self.add_error_and_check_if_max_exceeded(error_message):
|
||||
return False
|
||||
return True
|
||||
@@ -314,7 +315,7 @@ class TeamMembershipImportManager(object):
|
||||
This method will add a validation error and return False if this is the case.
|
||||
"""
|
||||
if None in row:
|
||||
error_message = "Team(s) {0} don't have matching teamsets.".format(
|
||||
error_message = "Team(s) {} don't have matching teamsets.".format(
|
||||
row[None]
|
||||
)
|
||||
if self.add_error_and_check_if_max_exceeded(error_message):
|
||||
@@ -374,7 +375,7 @@ class TeamMembershipImportManager(object):
|
||||
if self.is_FERPA_bubble_breached(teamset_id, team_name) or \
|
||||
not self.is_enrollment_protection_for_existing_team_matches_user(user, team_name, teamset_id):
|
||||
error_message = \
|
||||
'Team {} cannot have Master’s track users mixed with users in other tracks.'.format(team_name)
|
||||
f'Team {team_name} cannot have Master’s track users mixed with users in other tracks.'
|
||||
self.add_error_and_check_if_max_exceeded(error_message)
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -8,7 +8,6 @@ from textwrap import dedent
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.core.management import BaseCommand, CommandError
|
||||
from six.moves import map
|
||||
|
||||
from lms.djangoapps.teams.models import CourseTeam
|
||||
|
||||
@@ -38,7 +37,7 @@ class Command(BaseCommand):
|
||||
try:
|
||||
result = CourseTeam.objects.get(team_id=team_id)
|
||||
except ObjectDoesNotExist:
|
||||
raise CommandError('Argument {} is not a course_team team_id'.format(team_id)) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
raise CommandError(f'Argument {team_id} is not a course_team team_id') # lint-amnesty, pylint: disable=raise-missing-from
|
||||
|
||||
return result
|
||||
|
||||
@@ -67,5 +66,5 @@ class Command(BaseCommand):
|
||||
course_teams = list(map(self._get_course_team, options['course_team_ids']))
|
||||
|
||||
for course_team in course_teams:
|
||||
print('Indexing {}'.format(course_team.team_id))
|
||||
print(f'Indexing {course_team.team_id}')
|
||||
CourseTeamIndexer.index(course_team)
|
||||
|
||||
@@ -3,9 +3,10 @@ Tests for course_team reindex command.
|
||||
"""
|
||||
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import ddt
|
||||
from django.core.management import CommandError, call_command
|
||||
from mock import patch
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from search.search_engine_base import SearchEngine
|
||||
|
||||
@@ -27,7 +28,7 @@ class ReindexCourseTeamTest(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Set up tests.
|
||||
"""
|
||||
super(ReindexCourseTeamTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.team1 = CourseTeamFactory(course_id=COURSE_KEY1, team_id='team1')
|
||||
self.team2 = CourseTeamFactory(course_id=COURSE_KEY1, team_id='team2')
|
||||
@@ -61,8 +62,8 @@ class ReindexCourseTeamTest(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Test that raises CommandError for invalid team id.
|
||||
"""
|
||||
team_id = u'team4'
|
||||
error_str = u'Argument {} is not a course_team team_id'.format(team_id)
|
||||
team_id = 'team4'
|
||||
error_str = f'Argument {team_id} is not a course_team team_id'
|
||||
with self.assertRaisesRegex(CommandError, error_str):
|
||||
call_command('reindex_course_team', team_id)
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
import django_countries.fields
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
@@ -50,6 +47,6 @@ class Migration(migrations.Migration):
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='courseteammembership',
|
||||
unique_together=set([('user', 'team')]),
|
||||
unique_together={('user', 'team')},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.25 on 2019-10-22 14:42
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.25 on 2019-11-04 19:15
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.29 on 2020-03-18 15:36
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ from django_countries.fields import CountryField
|
||||
from model_utils import FieldTracker
|
||||
from opaque_keys.edx.django.models import CourseKeyField
|
||||
|
||||
from common.djangoapps.student.models import CourseEnrollment, LanguageField
|
||||
from lms.djangoapps.teams import TEAM_DISCUSSION_CONTEXT
|
||||
from lms.djangoapps.teams.utils import emit_team_event
|
||||
from openedx.core.djangoapps.django_comment_common.signals import (
|
||||
@@ -33,13 +34,12 @@ from openedx.core.djangoapps.django_comment_common.signals import (
|
||||
thread_unfollowed,
|
||||
thread_voted
|
||||
)
|
||||
from common.djangoapps.student.models import CourseEnrollment, LanguageField
|
||||
|
||||
from .errors import (
|
||||
AddToIncompatibleTeamError,
|
||||
AlreadyOnTeamInTeamset,
|
||||
ImmutableMembershipFieldException,
|
||||
NotEnrolledInCourseForTeam,
|
||||
AddToIncompatibleTeamError
|
||||
NotEnrolledInCourseForTeam
|
||||
)
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ class CourseTeam(models.Model):
|
||||
.. no_pii:
|
||||
"""
|
||||
def __str__(self):
|
||||
return "{} in {}".format(self.name, self.course_id)
|
||||
return f"{self.name} in {self.course_id}"
|
||||
|
||||
def __repr__(self):
|
||||
return ( # lint-amnesty, pylint: disable=missing-format-attribute
|
||||
@@ -125,7 +125,7 @@ class CourseTeam(models.Model):
|
||||
">"
|
||||
).format(self)
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
app_label = "teams"
|
||||
|
||||
team_id = models.SlugField(max_length=255, unique=True)
|
||||
@@ -233,7 +233,7 @@ class CourseTeamMembership(models.Model):
|
||||
"""
|
||||
|
||||
def __str__(self):
|
||||
return "{} is member of {}".format(self.user.username, self.team)
|
||||
return f"{self.user.username} is member of {self.team}"
|
||||
|
||||
def __repr__(self):
|
||||
return ( # lint-amnesty, pylint: disable=missing-format-attribute
|
||||
@@ -244,7 +244,7 @@ class CourseTeamMembership(models.Model):
|
||||
">"
|
||||
).format(self)
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
app_label = "teams"
|
||||
unique_together = (('user', 'team'),)
|
||||
|
||||
@@ -275,9 +275,9 @@ class CourseTeamMembership(models.Model):
|
||||
# Allow it *only* if the current value is None.
|
||||
if current_value is not None:
|
||||
raise ImmutableMembershipFieldException(
|
||||
u"Field %r shouldn't change from %r to %r" % (name, current_value, value)
|
||||
f"Field {name!r} shouldn't change from {current_value!r} to {value!r}"
|
||||
)
|
||||
super(CourseTeamMembership, self).__setattr__(name, value) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__setattr__(name, value)
|
||||
|
||||
def save(self, *args, **kwargs): # lint-amnesty, pylint: disable=arguments-differ, signature-differs
|
||||
"""Customize save method to set the last_activity_at if it does not
|
||||
@@ -289,13 +289,13 @@ class CourseTeamMembership(models.Model):
|
||||
should_reset_team_size = True
|
||||
if not self.last_activity_at:
|
||||
self.last_activity_at = datetime.utcnow().replace(tzinfo=pytz.utc)
|
||||
super(CourseTeamMembership, self).save(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().save(*args, **kwargs)
|
||||
if should_reset_team_size:
|
||||
self.team.reset_team_size()
|
||||
|
||||
def delete(self, *args, **kwargs): # lint-amnesty, pylint: disable=arguments-differ, signature-differs
|
||||
"""Recompute the related team's team_size after deleting a membership"""
|
||||
super(CourseTeamMembership, self).delete(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().delete(*args, **kwargs)
|
||||
self.team.reset_team_size()
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -27,7 +27,7 @@ class TeamsTab(EnrolledTab):
|
||||
course (CourseDescriptor): the course using the feature
|
||||
user (User): the user interacting with the course
|
||||
"""
|
||||
if not super(TeamsTab, cls).is_enabled(course, user=user):
|
||||
if not super().is_enabled(course, user=user):
|
||||
return False
|
||||
|
||||
return is_feature_enabled(course)
|
||||
|
||||
@@ -33,7 +33,7 @@ def if_search_enabled(f):
|
||||
return wrapper
|
||||
|
||||
|
||||
class CourseTeamIndexer(object):
|
||||
class CourseTeamIndexer:
|
||||
"""
|
||||
This is the index object for searching and storing CourseTeam model instances.
|
||||
"""
|
||||
@@ -79,7 +79,7 @@ class CourseTeamIndexer(object):
|
||||
"""
|
||||
# Always use the English version of any localizable strings (see TNL-3239)
|
||||
with translation.override('en'):
|
||||
return u"{name}\n{description}\n{country}\n{language}".format(
|
||||
return "{name}\n{description}\n{country}\n{language}".format(
|
||||
name=self.course_team.name,
|
||||
description=self.course_team.description,
|
||||
country=self.course_team.country.name.format(),
|
||||
@@ -123,7 +123,7 @@ class CourseTeamIndexer(object):
|
||||
try:
|
||||
return SearchEngine.get_search_engine(index=cls.INDEX_NAME)
|
||||
except ConnectionError as err:
|
||||
logging.error(u'Error connecting to elasticsearch: %s', err)
|
||||
logging.error('Error connecting to elasticsearch: %s', err)
|
||||
raise ElasticSearchConnectionError # lint-amnesty, pylint: disable=raise-missing-from
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -29,7 +29,7 @@ class CountryField(serializers.Field):
|
||||
"""
|
||||
Represent the country as a 2-character unicode identifier.
|
||||
"""
|
||||
return six.text_type(obj)
|
||||
return str(obj)
|
||||
|
||||
def to_internal_value(self, data):
|
||||
"""
|
||||
@@ -41,7 +41,7 @@ class CountryField(serializers.Field):
|
||||
"""
|
||||
if data and data not in self.COUNTRY_CODES:
|
||||
raise serializers.ValidationError(
|
||||
u"{code} is not a valid country code".format(code=data)
|
||||
f"{data} is not a valid country code"
|
||||
)
|
||||
return data
|
||||
|
||||
@@ -65,7 +65,7 @@ class UserMembershipSerializer(serializers.ModelSerializer):
|
||||
expanded_serializer=UserReadOnlySerializer(configuration=profile_configuration),
|
||||
)
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = CourseTeamMembership
|
||||
fields = ("user", "date_joined", "last_activity_at")
|
||||
read_only_fields = ("date_joined", "last_activity_at")
|
||||
@@ -77,7 +77,7 @@ class CourseTeamSerializer(serializers.ModelSerializer):
|
||||
membership = UserMembershipSerializer(many=True, read_only=True)
|
||||
country = CountryField()
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = CourseTeam
|
||||
fields = (
|
||||
"id",
|
||||
@@ -101,7 +101,7 @@ class CourseTeamCreationSerializer(serializers.ModelSerializer):
|
||||
|
||||
country = CountryField(required=False)
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = CourseTeam
|
||||
fields = (
|
||||
"name",
|
||||
@@ -135,7 +135,7 @@ class CourseTeamSerializerWithoutMembership(CourseTeamSerializer):
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CourseTeamSerializerWithoutMembership, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(*args, **kwargs)
|
||||
del self.fields['membership']
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ class MembershipSerializer(serializers.ModelSerializer):
|
||||
expanded_serializer=CourseTeamSerializerWithoutMembership(read_only=True),
|
||||
)
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = CourseTeamMembership
|
||||
fields = ("user", "team", "date_joined", "last_activity_at")
|
||||
read_only_fields = ("date_joined", "last_activity_at")
|
||||
@@ -209,7 +209,7 @@ class BulkTeamCountTopicListSerializer(serializers.ListSerializer): # pylint: d
|
||||
|
||||
def to_representation(self, obj): # pylint: disable=arguments-differ
|
||||
"""Adds team_count to each topic. """
|
||||
data = super(BulkTeamCountTopicListSerializer, self).to_representation(obj) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
data = super().to_representation(obj)
|
||||
add_team_count(
|
||||
self.context['request'].user,
|
||||
data,
|
||||
@@ -224,5 +224,5 @@ class BulkTeamCountTopicSerializer(BaseTopicSerializer): # pylint: disable=abst
|
||||
Serializes a set of topics, adding the team_count field to each topic as a bulk operation.
|
||||
Requires that `context` is provided with a valid course_id in order to filter teams within the course.
|
||||
"""
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
list_serializer_class = BulkTeamCountTopicListSerializer
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
class TeamsService(object):
|
||||
class TeamsService:
|
||||
""" Functions to provide teams functionality to XBlocks"""
|
||||
|
||||
def get_team(self, user, course_id, topic_id):
|
||||
|
||||
@@ -20,21 +20,21 @@ class CourseTeamFactory(DjangoModelFactory):
|
||||
|
||||
Note that team_id is not auto-generated from name when using the factory.
|
||||
"""
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = CourseTeam
|
||||
django_get_or_create = ('team_id',)
|
||||
|
||||
team_id = factory.Sequence('team-{0}'.format)
|
||||
topic_id = factory.Sequence('topic-{0}'.format)
|
||||
team_id = factory.Sequence('team-{}'.format)
|
||||
topic_id = factory.Sequence('topic-{}'.format)
|
||||
discussion_topic_id = factory.LazyAttribute(lambda a: uuid4().hex)
|
||||
name = factory.Sequence(u"Awesome Team {0}".format)
|
||||
name = factory.Sequence("Awesome Team {}".format)
|
||||
description = "A simple description"
|
||||
last_activity_at = LAST_ACTIVITY_AT
|
||||
|
||||
|
||||
class CourseTeamMembershipFactory(DjangoModelFactory):
|
||||
"""Factory for CourseTeamMemberships."""
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = CourseTeamMembership
|
||||
|
||||
last_activity_at = LAST_ACTIVITY_AT
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for Python APIs of the Teams app
|
||||
"""
|
||||
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from common.djangoapps.course_modes.models import CourseMode
|
||||
from common.djangoapps.student.models import AnonymousUserId, CourseEnrollment
|
||||
from common.djangoapps.student.roles import CourseStaffRole
|
||||
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
from lms.djangoapps.teams import api as teams_api
|
||||
from lms.djangoapps.teams.models import CourseTeam
|
||||
from lms.djangoapps.teams.tests.factories import CourseTeamFactory
|
||||
from openedx.core.lib.teams_config import TeamsConfig, TeamsetType
|
||||
from common.djangoapps.student.models import CourseEnrollment, AnonymousUserId
|
||||
from common.djangoapps.student.roles import CourseStaffRole
|
||||
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
@@ -36,7 +35,7 @@ class PythonAPITests(SharedModuleStoreTestCase):
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(PythonAPITests, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
cls.user1 = UserFactory.create(username='user1')
|
||||
cls.user2 = UserFactory.create(username='user2')
|
||||
cls.user3 = UserFactory.create(username='user3')
|
||||
@@ -231,7 +230,7 @@ class TeamAccessTests(SharedModuleStoreTestCase):
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TeamAccessTests, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
cls.user_audit = UserFactory.create(username='user_audit')
|
||||
cls.user_staff = UserFactory.create(username='user_staff')
|
||||
cls.user_masters = UserFactory.create(username='user_masters')
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
""" Tests for the functionality in csv """
|
||||
from csv import DictWriter, DictReader
|
||||
from csv import DictReader, DictWriter
|
||||
from io import BytesIO, StringIO, TextIOWrapper
|
||||
|
||||
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user, unused-import
|
||||
|
||||
from lms.djangoapps.program_enrollments.tests.factories import ProgramEnrollmentFactory, ProgramCourseEnrollmentFactory
|
||||
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
from common.djangoapps.util.testing import EventTestMixin
|
||||
from lms.djangoapps.program_enrollments.tests.factories import ProgramCourseEnrollmentFactory, ProgramEnrollmentFactory
|
||||
from lms.djangoapps.teams import csv
|
||||
from lms.djangoapps.teams.models import CourseTeam, CourseTeamMembership
|
||||
from lms.djangoapps.teams.tests.factories import CourseTeamFactory
|
||||
from openedx.core.lib.teams_config import TeamsConfig
|
||||
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
from common.djangoapps.util.testing import EventTestMixin
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
@@ -72,13 +72,13 @@ class TeamMembershipCsvTests(SharedModuleStoreTestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
# pylint: disable=no-member
|
||||
super(TeamMembershipCsvTests, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
teams_config = TeamsConfig({
|
||||
'team_sets': [
|
||||
{
|
||||
'id': 'teamset_{}'.format(i),
|
||||
'name': 'teamset_{}_name'.format(i),
|
||||
'description': 'teamset_{}_desc'.format(i),
|
||||
'id': f'teamset_{i}',
|
||||
'name': f'teamset_{i}_name',
|
||||
'description': f'teamset_{i}_desc',
|
||||
}
|
||||
for i in [1, 2, 3, 4]
|
||||
]
|
||||
@@ -231,7 +231,7 @@ class TeamMembershipImportManagerTests(TeamMembershipEventTestMixin, SharedModul
|
||||
""" Tests for TeamMembershipImportManager """
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TeamMembershipImportManagerTests, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
teams_config = TeamsConfig({
|
||||
'team_sets': [{
|
||||
'id': 'teamset_1',
|
||||
@@ -246,7 +246,7 @@ class TeamMembershipImportManagerTests(TeamMembershipEventTestMixin, SharedModul
|
||||
|
||||
def setUp(self):
|
||||
""" Initialize import manager """
|
||||
super(TeamMembershipImportManagerTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.import_manager = csv.TeamMembershipImportManager(self.course)
|
||||
self.import_manager.teamset_ids = {ts.teamset_id for ts in self.course.teamsets}
|
||||
|
||||
@@ -347,7 +347,7 @@ class TeamMembershipImportManagerTests(TeamMembershipEventTestMixin, SharedModul
|
||||
# Given a bunch of students enrolled in a course
|
||||
users = []
|
||||
for i in range(5):
|
||||
user = UserFactory.create(username='max_size_{id}'.format(id=i))
|
||||
user = UserFactory.create(username=f'max_size_{i}')
|
||||
CourseEnrollmentFactory.create(user=user, course_id=self.course.id, mode='audit')
|
||||
users.append(user)
|
||||
|
||||
@@ -406,7 +406,7 @@ class TeamMembershipImportManagerTests(TeamMembershipEventTestMixin, SharedModul
|
||||
# Given a bunch of students enrolled in a course
|
||||
users = []
|
||||
for i in range(5):
|
||||
user = UserFactory.create(username='learner_{id}'.format(id=i))
|
||||
user = UserFactory.create(username=f'learner_{i}')
|
||||
CourseEnrollmentFactory.create(user=user, course_id=self.course.id, mode='audit')
|
||||
users.append(user)
|
||||
|
||||
@@ -547,7 +547,7 @@ class TeamMembershipImportManagerTests(TeamMembershipEventTestMixin, SharedModul
|
||||
Example:
|
||||
[['header1', 'header2'], ['r1:c1', 'r1:c2'], ['r2:c2', 'r3:c3'] ... ]
|
||||
"""
|
||||
return DictReader((','.join(row) for row in rows))
|
||||
return DictReader(','.join(row) for row in rows)
|
||||
|
||||
|
||||
class ExternalKeyCsvTests(TeamMembershipEventTestMixin, SharedModuleStoreTestCase):
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for the teams API at the HTTP request level.
|
||||
"""
|
||||
|
||||
|
||||
import itertools
|
||||
import pytest
|
||||
from contextlib import contextmanager
|
||||
from datetime import datetime
|
||||
from unittest.mock import Mock
|
||||
|
||||
import ddt
|
||||
import pytest
|
||||
import pytz
|
||||
import six
|
||||
from mock import Mock, patch # lint-amnesty, pylint: disable=unused-import
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from common.djangoapps.course_modes.models import CourseMode
|
||||
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
from common.djangoapps.util.testing import EventTestMixin
|
||||
from lms.djangoapps.teams import TEAM_DISCUSSION_CONTEXT
|
||||
from lms.djangoapps.teams.errors import AddToIncompatibleTeamError
|
||||
from lms.djangoapps.teams.models import CourseTeam, CourseTeamMembership
|
||||
@@ -32,9 +32,6 @@ from openedx.core.djangoapps.django_comment_common.signals import (
|
||||
thread_voted
|
||||
)
|
||||
from openedx.core.lib.teams_config import TeamsConfig
|
||||
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
from common.djangoapps.util.testing import EventTestMixin
|
||||
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
@@ -65,7 +62,7 @@ class TestModelStrings(SharedModuleStoreTestCase):
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestModelStrings, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
cls.course_id = "edx/the-course/1"
|
||||
cls.course1 = create_course(CourseKey.from_string(cls.course_id), TEAMS_CONFIG_1)
|
||||
cls.user = UserFactory.create(username="the-user")
|
||||
@@ -90,7 +87,7 @@ class TestModelStrings(SharedModuleStoreTestCase):
|
||||
)
|
||||
|
||||
def test_team_text(self):
|
||||
assert six.text_type(self.team) == (
|
||||
assert str(self.team) == (
|
||||
"The Team in edx/the-course/1"
|
||||
)
|
||||
|
||||
@@ -100,7 +97,7 @@ class TestModelStrings(SharedModuleStoreTestCase):
|
||||
)
|
||||
|
||||
def test_team_membership_text_type(self):
|
||||
assert six.text_type(self.team_membership) == (
|
||||
assert str(self.team_membership) == (
|
||||
"the-user is member of The Team in edx/the-course/1"
|
||||
)
|
||||
|
||||
@@ -110,7 +107,7 @@ class CourseTeamTest(SharedModuleStoreTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(CourseTeamTest, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
cls.course_id = "edx/the-course/1"
|
||||
cls.course1 = create_course(CourseKey.from_string(cls.course_id), TEAMS_CONFIG_1)
|
||||
|
||||
@@ -154,7 +151,7 @@ class TeamMembershipTest(SharedModuleStoreTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TeamMembershipTest, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
create_course(COURSE_KEY1, TEAMS_CONFIG_1)
|
||||
create_course(COURSE_KEY2, TEAMS_CONFIG_2)
|
||||
|
||||
@@ -162,7 +159,7 @@ class TeamMembershipTest(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Set up tests.
|
||||
"""
|
||||
super(TeamMembershipTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.user1 = UserFactory.create(username='user1')
|
||||
self.user2 = UserFactory.create(username='user2')
|
||||
@@ -263,7 +260,7 @@ class TeamSignalsTest(EventTestMixin, SharedModuleStoreTestCase):
|
||||
|
||||
def setUp(self): # pylint: disable=arguments-differ
|
||||
"""Create a user with a team to test signals."""
|
||||
super(TeamSignalsTest, self).setUp('lms.djangoapps.teams.utils.tracker') # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp('lms.djangoapps.teams.utils.tracker')
|
||||
self.user = UserFactory.create(username="user")
|
||||
self.moderator = UserFactory.create(username="moderator")
|
||||
self.team = CourseTeamFactory(discussion_topic_id=self.DISCUSSION_TOPIC_ID)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for custom Teams Serializers.
|
||||
"""
|
||||
@@ -8,10 +7,10 @@ import six
|
||||
from django.core.paginator import Paginator
|
||||
from django.test.client import RequestFactory
|
||||
|
||||
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
from lms.djangoapps.teams.serializers import BulkTeamCountTopicSerializer, MembershipSerializer, TopicSerializer
|
||||
from lms.djangoapps.teams.tests.factories import CourseTeamFactory, CourseTeamMembershipFactory
|
||||
from openedx.core.lib.teams_config import TeamsConfig
|
||||
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
@@ -24,11 +23,11 @@ class SerializerTestCase(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Set up a course with a teams configuration.
|
||||
"""
|
||||
super(SerializerTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.course = CourseFactory.create(
|
||||
teams_configuration=TeamsConfig({
|
||||
"max_team_size": 10,
|
||||
"topics": [{u'name': u'Tøpic', u'description': u'The bést topic!', u'id': u'0'}]
|
||||
"topics": [{'name': 'Tøpic', 'description': 'The bést topic!', 'id': '0'}]
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -39,7 +38,7 @@ class MembershipSerializerTestCase(SerializerTestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(MembershipSerializerTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.team = CourseTeamFactory.create(
|
||||
course_id=self.course.id,
|
||||
topic_id=self.course.teamsets[0].teamset_id,
|
||||
@@ -51,7 +50,7 @@ class MembershipSerializerTestCase(SerializerTestCase):
|
||||
def test_membership_serializer_expand_user_and_team(self):
|
||||
"""Verify that the serializer only expands the user and team one level."""
|
||||
data = MembershipSerializer(self.team_membership, context={
|
||||
'expand': [u'team', u'user'],
|
||||
'expand': ['team', 'user'],
|
||||
'request': RequestFactory().get('/api/team/v0/team_membership')
|
||||
}).data
|
||||
username = self.user.username
|
||||
@@ -81,8 +80,8 @@ class TopicSerializerTestCase(SerializerTestCase):
|
||||
self.course.teamsets[0].cleaned_data,
|
||||
context={'course_id': self.course.id},
|
||||
)
|
||||
assert serializer.data == {u'name': u'Tøpic', u'description': u'The bést topic!', u'id': u'0',
|
||||
u'team_count': 0, u'type': u'open', u'max_team_size': None}
|
||||
assert serializer.data == {'name': 'Tøpic', 'description': 'The bést topic!', 'id': '0',
|
||||
'team_count': 0, 'type': 'open', 'max_team_size': None}
|
||||
|
||||
def test_topic_with_team_count(self):
|
||||
"""
|
||||
@@ -97,8 +96,8 @@ class TopicSerializerTestCase(SerializerTestCase):
|
||||
self.course.teamsets[0].cleaned_data,
|
||||
context={'course_id': self.course.id},
|
||||
)
|
||||
assert serializer.data == {u'name': u'Tøpic', u'description': u'The bést topic!', u'id': u'0',
|
||||
u'team_count': 1, u'type': u'open', u'max_team_size': None}
|
||||
assert serializer.data == {'name': 'Tøpic', 'description': 'The bést topic!', 'id': '0',
|
||||
'team_count': 1, 'type': 'open', 'max_team_size': None}
|
||||
|
||||
def test_scoped_within_course(self):
|
||||
"""Verify that team count is scoped within a course."""
|
||||
@@ -109,15 +108,15 @@ class TopicSerializerTestCase(SerializerTestCase):
|
||||
"topics": [duplicate_topic]
|
||||
}),
|
||||
)
|
||||
CourseTeamFactory.create(course_id=self.course.id, topic_id=duplicate_topic[u'id'])
|
||||
CourseTeamFactory.create(course_id=second_course.id, topic_id=duplicate_topic[u'id'])
|
||||
CourseTeamFactory.create(course_id=self.course.id, topic_id=duplicate_topic['id'])
|
||||
CourseTeamFactory.create(course_id=second_course.id, topic_id=duplicate_topic['id'])
|
||||
with self.assertNumQueries(2):
|
||||
serializer = TopicSerializer(
|
||||
self.course.teamsets[0].cleaned_data,
|
||||
context={'course_id': self.course.id},
|
||||
)
|
||||
assert serializer.data == {u'name': u'Tøpic', u'description': u'The bést topic!', u'id': u'0',
|
||||
u'team_count': 1, u'type': u'open', u'max_team_size': None}
|
||||
assert serializer.data == {'name': 'Tøpic', 'description': 'The bést topic!', 'id': '0',
|
||||
'team_count': 1, 'type': 'open', 'max_team_size': None}
|
||||
|
||||
|
||||
class BaseTopicSerializerTestCase(SerializerTestCase):
|
||||
@@ -143,9 +142,9 @@ class BaseTopicSerializerTestCase(SerializerTestCase):
|
||||
"""
|
||||
topics = [
|
||||
{
|
||||
'name': 'Tøpic {}'.format(i),
|
||||
'description': 'The bést topic! {}'.format(i),
|
||||
'id': six.text_type(i),
|
||||
'name': f'Tøpic {i}',
|
||||
'description': f'The bést topic! {i}',
|
||||
'id': str(i),
|
||||
'type': 'open',
|
||||
'max_team_size': i + 10
|
||||
}
|
||||
@@ -172,7 +171,7 @@ class BaseTopicSerializerTestCase(SerializerTestCase):
|
||||
# pylint: disable=not-callable
|
||||
serializer = self.serializer(instance=page, context={'course_id': self.course.id})
|
||||
assert serializer.data['results'] ==\
|
||||
[self._merge_dicts(topic, {u'team_count': num_teams_per_topic}) for topic in topics]
|
||||
[self._merge_dicts(topic, {'team_count': num_teams_per_topic}) for topic in topics]
|
||||
|
||||
def test_no_topics(self):
|
||||
"""
|
||||
@@ -235,7 +234,7 @@ class BulkTeamCountTopicSerializerTestCase(BaseTopicSerializerTestCase):
|
||||
"topics": [duplicate_topic]
|
||||
}),
|
||||
)
|
||||
CourseTeamFactory.create(course_id=second_course.id, topic_id=duplicate_topic[u'id'])
|
||||
CourseTeamFactory.create(course_id=second_course.id, topic_id=duplicate_topic['id'])
|
||||
self.assert_serializer_output(first_course_topics, num_teams_per_topic=teams_per_topic, num_queries=2)
|
||||
|
||||
def _merge_dicts(self, first, second):
|
||||
@@ -262,7 +261,7 @@ class BulkTeamCountTopicSerializerTestCase(BaseTopicSerializerTestCase):
|
||||
many=True
|
||||
)
|
||||
assert serializer.data ==\
|
||||
[self._merge_dicts(topic, {u'team_count': num_teams_per_topic}) for topic in topics]
|
||||
[self._merge_dicts(topic, {'team_count': num_teams_per_topic}) for topic in topics]
|
||||
|
||||
def test_no_topics(self):
|
||||
"""
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for any Teams app services
|
||||
"""
|
||||
|
||||
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from openedx.core.djangoapps.catalog.tests.factories import CourseRunFactory
|
||||
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
|
||||
from lms.djangoapps.teams.services import TeamsService
|
||||
from lms.djangoapps.teams.tests.factories import CourseTeamFactory
|
||||
from openedx.core.djangoapps.catalog.tests.factories import CourseRunFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
|
||||
|
||||
class TeamsServiceTests(ModuleStoreTestCase):
|
||||
""" Tests for the TeamsService """
|
||||
|
||||
def setUp(self):
|
||||
super(TeamsServiceTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.course_run = CourseRunFactory.create()
|
||||
self.course_key = self.course_run['key']
|
||||
self.team = CourseTeamFactory.create(course_id=self.course_key)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for the teams API at the HTTP request level.
|
||||
"""
|
||||
@@ -7,32 +6,31 @@ Tests for the teams API at the HTTP request level.
|
||||
import json
|
||||
import unittest
|
||||
from datetime import datetime
|
||||
from unittest.mock import patch
|
||||
from uuid import UUID
|
||||
|
||||
import ddt
|
||||
import pytz
|
||||
from dateutil import parser
|
||||
from django.conf import settings
|
||||
from django.db.models.signals import post_save
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.db.models.signals import post_save
|
||||
from django.urls import reverse
|
||||
from django.utils import translation
|
||||
from elasticsearch.exceptions import ConnectionError # lint-amnesty, pylint: disable=redefined-builtin
|
||||
from mock import patch
|
||||
from rest_framework.test import APIClient, APITestCase
|
||||
from search.search_engine_base import SearchEngine
|
||||
from six.moves import range
|
||||
|
||||
from common.test.utils import skip_signal
|
||||
from common.djangoapps.course_modes.models import CourseMode
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from common.djangoapps.student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory
|
||||
from common.djangoapps.util.testing import EventTestMixin
|
||||
from common.test.utils import skip_signal
|
||||
from lms.djangoapps.courseware.tests.factories import StaffFactory
|
||||
from lms.djangoapps.program_enrollments.tests.factories import ProgramEnrollmentFactory
|
||||
from openedx.core.djangoapps.django_comment_common.models import FORUM_ROLE_COMMUNITY_TA, Role
|
||||
from openedx.core.djangoapps.django_comment_common.utils import seed_permissions_roles
|
||||
from openedx.core.lib.teams_config import TeamsConfig
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from lms.djangoapps.program_enrollments.tests.factories import ProgramEnrollmentFactory
|
||||
from common.djangoapps.student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory
|
||||
from common.djangoapps.util.testing import EventTestMixin
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
|
||||
@@ -50,15 +48,15 @@ class TestDashboard(SharedModuleStoreTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestDashboard, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
cls.course = CourseFactory.create(
|
||||
teams_configuration=TeamsConfig({
|
||||
"max_team_size": 10,
|
||||
"topics": [
|
||||
{
|
||||
"name": u"Topic {}".format(topic_id),
|
||||
"name": f"Topic {topic_id}",
|
||||
"id": topic_id,
|
||||
"description": u"Description for topic {}".format(topic_id)
|
||||
"description": f"Description for topic {topic_id}"
|
||||
}
|
||||
for topic_id in range(cls.NUM_TOPICS)
|
||||
]
|
||||
@@ -69,7 +67,7 @@ class TestDashboard(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Set up tests
|
||||
"""
|
||||
super(TestDashboard, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
# will be assigned to self.client by default
|
||||
self.user = UserFactory.create(password=self.test_password)
|
||||
self.teams_url = reverse('teams_dashboard', args=[self.course.id])
|
||||
@@ -79,7 +77,7 @@ class TestDashboard(SharedModuleStoreTestCase):
|
||||
dashboard, and is redirected to the login page."""
|
||||
anonymous_client = APIClient()
|
||||
response = anonymous_client.get(self.teams_url)
|
||||
redirect_url = '{0}?next={1}'.format(settings.LOGIN_URL, self.teams_url)
|
||||
redirect_url = f'{settings.LOGIN_URL}?next={self.teams_url}'
|
||||
self.assertRedirects(response, redirect_url)
|
||||
|
||||
def test_not_enrolled_not_staff(self):
|
||||
@@ -132,7 +130,7 @@ class TestDashboard(SharedModuleStoreTestCase):
|
||||
# Create some teams
|
||||
for topic_id in range(self.NUM_TOPICS):
|
||||
team = CourseTeamFactory.create(
|
||||
name=u"Team for topic {}".format(topic_id),
|
||||
name=f"Team for topic {topic_id}",
|
||||
course_id=self.course.id,
|
||||
topic_id=topic_id,
|
||||
)
|
||||
@@ -305,34 +303,34 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
# pylint: disable=super-method-not-called
|
||||
with super(TeamAPITestCase, cls).setUpClassAndTestData():
|
||||
with super().setUpClassAndTestData():
|
||||
base_topics = [{
|
||||
'id': 'topic_{}'.format(i), 'name': name,
|
||||
'description': u'Description for topic {}.'.format(i),
|
||||
'id': f'topic_{i}', 'name': name,
|
||||
'description': f'Description for topic {i}.',
|
||||
'max_team_size': 3
|
||||
} for i, name in enumerate([u'Sólar power', 'Wind Power', 'Nuclear Power', 'Coal Power'])]
|
||||
} for i, name in enumerate(['Sólar power', 'Wind Power', 'Nuclear Power', 'Coal Power'])]
|
||||
base_topics.append(
|
||||
{
|
||||
'id': 'private_topic_1_id',
|
||||
'name': 'private_topic_1_name',
|
||||
'description': u'Description for topic private topic 1.',
|
||||
'type': u'private_managed'
|
||||
'description': 'Description for topic private topic 1.',
|
||||
'type': 'private_managed'
|
||||
}
|
||||
)
|
||||
base_topics.append(
|
||||
{
|
||||
'id': 'private_topic_2_id',
|
||||
'name': 'private_topic_2_name',
|
||||
'description': u'Description for topic private topic 2.',
|
||||
'type': u'private_managed'
|
||||
'description': 'Description for topic private topic 2.',
|
||||
'type': 'private_managed'
|
||||
}
|
||||
)
|
||||
base_topics.append(
|
||||
{
|
||||
'id': 'private_topic_no_teams',
|
||||
'name': 'private_topic_no_teams_name',
|
||||
'description': u'Description for topic private_topic_no_teams.',
|
||||
'type': u'private_managed'
|
||||
'description': 'Description for topic private_topic_no_teams.',
|
||||
'type': 'private_managed'
|
||||
}
|
||||
)
|
||||
teams_configuration_1 = TeamsConfig({
|
||||
@@ -377,7 +375,7 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
super(TeamAPITestCase, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
cls.topics_count = 6
|
||||
cls.users = {
|
||||
'staff': AdminFactory.create(password=cls.test_password),
|
||||
@@ -439,7 +437,7 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
|
||||
dispatch_uid='teams.signals.course_team_post_save_callback'
|
||||
):
|
||||
cls.solar_team = CourseTeamFactory.create(
|
||||
name=u'Sólar team',
|
||||
name='Sólar team',
|
||||
course_id=cls.test_course_1.id,
|
||||
topic_id='topic_0'
|
||||
)
|
||||
@@ -472,8 +470,8 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
|
||||
topic_id='topic_7'
|
||||
)
|
||||
cls.chinese_team = CourseTeamFactory.create(
|
||||
name=u'著文企臺個',
|
||||
description=u'共樣地面較,件展冷不護者這與民教過住意,國制銀產物助音是勢一友',
|
||||
name='著文企臺個',
|
||||
description='共樣地面較,件展冷不護者這與民教過住意,國制銀產物助音是勢一友',
|
||||
country='CN',
|
||||
language='zh_HANS',
|
||||
course_id=cls.test_course_2.id,
|
||||
@@ -773,7 +771,7 @@ class TestListTeamsAPI(EventTestMixin, TeamAPITestCase):
|
||||
"""Test cases for the team listing API endpoint."""
|
||||
|
||||
def setUp(self): # pylint: disable=arguments-differ
|
||||
super(TestListTeamsAPI, self).setUp('lms.djangoapps.teams.utils.tracker') # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp('lms.djangoapps.teams.utils.tracker')
|
||||
|
||||
@ddt.data(
|
||||
(None, 401),
|
||||
@@ -808,27 +806,27 @@ class TestListTeamsAPI(EventTestMixin, TeamAPITestCase):
|
||||
self.verify_names(
|
||||
{'course_id': str(self.test_course_2.id)},
|
||||
200,
|
||||
['Another Team', 'Public Profile Team', 'Search', u'著文企臺個'],
|
||||
['Another Team', 'Public Profile Team', 'Search', '著文企臺個'],
|
||||
user='staff'
|
||||
)
|
||||
|
||||
def test_filter_topic_id(self):
|
||||
self.verify_names({'course_id': str(self.test_course_1.id), 'topic_id': 'topic_0'}, 200, [u'Sólar team'])
|
||||
self.verify_names({'course_id': str(self.test_course_1.id), 'topic_id': 'topic_0'}, 200, ['Sólar team'])
|
||||
|
||||
def test_filter_username(self):
|
||||
self.verify_names({'course_id': str(self.test_course_1.id),
|
||||
'username': 'student_enrolled'}, 200, [u'Sólar team'])
|
||||
'username': 'student_enrolled'}, 200, ['Sólar team'])
|
||||
self.verify_names({'course_id': str(self.test_course_1.id), 'username': 'staff'}, 200, [])
|
||||
|
||||
@ddt.data(
|
||||
(None, 200, ['Nuclear Team', u'Sólar team', 'Wind Team']),
|
||||
('name', 200, ['Nuclear Team', u'Sólar team', 'Wind Team']),
|
||||
(None, 200, ['Nuclear Team', 'Sólar team', 'Wind Team']),
|
||||
('name', 200, ['Nuclear Team', 'Sólar team', 'Wind Team']),
|
||||
# Note that "Nuclear Team" and "Solar team" have the same open_slots.
|
||||
# "Solar team" comes first due to secondary sort by last_activity_at.
|
||||
('open_slots', 200, ['Wind Team', u'Sólar team', 'Nuclear Team']),
|
||||
('open_slots', 200, ['Wind Team', 'Sólar team', 'Nuclear Team']),
|
||||
# Note that "Wind Team" and "Nuclear Team" have the same last_activity_at.
|
||||
# "Wind Team" comes first due to secondary sort by open_slots.
|
||||
('last_activity_at', 200, [u'Sólar team', 'Wind Team', 'Nuclear Team']),
|
||||
('last_activity_at', 200, ['Sólar team', 'Wind Team', 'Nuclear Team']),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_order_by(self, field, status, names):
|
||||
@@ -841,7 +839,7 @@ class TestListTeamsAPI(EventTestMixin, TeamAPITestCase):
|
||||
sender=CourseTeam,
|
||||
dispatch_uid='teams.signals.course_team_post_save_callback'
|
||||
):
|
||||
solar_team = self.test_team_name_id_map[u'Sólar team']
|
||||
solar_team = self.test_team_name_id_map['Sólar team']
|
||||
solar_team.last_activity_at = datetime.utcnow().replace(tzinfo=pytz.utc)
|
||||
solar_team.save()
|
||||
|
||||
@@ -969,7 +967,7 @@ class TestListTeamsAPI(EventTestMixin, TeamAPITestCase):
|
||||
('Island', ['Search']),
|
||||
('not-a-query', []),
|
||||
('team', ['Another Team', 'Public Profile Team']),
|
||||
(u'著文企臺個', [u'著文企臺個']),
|
||||
('著文企臺個', ['著文企臺個']),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_text_search(self, text_search, expected_team_names):
|
||||
@@ -1017,7 +1015,7 @@ class TestListTeamsAPI(EventTestMixin, TeamAPITestCase):
|
||||
|
||||
def test_delete_removed_from_search(self):
|
||||
team = CourseTeamFactory.create(
|
||||
name=u'zoinks',
|
||||
name='zoinks',
|
||||
course_id=self.test_course_1.id,
|
||||
topic_id='topic_0'
|
||||
)
|
||||
@@ -1073,7 +1071,7 @@ class TestCreateTeamAPI(EventTestMixin, TeamAPITestCase):
|
||||
"""Test cases for the team creation endpoint."""
|
||||
|
||||
def setUp(self): # pylint: disable=arguments-differ
|
||||
super(TestCreateTeamAPI, self).setUp('lms.djangoapps.teams.utils.tracker') # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp('lms.djangoapps.teams.utils.tracker')
|
||||
|
||||
@ddt.data(
|
||||
(None, 401),
|
||||
@@ -1378,7 +1376,7 @@ class TestDeleteTeamAPI(EventTestMixin, TeamAPITestCase):
|
||||
"""Test cases for the team delete endpoint."""
|
||||
|
||||
def setUp(self): # pylint: disable=arguments-differ
|
||||
super(TestDeleteTeamAPI, self).setUp('lms.djangoapps.teams.utils.tracker') # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp('lms.djangoapps.teams.utils.tracker')
|
||||
|
||||
@ddt.data(
|
||||
('staff', 204),
|
||||
@@ -1494,7 +1492,7 @@ class TestUpdateTeamAPI(EventTestMixin, TeamAPITestCase):
|
||||
"""Test cases for the team update endpoint."""
|
||||
|
||||
def setUp(self): # pylint: disable=arguments-differ
|
||||
super(TestUpdateTeamAPI, self).setUp('lms.djangoapps.teams.utils.tracker') # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp('lms.djangoapps.teams.utils.tracker')
|
||||
|
||||
@ddt.data(
|
||||
(None, 401),
|
||||
@@ -1732,11 +1730,11 @@ class TestListTopicsAPI(TeamAPITestCase):
|
||||
self.get_topics_list(400)
|
||||
|
||||
@ddt.data(
|
||||
(None, 200, ['Coal Power', 'Nuclear Power', u'Sólar power', 'Wind Power'], 'name'),
|
||||
('name', 200, ['Coal Power', 'Nuclear Power', u'Sólar power', 'Wind Power'], 'name'),
|
||||
(None, 200, ['Coal Power', 'Nuclear Power', 'Sólar power', 'Wind Power'], 'name'),
|
||||
('name', 200, ['Coal Power', 'Nuclear Power', 'Sólar power', 'Wind Power'], 'name'),
|
||||
# Note that "Nuclear Power" will have 2 teams. "Coal Power" "Wind Power" and "Solar Power"
|
||||
# all have 1 team. The secondary sort is alphabetical by name.
|
||||
('team_count', 200, ['Nuclear Power', 'Coal Power', u'Sólar power', 'Wind Power'], 'team_count'),
|
||||
('team_count', 200, ['Nuclear Power', 'Coal Power', 'Sólar power', 'Wind Power'], 'team_count'),
|
||||
('no_such_field', 400, [], None),
|
||||
)
|
||||
@ddt.unpack
|
||||
@@ -1749,11 +1747,11 @@ class TestListTopicsAPI(TeamAPITestCase):
|
||||
):
|
||||
# Add a team to "Nuclear Power", so it has two teams
|
||||
CourseTeamFactory.create(
|
||||
name=u'Nuclear Team 1', course_id=self.test_course_1.id, topic_id='topic_2'
|
||||
name='Nuclear Team 1', course_id=self.test_course_1.id, topic_id='topic_2'
|
||||
)
|
||||
# Add a team to "Coal Power", so it has one team, same as "Wind" and "Solar"
|
||||
CourseTeamFactory.create(
|
||||
name=u'Coal Team 1', course_id=self.test_course_1.id, topic_id='topic_3'
|
||||
name='Coal Team 1', course_id=self.test_course_1.id, topic_id='topic_3'
|
||||
)
|
||||
data = {'course_id': str(self.test_course_1.id)}
|
||||
if field:
|
||||
@@ -1778,16 +1776,16 @@ class TestListTopicsAPI(TeamAPITestCase):
|
||||
# Add two wind teams, a solar team and a coal team, to bring the totals to
|
||||
# Wind: 3 Solar: 2 Coal: 1, Nuclear: 1
|
||||
CourseTeamFactory.create(
|
||||
name=u'Wind Team 1', course_id=self.test_course_1.id, topic_id='topic_1'
|
||||
name='Wind Team 1', course_id=self.test_course_1.id, topic_id='topic_1'
|
||||
)
|
||||
CourseTeamFactory.create(
|
||||
name=u'Wind Team 2', course_id=self.test_course_1.id, topic_id='topic_1'
|
||||
name='Wind Team 2', course_id=self.test_course_1.id, topic_id='topic_1'
|
||||
)
|
||||
CourseTeamFactory.create(
|
||||
name=u'Solar Team 1', course_id=self.test_course_1.id, topic_id='topic_0'
|
||||
name='Solar Team 1', course_id=self.test_course_1.id, topic_id='topic_0'
|
||||
)
|
||||
CourseTeamFactory.create(
|
||||
name=u'Coal Team 1', course_id=self.test_course_1.id, topic_id='topic_3'
|
||||
name='Coal Team 1', course_id=self.test_course_1.id, topic_id='topic_3'
|
||||
)
|
||||
|
||||
# Wind power has the most teams, followed by Solar
|
||||
@@ -1800,7 +1798,7 @@ class TestListTopicsAPI(TeamAPITestCase):
|
||||
},
|
||||
user='student_enrolled'
|
||||
)
|
||||
assert ['Wind Power', u'Sólar power'] == [topic['name'] for topic in topics['results']]
|
||||
assert ['Wind Power', 'Sólar power'] == [topic['name'] for topic in topics['results']]
|
||||
|
||||
# Coal and Nuclear are tied, so they are alphabetically sorted.
|
||||
topics = self.get_topics_list(
|
||||
@@ -2085,7 +2083,7 @@ class TestListMembershipAPI(TeamAPITestCase):
|
||||
@ddt.data(
|
||||
('student_enrolled_both_courses_other_team', 'TestX/TS101/Test_Course', 200, 'Nuclear Team'),
|
||||
('student_enrolled_both_courses_other_team', 'MIT/6.002x/Circuits', 200, 'Another Team'),
|
||||
('student_enrolled', 'TestX/TS101/Test_Course', 200, u'Sólar team'),
|
||||
('student_enrolled', 'TestX/TS101/Test_Course', 200, 'Sólar team'),
|
||||
('student_enrolled', 'MIT/6.002x/Circuits', 400, ''),
|
||||
)
|
||||
@ddt.unpack
|
||||
@@ -2288,7 +2286,7 @@ class TestCreateMembershipAPI(EventTestMixin, TeamAPITestCase):
|
||||
"""Test cases for the membership creation endpoint."""
|
||||
|
||||
def setUp(self): # pylint: disable=arguments-differ
|
||||
super(TestCreateMembershipAPI, self).setUp('lms.djangoapps.teams.utils.tracker') # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp('lms.djangoapps.teams.utils.tracker')
|
||||
|
||||
@ddt.data(
|
||||
(None, 401),
|
||||
@@ -2577,7 +2575,7 @@ class TestDeleteMembershipAPI(EventTestMixin, TeamAPITestCase):
|
||||
"""Test cases for the membership deletion endpoint."""
|
||||
|
||||
def setUp(self): # pylint: disable=arguments-differ
|
||||
super(TestDeleteMembershipAPI, self).setUp('lms.djangoapps.teams.utils.tracker') # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp('lms.djangoapps.teams.utils.tracker')
|
||||
|
||||
@ddt.data(
|
||||
(None, 401),
|
||||
@@ -2933,9 +2931,9 @@ class TestBulkMembershipManagement(TeamAPITestCase):
|
||||
self.create_and_enroll_student(username=masters_username_b, mode=CourseMode.MASTERS)
|
||||
|
||||
csv_content = 'user,mode,topic_1' + '\n'
|
||||
csv_content += '{},audit,team wind power'.format(audit_username) + '\n'
|
||||
csv_content += '{},masters,team wind power'.format(masters_username_a) + '\n'
|
||||
csv_content += '{},masters,team wind power'.format(masters_username_b) + '\n'
|
||||
csv_content += f'{audit_username},audit,team wind power' + '\n'
|
||||
csv_content += f'{masters_username_a},masters,team wind power' + '\n'
|
||||
csv_content += f'{masters_username_b},masters,team wind power' + '\n'
|
||||
csv_file = SimpleUploadedFile('test_file.csv', csv_content.encode('utf8'), content_type='text/csv')
|
||||
self.client.login(username=self.users['course_staff'].username, password=self.users['course_staff'].password)
|
||||
response = self.make_call(reverse(
|
||||
@@ -2955,7 +2953,7 @@ class TestBulkMembershipManagement(TeamAPITestCase):
|
||||
for name_enum in enumerate(['a', 'b', 'c', 'd', 'e', 'f', 'g']):
|
||||
username = 'user_{}'.format(name_enum[1])
|
||||
self.create_and_enroll_student(username=username, mode=CourseMode.MASTERS)
|
||||
csv_content += '{},masters,{},{}'.format(username, team1, team2) + '\n'
|
||||
csv_content += f'{username},masters,{team1},{team2}' + '\n'
|
||||
|
||||
csv_file = SimpleUploadedFile('test_file.csv', csv_content.encode('utf8'), content_type='text/csv')
|
||||
self.client.login(username=self.users['course_staff'].username, password=self.users['course_staff'].password)
|
||||
@@ -2975,8 +2973,8 @@ class TestBulkMembershipManagement(TeamAPITestCase):
|
||||
topic_0_id = 'topic_0'
|
||||
assert CourseTeamMembership.objects.filter(user_id=self.users[username].id, team__topic_id=topic_0_id).exists()
|
||||
|
||||
csv_content = 'user,mode,{},topic_1'.format(topic_0_id) + '\n'
|
||||
csv_content += '{},audit'.format(username)
|
||||
csv_content = f'user,mode,{topic_0_id},topic_1' + '\n'
|
||||
csv_content += f'{username},audit'
|
||||
csv_file = SimpleUploadedFile('test_file.csv', csv_content.encode('utf8'), content_type='text/csv')
|
||||
self.client.login(username=self.users['course_staff'].username, password=self.users['course_staff'].password)
|
||||
self.make_call(
|
||||
@@ -2998,8 +2996,8 @@ class TestBulkMembershipManagement(TeamAPITestCase):
|
||||
windpower_team_name = 'team wind power'
|
||||
assert CourseTeamMembership.objects\
|
||||
.filter(user_id=self.users[username].id, team__topic_id=topic_0_id, team__name=windpower_team_name).exists()
|
||||
csv_content = 'user,mode,{}'.format(topic_0_id) + '\n'
|
||||
csv_content += '{0},audit,{1}'.format(username, nuclear_team_name)
|
||||
csv_content = f'user,mode,{topic_0_id}' + '\n'
|
||||
csv_content += f'{username},audit,{nuclear_team_name}'
|
||||
csv_file = SimpleUploadedFile('test_file.csv', csv_content.encode('utf8'), content_type='text/csv')
|
||||
self.client.login(username=self.users['course_staff'].username, password=self.users['course_staff'].password)
|
||||
self.make_call(
|
||||
@@ -3024,8 +3022,8 @@ class TestBulkMembershipManagement(TeamAPITestCase):
|
||||
topic_0_id = 'topic_0'
|
||||
nuclear_team_name = 'team wind power'
|
||||
assert len(CourseTeamMembership.objects.filter(user_id=self.users[username].id, team__topic_id=topic_0_id)) == 1
|
||||
csv_content = 'user,mode,{}'.format(topic_0_id) + '\n'
|
||||
csv_content += '{0},audit,{1}'.format(username, nuclear_team_name)
|
||||
csv_content = f'user,mode,{topic_0_id}' + '\n'
|
||||
csv_content += f'{username},audit,{nuclear_team_name}'
|
||||
csv_file = SimpleUploadedFile('test_file.csv', csv_content.encode('utf8'), content_type='text/csv')
|
||||
self.client.login(username=self.users['course_staff'].username,
|
||||
password=self.users['course_staff'].password)
|
||||
@@ -3078,7 +3076,7 @@ class TestBulkMembershipManagement(TeamAPITestCase):
|
||||
team_name = '著文企臺個'
|
||||
user_name = '著著文企臺個文企臺個'
|
||||
self.create_and_enroll_student(username=user_name)
|
||||
csv_content += '{},audit,{}'.format(user_name, team_name)
|
||||
csv_content += f'{user_name},audit,{team_name}'
|
||||
csv_file = SimpleUploadedFile('test_file.csv', csv_content.encode('utf8'), content_type='text/csv')
|
||||
self.client.login(username=self.users['course_staff'].username, password=self.users['course_staff'].password)
|
||||
self.make_call(
|
||||
@@ -3099,8 +3097,8 @@ class TestBulkMembershipManagement(TeamAPITestCase):
|
||||
masters_a = 'masters_a'
|
||||
team = self.wind_team
|
||||
self.create_and_enroll_student(username=masters_a, mode=CourseMode.MASTERS)
|
||||
csv_content = 'user,mode,{}'.format(team.topic_id) + '\n'
|
||||
csv_content += 'masters_a, masters,{}'.format(team.name)
|
||||
csv_content = f'user,mode,{team.topic_id}' + '\n'
|
||||
csv_content += f'masters_a, masters,{team.name}'
|
||||
csv_file = SimpleUploadedFile('test_file.csv', csv_content.encode('utf8'), content_type='text/csv')
|
||||
self.client.login(username=self.users['course_staff'].username, password=self.users['course_staff'].password)
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
Togglable settings for Teams behavior
|
||||
"""
|
||||
from edx_toggles.toggles import SettingDictToggle
|
||||
from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
|
||||
|
||||
from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
|
||||
|
||||
# Course Waffle inherited from edx/edx-ora2
|
||||
WAFFLE_NAMESPACE = "openresponseassessment"
|
||||
|
||||
@@ -27,12 +27,13 @@ from rest_framework.generics import GenericAPIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.reverse import reverse
|
||||
from rest_framework.views import APIView
|
||||
from openedx.core.lib.api.authentication import BearerAuthentication
|
||||
|
||||
from common.djangoapps.student.models import CourseAccessRole, CourseEnrollment
|
||||
from common.djangoapps.util.model_utils import truncate_fields
|
||||
from lms.djangoapps.courseware.courses import get_course_with_access, has_access
|
||||
from lms.djangoapps.discussion.django_comment_client.utils import has_discussion_privileges
|
||||
from lms.djangoapps.teams.models import CourseTeam, CourseTeamMembership
|
||||
from openedx.core.lib.teams_config import TeamsetType
|
||||
from openedx.core.lib.api.authentication import BearerAuthentication
|
||||
from openedx.core.lib.api.parsers import MergePatchParser
|
||||
from openedx.core.lib.api.permissions import IsCourseStaffInstructor, IsStaffOrReadOnly
|
||||
from openedx.core.lib.api.view_utils import (
|
||||
@@ -41,16 +42,15 @@ from openedx.core.lib.api.view_utils import (
|
||||
add_serializer_errors,
|
||||
build_api_error
|
||||
)
|
||||
from common.djangoapps.student.models import CourseAccessRole, CourseEnrollment
|
||||
from common.djangoapps.util.model_utils import truncate_fields
|
||||
from openedx.core.lib.teams_config import TeamsetType
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
from . import is_feature_enabled
|
||||
from .api import (
|
||||
OrganizationProtectionStatus,
|
||||
add_team_count,
|
||||
can_user_modify_team,
|
||||
can_user_create_team_in_topic,
|
||||
can_user_modify_team,
|
||||
get_assignments_for_team,
|
||||
has_course_staff_privileges,
|
||||
has_specific_team_access,
|
||||
@@ -58,7 +58,7 @@ from .api import (
|
||||
has_team_api_access,
|
||||
user_organization_protection_status
|
||||
)
|
||||
from .csv import load_team_membership_csv, TeamMembershipImportManager
|
||||
from .csv import TeamMembershipImportManager, load_team_membership_csv
|
||||
from .errors import AlreadyOnTeamInTeamset, ElasticSearchConnectionError, NotEnrolledInCourseForTeam
|
||||
from .search_indexes import CourseTeamIndexer
|
||||
from .serializers import (
|
||||
@@ -68,8 +68,8 @@ from .serializers import (
|
||||
MembershipSerializer,
|
||||
TopicSerializer
|
||||
)
|
||||
from .utils import emit_team_event
|
||||
from .toggles import are_team_submissions_enabled
|
||||
from .utils import emit_team_event
|
||||
|
||||
TEAM_MEMBERSHIPS_PER_PAGE = 5
|
||||
TOPICS_PER_PAGE = 12
|
||||
@@ -87,8 +87,8 @@ def team_post_save_callback(sender, instance, **kwargs): # pylint: disable=unus
|
||||
for field in changed_fields:
|
||||
if field not in instance.FIELD_BLACKLIST:
|
||||
truncated_fields = truncate_fields(
|
||||
six.text_type(changed_fields[field]),
|
||||
six.text_type(getattr(instance, field))
|
||||
str(changed_fields[field]),
|
||||
str(getattr(instance, field))
|
||||
)
|
||||
truncated_fields['team_id'] = instance.team_id
|
||||
truncated_fields['team_id'] = instance.team_id
|
||||
@@ -412,7 +412,7 @@ class TeamsListView(ExpandableFieldViewMixin, GenericAPIView):
|
||||
course_module = modulestore().get_course(course_key)
|
||||
except InvalidKeyError:
|
||||
error = build_api_error(
|
||||
ugettext_noop(u"The supplied course id {course_id} is not valid."),
|
||||
ugettext_noop("The supplied course id {course_id} is not valid."),
|
||||
course_id=course_id_string,
|
||||
)
|
||||
return Response(error, status=status.HTTP_400_BAD_REQUEST)
|
||||
@@ -439,7 +439,7 @@ class TeamsListView(ExpandableFieldViewMixin, GenericAPIView):
|
||||
if topic_id is not None:
|
||||
if topic_id not in course_module.teamsets_by_id:
|
||||
error = build_api_error(
|
||||
ugettext_noop(u'The supplied topic id {topic_id} is not valid'),
|
||||
ugettext_noop('The supplied topic id {topic_id} is not valid'),
|
||||
topic_id=topic_id
|
||||
)
|
||||
return Response(error, status=status.HTTP_400_BAD_REQUEST)
|
||||
@@ -514,14 +514,14 @@ class TeamsListView(ExpandableFieldViewMixin, GenericAPIView):
|
||||
if order_by_input not in ordering_schemes:
|
||||
return Response(
|
||||
{
|
||||
'developer_message': u"unsupported order_by value {ordering}".format(
|
||||
'developer_message': "unsupported order_by value {ordering}".format(
|
||||
ordering=order_by_input,
|
||||
),
|
||||
# Translators: 'ordering' is a string describing a way
|
||||
# of ordering a list. For example, {ordering} may be
|
||||
# 'name', indicating that the user wants to sort the
|
||||
# list by lower case name.
|
||||
'user_message': _(u"The ordering {ordering} is not supported").format(
|
||||
'user_message': _("The ordering {ordering} is not supported").format(
|
||||
ordering=order_by_input,
|
||||
),
|
||||
},
|
||||
@@ -548,7 +548,7 @@ class TeamsListView(ExpandableFieldViewMixin, GenericAPIView):
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
except InvalidKeyError:
|
||||
field_errors['course_id'] = build_api_error(
|
||||
ugettext_noop(u'The supplied course_id {course_id} is not valid.'),
|
||||
ugettext_noop('The supplied course_id {course_id} is not valid.'),
|
||||
course_id=course_id
|
||||
)
|
||||
return Response({
|
||||
@@ -558,7 +558,7 @@ class TeamsListView(ExpandableFieldViewMixin, GenericAPIView):
|
||||
topic_id = request.data.get('topic_id')
|
||||
if not topic_id:
|
||||
field_errors['topic_id'] = build_api_error(
|
||||
ugettext_noop(u'topic_id is required'),
|
||||
ugettext_noop('topic_id is required'),
|
||||
course_id=course_id
|
||||
)
|
||||
return Response({
|
||||
@@ -596,7 +596,7 @@ class TeamsListView(ExpandableFieldViewMixin, GenericAPIView):
|
||||
return Response(error_message, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
data = request.data.copy()
|
||||
data['course_id'] = six.text_type(course_key)
|
||||
data['course_id'] = str(course_key)
|
||||
|
||||
organization_protection_status = user_organization_protection_status(request.user, course_key)
|
||||
if organization_protection_status != OrganizationProtectionStatus.protection_exempt:
|
||||
@@ -676,7 +676,7 @@ class IsStaffOrPrivilegedOrReadOnly(IsStaffOrReadOnly):
|
||||
return (
|
||||
has_discussion_privileges(request.user, obj.course_id) or
|
||||
IsCourseStaffInstructor.has_object_permission(self, request, view, obj) or
|
||||
super(IsStaffOrPrivilegedOrReadOnly, self).has_object_permission(request, view, obj) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().has_object_permission(request, view, obj)
|
||||
)
|
||||
|
||||
|
||||
@@ -814,7 +814,7 @@ class TeamsDetailView(ExpandableFieldViewMixin, RetrievePatchAPIView):
|
||||
|
||||
# Note: also deletes all team memberships associated with this team
|
||||
team.delete()
|
||||
log.info(u'user %d deleted team %s', request.user.id, team_id)
|
||||
log.info('user %d deleted team %s', request.user.id, team_id)
|
||||
emit_team_event('edx.team.deleted', team.course_id, {
|
||||
'team_id': team_id,
|
||||
})
|
||||
@@ -983,7 +983,7 @@ class TopicListView(GenericAPIView):
|
||||
return Response({
|
||||
'field_errors': {
|
||||
'course_id': build_api_error(
|
||||
ugettext_noop(u"The supplied course id {course_id} is not valid."),
|
||||
ugettext_noop("The supplied course id {course_id} is not valid."),
|
||||
course_id=course_id_string
|
||||
)
|
||||
}
|
||||
@@ -1005,12 +1005,12 @@ class TopicListView(GenericAPIView):
|
||||
ordering = request.query_params.get('order_by', 'name')
|
||||
if ordering not in ['name', 'team_count']:
|
||||
return Response({
|
||||
'developer_message': u"unsupported order_by value {ordering}".format(ordering=ordering),
|
||||
'developer_message': f"unsupported order_by value {ordering}",
|
||||
# Translators: 'ordering' is a string describing a way
|
||||
# of ordering a list. For example, {ordering} may be
|
||||
# 'name', indicating that the user wants to sort the
|
||||
# list by lower case name.
|
||||
'user_message': _(u"The ordering {ordering} is not supported").format(ordering=ordering),
|
||||
'user_message': _("The ordering {ordering} is not supported").format(ordering=ordering),
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Always sort alphabetically, as it will be used as secondary sort
|
||||
@@ -1636,7 +1636,7 @@ class MembershipBulkManagementView(GenericAPIView):
|
||||
filename = "team-membership_{}_{}_{}.csv".format(
|
||||
self.course.id.org, self.course.id.course, self.course.id.run
|
||||
)
|
||||
response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
|
||||
response['Content-Disposition'] = f'attachment; filename="{filename}"'
|
||||
load_team_membership_csv(self.course, response)
|
||||
return response
|
||||
|
||||
@@ -1651,7 +1651,7 @@ class MembershipBulkManagementView(GenericAPIView):
|
||||
team_import_manager.set_team_membership_from_csv(inputfile_handle)
|
||||
|
||||
if team_import_manager.import_succeeded:
|
||||
msg = "{} learners were affected.".format(team_import_manager.number_of_learners_assigned)
|
||||
msg = f"{team_import_manager.number_of_learners_assigned} learners were affected."
|
||||
return JsonResponse({'message': msg}, status=status.HTTP_201_CREATED)
|
||||
else:
|
||||
return JsonResponse({
|
||||
@@ -1681,8 +1681,8 @@ class MembershipBulkManagementView(GenericAPIView):
|
||||
try:
|
||||
course_id = CourseKey.from_string(course_id_string)
|
||||
except InvalidKeyError:
|
||||
raise Http404('Invalid course key: {}'.format(course_id_string)) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
raise Http404(f'Invalid course key: {course_id_string}') # lint-amnesty, pylint: disable=raise-missing-from
|
||||
course_module = modulestore().get_course(course_id)
|
||||
if not course_module:
|
||||
raise Http404('Course not found: {}'.format(course_id))
|
||||
raise Http404(f'Course not found: {course_id}')
|
||||
return course_module
|
||||
|
||||
Reference in New Issue
Block a user