diff --git a/lms/djangoapps/survey/admin.py b/lms/djangoapps/survey/admin.py index 69d63257f1..50a3bc0cc7 100644 --- a/lms/djangoapps/survey/admin.py +++ b/lms/djangoapps/survey/admin.py @@ -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') diff --git a/lms/djangoapps/survey/migrations/0001_initial.py b/lms/djangoapps/survey/migrations/0001_initial.py index 91f3cc5316..1fc7e579e1 100644 --- a/lms/djangoapps/survey/migrations/0001_initial.py +++ b/lms/djangoapps/survey/migrations/0001_initial.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- - - - from django.db import migrations, models import django.utils.timezone from django.conf import settings diff --git a/lms/djangoapps/survey/models.py b/lms/djangoapps/survey/models.py index c978bc7c36..1f234fcf33 100644 --- a/lms/djangoapps/survey/models.py +++ b/lms/djangoapps/survey/models.py @@ -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'
{}
').format(HTML(html))) + tree = etree.fromstring(HTML('
{}
').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 diff --git a/lms/djangoapps/survey/signals.py b/lms/djangoapps/survey/signals.py index 8e13f6f2ee..c43238e927 100644 --- a/lms/djangoapps/survey/signals.py +++ b/lms/djangoapps/survey/signals.py @@ -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) diff --git a/lms/djangoapps/survey/tests/factories.py b/lms/djangoapps/survey/tests/factories.py index f65732d000..dd3c35b35e 100644 --- a/lms/djangoapps/survey/tests/factories.py +++ b/lms/djangoapps/survey/tests/factories.py @@ -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) diff --git a/lms/djangoapps/survey/tests/test_models.py b/lms/djangoapps/survey/tests/test_models.py index d15e6f1a55..4c00cd0d24 100644 --- a/lms/djangoapps/survey/tests/test_models.py +++ b/lms/djangoapps/survey/tests/test_models.py @@ -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 diff --git a/lms/djangoapps/survey/tests/test_signals.py b/lms/djangoapps/survey/tests/test_signals.py index a08f4b8bf8..fb4ae8ea7d 100644 --- a/lms/djangoapps/survey/tests/test_signals.py +++ b/lms/djangoapps/survey/tests/test_signals.py @@ -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 diff --git a/lms/djangoapps/survey/tests/test_utils.py b/lms/djangoapps/survey/tests/test_utils.py index 326b97367d..3a312cc8bd 100644 --- a/lms/djangoapps/survey/tests/test_utils.py +++ b/lms/djangoapps/survey/tests/test_utils.py @@ -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() diff --git a/lms/djangoapps/survey/tests/test_views.py b/lms/djangoapps/survey/tests/test_views.py index 3d5be2bb35..55910a3210 100644 --- a/lms/djangoapps/survey/tests/test_views.py +++ b/lms/djangoapps/survey/tests/test_views.py @@ -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): """ diff --git a/lms/djangoapps/survey/utils.py b/lms/djangoapps/survey/utils.py index 246c573d69..889fc90c11 100644 --- a/lms/djangoapps/survey/utils.py +++ b/lms/djangoapps/survey/utils.py @@ -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): diff --git a/lms/djangoapps/survey/views.py b/lms/djangoapps/survey/views.py index b0362ce540..c6a3b56979 100644 --- a/lms/djangoapps/survey/views.py +++ b/lms/djangoapps/survey/views.py @@ -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") diff --git a/lms/djangoapps/teams/api.py b/lms/djangoapps/teams/api.py index f0f0fe8668..d8297d8ac9 100644 --- a/lms/djangoapps/teams/api.py +++ b/lms/djangoapps/teams/api.py @@ -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: diff --git a/lms/djangoapps/teams/csv.py b/lms/djangoapps/teams/csv.py index e6c672d2e4..d6b6045c6d 100644 --- a/lms/djangoapps/teams/csv.py +++ b/lms/djangoapps/teams/csv.py @@ -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 diff --git a/lms/djangoapps/teams/management/commands/reindex_course_team.py b/lms/djangoapps/teams/management/commands/reindex_course_team.py index 6f047877df..486325a569 100644 --- a/lms/djangoapps/teams/management/commands/reindex_course_team.py +++ b/lms/djangoapps/teams/management/commands/reindex_course_team.py @@ -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) diff --git a/lms/djangoapps/teams/management/commands/tests/test_reindex_course_team.py b/lms/djangoapps/teams/management/commands/tests/test_reindex_course_team.py index 10b289d039..7ddd2f6249 100644 --- a/lms/djangoapps/teams/management/commands/tests/test_reindex_course_team.py +++ b/lms/djangoapps/teams/management/commands/tests/test_reindex_course_team.py @@ -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) diff --git a/lms/djangoapps/teams/migrations/0001_initial.py b/lms/djangoapps/teams/migrations/0001_initial.py index 1fdc9a60cf..6c68802753 100644 --- a/lms/djangoapps/teams/migrations/0001_initial.py +++ b/lms/djangoapps/teams/migrations/0001_initial.py @@ -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')}, ), ] diff --git a/lms/djangoapps/teams/migrations/0002_slug_field_ids.py b/lms/djangoapps/teams/migrations/0002_slug_field_ids.py index 834f51c449..9dbec7acdc 100644 --- a/lms/djangoapps/teams/migrations/0002_slug_field_ids.py +++ b/lms/djangoapps/teams/migrations/0002_slug_field_ids.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.25 on 2019-10-22 14:42 diff --git a/lms/djangoapps/teams/migrations/0003_courseteam_organization_protected.py b/lms/djangoapps/teams/migrations/0003_courseteam_organization_protected.py index 9c201539da..353a2c92b7 100644 --- a/lms/djangoapps/teams/migrations/0003_courseteam_organization_protected.py +++ b/lms/djangoapps/teams/migrations/0003_courseteam_organization_protected.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.25 on 2019-11-04 19:15 diff --git a/lms/djangoapps/teams/migrations/0004_alter_defaults.py b/lms/djangoapps/teams/migrations/0004_alter_defaults.py index 28a878812c..6148090422 100644 --- a/lms/djangoapps/teams/migrations/0004_alter_defaults.py +++ b/lms/djangoapps/teams/migrations/0004_alter_defaults.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.29 on 2020-03-18 15:36 diff --git a/lms/djangoapps/teams/models.py b/lms/djangoapps/teams/models.py index 24fde748d8..7eb6c64de7 100644 --- a/lms/djangoapps/teams/models.py +++ b/lms/djangoapps/teams/models.py @@ -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 diff --git a/lms/djangoapps/teams/plugins.py b/lms/djangoapps/teams/plugins.py index da67b48e76..e60df52fcd 100644 --- a/lms/djangoapps/teams/plugins.py +++ b/lms/djangoapps/teams/plugins.py @@ -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) diff --git a/lms/djangoapps/teams/search_indexes.py b/lms/djangoapps/teams/search_indexes.py index 048a307194..349a1ba65a 100644 --- a/lms/djangoapps/teams/search_indexes.py +++ b/lms/djangoapps/teams/search_indexes.py @@ -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 diff --git a/lms/djangoapps/teams/serializers.py b/lms/djangoapps/teams/serializers.py index 9aab17694a..1439d6cfd6 100644 --- a/lms/djangoapps/teams/serializers.py +++ b/lms/djangoapps/teams/serializers.py @@ -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 diff --git a/lms/djangoapps/teams/services.py b/lms/djangoapps/teams/services.py index b07622ac4c..5c5b5c6bf0 100644 --- a/lms/djangoapps/teams/services.py +++ b/lms/djangoapps/teams/services.py @@ -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): diff --git a/lms/djangoapps/teams/tests/factories.py b/lms/djangoapps/teams/tests/factories.py index fd76b9f40d..178d926183 100644 --- a/lms/djangoapps/teams/tests/factories.py +++ b/lms/djangoapps/teams/tests/factories.py @@ -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 diff --git a/lms/djangoapps/teams/tests/test_api.py b/lms/djangoapps/teams/tests/test_api.py index 3aaba90677..103b7b070a 100644 --- a/lms/djangoapps/teams/tests/test_api.py +++ b/lms/djangoapps/teams/tests/test_api.py @@ -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') diff --git a/lms/djangoapps/teams/tests/test_csv.py b/lms/djangoapps/teams/tests/test_csv.py index 842d0a4825..b7e1db2102 100644 --- a/lms/djangoapps/teams/tests/test_csv.py +++ b/lms/djangoapps/teams/tests/test_csv.py @@ -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): diff --git a/lms/djangoapps/teams/tests/test_models.py b/lms/djangoapps/teams/tests/test_models.py index 3848ed2e88..fe7f3ac01b 100644 --- a/lms/djangoapps/teams/tests/test_models.py +++ b/lms/djangoapps/teams/tests/test_models.py @@ -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) diff --git a/lms/djangoapps/teams/tests/test_serializers.py b/lms/djangoapps/teams/tests/test_serializers.py index b77fea6c85..e8e1fc2554 100644 --- a/lms/djangoapps/teams/tests/test_serializers.py +++ b/lms/djangoapps/teams/tests/test_serializers.py @@ -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): """ diff --git a/lms/djangoapps/teams/tests/test_services.py b/lms/djangoapps/teams/tests/test_services.py index d6eb53ad6a..8b467112c0 100644 --- a/lms/djangoapps/teams/tests/test_services.py +++ b/lms/djangoapps/teams/tests/test_services.py @@ -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) diff --git a/lms/djangoapps/teams/tests/test_views.py b/lms/djangoapps/teams/tests/test_views.py index 1eeec4c186..cf451bbb97 100644 --- a/lms/djangoapps/teams/tests/test_views.py +++ b/lms/djangoapps/teams/tests/test_views.py @@ -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) diff --git a/lms/djangoapps/teams/toggles.py b/lms/djangoapps/teams/toggles.py index d75ca8d662..f9a100e15b 100644 --- a/lms/djangoapps/teams/toggles.py +++ b/lms/djangoapps/teams/toggles.py @@ -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" diff --git a/lms/djangoapps/teams/views.py b/lms/djangoapps/teams/views.py index 896dc7f9b5..88e2eaf060 100644 --- a/lms/djangoapps/teams/views.py +++ b/lms/djangoapps/teams/views.py @@ -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