pyupgrade on survey, teams and tests apps (#26647)

This commit is contained in:
M. Zulqarnain
2021-02-23 15:50:22 +05:00
committed by GitHub
parent 57684f9938
commit 0fdb3fce9a
33 changed files with 225 additions and 242 deletions

View File

@@ -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')

View File

@@ -1,7 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models
import django.utils.timezone
from django.conf import settings

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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):
"""

View File

@@ -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):

View File

@@ -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")

View File

@@ -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:

View File

@@ -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 Masters track users mixed with users in other tracks.'.format(team_name)
f'Team {team_name} cannot have Masters track users mixed with users in other tracks.'
self.add_error_and_check_if_max_exceeded(error_message)
return False
return True

View File

@@ -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)

View File

@@ -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)

View File

@@ -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')},
),
]

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.25 on 2019-10-22 14:42

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.25 on 2019-11-04 19:15

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-03-18 15:36

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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):

View File

@@ -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

View File

@@ -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')

View File

@@ -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):

View File

@@ -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)

View File

@@ -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):
"""

View File

@@ -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)

View File

@@ -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)

View File

@@ -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"

View File

@@ -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