From 16f600910be2ba92bf9252da13432fb91f77a2c8 Mon Sep 17 00:00:00 2001 From: "M. Zulqarnain" Date: Mon, 22 Feb 2021 15:42:32 +0500 Subject: [PATCH] pyupgrade on lms edxnotes,email_marketing,experiments apps (#26531) --- lms/djangoapps/edxnotes/decorators.py | 5 +- lms/djangoapps/edxnotes/helpers.py | 45 +- lms/djangoapps/edxnotes/plugins.py | 2 +- lms/djangoapps/edxnotes/tests.py | 469 +++++++++--------- lms/djangoapps/edxnotes/views.py | 13 +- .../migrations/0001_initial.py | 3 - .../migrations/0002_auto_20160623_1656.py | 3 - .../migrations/0003_auto_20160715_1145.py | 3 - ...gconfiguration_welcome_email_send_delay.py | 3 - ..._user_registration_cookie_timeout_delay.py | 3 - .../migrations/0006_auto_20170711_0615.py | 3 - .../migrations/0007_auto_20170809_0653.py | 3 - .../migrations/0008_auto_20170809_0539.py | 3 - ...figuration_sailthru_activation_template.py | 3 - .../migrations/0010_auto_20180425_0800.py | 1 - lms/djangoapps/email_marketing/models.py | 4 +- lms/djangoapps/email_marketing/signals.py | 29 +- lms/djangoapps/email_marketing/tasks.py | 51 +- .../email_marketing/tests/test_signals.py | 27 +- lms/djangoapps/experiments/apps.py | 2 +- lms/djangoapps/experiments/factories.py | 6 +- lms/djangoapps/experiments/filters.py | 4 +- lms/djangoapps/experiments/flags.py | 10 +- .../experiments/migrations/0001_initial.py | 10 +- .../migrations/0002_auto_20170627_1402.py | 8 +- .../migrations/0003_auto_20170713_1148.py | 4 - .../0004_historicalexperimentkeyvalue.py | 1 - lms/djangoapps/experiments/models.py | 8 +- lms/djangoapps/experiments/permissions.py | 2 +- lms/djangoapps/experiments/serializers.py | 4 +- .../experiments/tests/test_flags.py | 9 +- .../experiments/tests/test_utils.py | 13 +- .../experiments/tests/test_views.py | 28 +- .../experiments/tests/test_views_custom.py | 21 +- lms/djangoapps/experiments/testutils.py | 3 +- lms/djangoapps/experiments/utils.py | 49 +- lms/djangoapps/experiments/views.py | 16 +- lms/djangoapps/experiments/views_custom.py | 16 +- 38 files changed, 408 insertions(+), 479 deletions(-) diff --git a/lms/djangoapps/edxnotes/decorators.py b/lms/djangoapps/edxnotes/decorators.py index 277ac8ca05..ef039061b3 100644 --- a/lms/djangoapps/edxnotes/decorators.py +++ b/lms/djangoapps/edxnotes/decorators.py @@ -5,7 +5,6 @@ Decorators related to edXNotes. import json -import six from django.conf import settings from common.djangoapps.edxmako.shortcuts import render_to_string @@ -51,8 +50,8 @@ def edxnotes(cls): ), "params": { # Use camelCase to name keys. - "usageId": six.text_type(self.scope_ids.usage_id), - "courseId": six.text_type(self.runtime.course_id), + "usageId": str(self.scope_ids.usage_id), + "courseId": str(self.runtime.course_id), "token": get_edxnotes_id_token(user), "tokenUrl": get_token_url(self.runtime.course_id), "endpoint": get_public_endpoint(), diff --git a/lms/djangoapps/edxnotes/helpers.py b/lms/djangoapps/edxnotes/helpers.py index 66f0ba7843..50f26d06f2 100644 --- a/lms/djangoapps/edxnotes/helpers.py +++ b/lms/djangoapps/edxnotes/helpers.py @@ -7,11 +7,10 @@ import json import logging from datetime import datetime from json import JSONEncoder +from urllib.parse import parse_qs, urlencode, urlparse from uuid import uuid4 import requests -import six -from six.moves.urllib.parse import urlencode, urlparse, parse_qs from dateutil.parser import parse as dateutil_parse from django.conf import settings from django.core.exceptions import ImproperlyConfigured @@ -21,6 +20,8 @@ from oauth2_provider.models import Application from opaque_keys.edx.keys import UsageKey from requests.exceptions import RequestException +from common.djangoapps.student.models import anonymous_id_for_user +from common.djangoapps.util.date_utils import get_default_time_display from lms.djangoapps.courseware.access import has_access from lms.djangoapps.courseware.courses import get_current_child from lms.djangoapps.edxnotes.exceptions import EdxNotesParseError, EdxNotesServiceUnavailable @@ -28,8 +29,6 @@ from lms.djangoapps.edxnotes.plugins import EdxNotesTab from lms.lib.utils import get_parent_unit from openedx.core.djangoapps.oauth_dispatch.jwt import create_jwt_for_user from openedx.core.djangolib.markup import Text -from common.djangoapps.student.models import anonymous_id_for_user -from common.djangoapps.util.date_utils import get_default_time_display from xmodule.modulestore.django import modulestore from xmodule.modulestore.exceptions import ItemNotFoundError @@ -58,7 +57,7 @@ def get_edxnotes_id_token(user): notes_application = Application.objects.get(name=settings.EDXNOTES_CLIENT_NAME) except Application.DoesNotExist: raise ImproperlyConfigured( # lint-amnesty, pylint: disable=raise-missing-from - u'OAuth2 Client with name [{}] does not exist.'.format(settings.EDXNOTES_CLIENT_NAME) + f'OAuth2 Client with name [{settings.EDXNOTES_CLIENT_NAME}] does not exist.' ) return create_jwt_for_user( user, secret=notes_application.client_secret, aud=notes_application.client_id @@ -70,7 +69,7 @@ def get_token_url(course_id): Returns token url for the course. """ return reverse("get_token", kwargs={ - "course_id": six.text_type(course_id), + "course_id": str(course_id), }) @@ -92,7 +91,7 @@ def send_request(user, course_id, page, page_size, path="", text=None): url = get_internal_endpoint(path) params = { "user": anonymous_id_for_user(user, None), - "course_id": six.text_type(course_id), + "course_id": str(course_id), "page": page, "page_size": page_size, } @@ -113,7 +112,7 @@ def send_request(user, course_id, page, page_size, path="", text=None): timeout=(settings.EDXNOTES_CONNECT_TIMEOUT, settings.EDXNOTES_READ_TIMEOUT) ) except RequestException: - log.error(u"Failed to connect to edx-notes-api: url=%s, params=%s", url, str(params)) + log.error("Failed to connect to edx-notes-api: url=%s, params=%s", url, str(params)) raise EdxNotesServiceUnavailable(_("EdxNotes Service is unavailable. Please try again in a few minutes.")) # lint-amnesty, pylint: disable=raise-missing-from return response @@ -144,7 +143,7 @@ def delete_all_notes_for_user(user): timeout=(settings.EDXNOTES_CONNECT_TIMEOUT, settings.EDXNOTES_READ_TIMEOUT) ) except RequestException: - log.error(u"Failed to connect to edx-notes-api: url=%s, params=%s", url, str(headers)) + log.error("Failed to connect to edx-notes-api: url=%s, params=%s", url, str(headers)) raise EdxNotesServiceUnavailable(_("EdxNotes Service is unavailable. Please try again in a few minutes.")) # lint-amnesty, pylint: disable=raise-missing-from return response @@ -169,7 +168,7 @@ def preprocess_collection(user, course, collection): with store.bulk_operations(course.id): for model in collection: update = { - u"updated": dateutil_parse(model["updated"]), + "updated": dateutil_parse(model["updated"]), } model.update(update) @@ -186,22 +185,22 @@ def preprocess_collection(user, course, collection): try: item = store.get_item(usage_key) except ItemNotFoundError: - log.debug(u"Module not found: %s", usage_key) + log.debug("Module not found: %s", usage_key) continue if not has_access(user, "load", item, course_key=course.id): - log.debug(u"User %s does not have an access to %s", user, item) + log.debug("User %s does not have an access to %s", user, item) continue unit = get_parent_unit(item) if unit is None: - log.debug(u"Unit not found: %s", usage_key) + log.debug("Unit not found: %s", usage_key) continue if include_path_info: section = unit.get_parent() if not section: - log.debug(u"Section not found: %s", usage_key) + log.debug("Section not found: %s", usage_key) continue if section.location in list(cache.keys()): usage_context = cache[section.location] @@ -215,7 +214,7 @@ def preprocess_collection(user, course, collection): chapter = section.get_parent() if not chapter: - log.debug(u"Chapter not found: %s", usage_key) + log.debug("Chapter not found: %s", usage_key) continue if chapter.location in list(cache.keys()): usage_context = cache[chapter.location] @@ -248,7 +247,7 @@ def get_module_context(course, item): Returns dispay_name and url for the parent module. """ item_dict = { - 'location': six.text_type(item.location), + 'location': str(item.location), 'display_name': Text(item.display_name_with_default), } if item.category == 'chapter' and item.get_parent(): @@ -260,15 +259,15 @@ def get_module_context(course, item): section = item.get_parent() chapter = section.get_parent() # Position starts from 1, that's why we add 1. - position = get_index(six.text_type(item.location), section.children) + 1 + position = get_index(str(item.location), section.children) + 1 item_dict['url'] = reverse('courseware_position', kwargs={ - 'course_id': six.text_type(course.id), + 'course_id': str(course.id), 'chapter': chapter.url_name, 'section': section.url_name, 'position': position, }) if item.category in ('chapter', 'sequential'): - item_dict['children'] = [six.text_type(child) for child in item.children] + item_dict['children'] = [str(child) for child in item.children] return item_dict @@ -277,7 +276,7 @@ def get_index(usage_key, children): """ Returns an index of the child with `usage_key`. """ - children = [six.text_type(child) for child in children] + children = [str(child) for child in children] return children.index(usage_key) @@ -345,14 +344,14 @@ def get_notes(request, course, page=DEFAULT_PAGE, page_size=DEFAULT_PAGE_SIZE, t try: collection = json.loads(response.content.decode('utf-8')) except ValueError: - log.error(u"Invalid JSON response received from notes api: response_content=%s", response.content) + log.error("Invalid JSON response received from notes api: response_content=%s", response.content) raise EdxNotesParseError(_("Invalid JSON response received from notes api.")) # lint-amnesty, pylint: disable=raise-missing-from # Verify response dict structure expected_keys = ['total', 'rows', 'num_pages', 'start', 'next', 'previous', 'current_page'] keys = list(collection.keys()) if not keys or not all(key in expected_keys for key in keys): - log.error(u"Incorrect data received from notes api: collection_data=%s", str(collection)) + log.error("Incorrect data received from notes api: collection_data=%s", str(collection)) raise EdxNotesParseError(_("Incorrect data received from notes api.")) filtered_results = preprocess_collection(request.user, course, collection['rows']) @@ -419,7 +418,7 @@ def get_course_position(course_module): If there is no current position in the course or chapter, then selects the first child. """ - urlargs = {'course_id': six.text_type(course_module.id)} + urlargs = {'course_id': str(course_module.id)} chapter = get_current_child(course_module, min_depth=1) if chapter is None: log.debug("No chapter found when loading current position in course") diff --git a/lms/djangoapps/edxnotes/plugins.py b/lms/djangoapps/edxnotes/plugins.py index 45f468121f..31c0103234 100644 --- a/lms/djangoapps/edxnotes/plugins.py +++ b/lms/djangoapps/edxnotes/plugins.py @@ -26,7 +26,7 @@ class EdxNotesTab(EnrolledTab): course (CourseDescriptor): the course using the feature user (User): the user interacting with the course """ - if not super(EdxNotesTab, cls).is_enabled(course, user=user): + if not super().is_enabled(course, user=user): return False if not settings.FEATURES.get("ENABLE_EDXNOTES"): diff --git a/lms/djangoapps/edxnotes/tests.py b/lms/djangoapps/edxnotes/tests.py index 0436848e85..c4f18a4144 100644 --- a/lms/djangoapps/edxnotes/tests.py +++ b/lms/djangoapps/edxnotes/tests.py @@ -6,39 +6,39 @@ import json from contextlib import contextmanager from datetime import datetime from unittest import skipUnless -import pytest +from unittest.mock import MagicMock, patch import ddt import jwt +import pytest import six -from six import text_type -from six.moves.urllib.parse import urlparse, parse_qs from django.conf import settings from django.contrib.auth.models import AnonymousUser from django.core.exceptions import ImproperlyConfigured from django.test.client import RequestFactory from django.test.utils import override_settings from django.urls import reverse -from mock import MagicMock, patch from oauth2_provider.models import Application +from six.moves.urllib.parse import parse_qs, urlparse +from common.djangoapps.edxmako.shortcuts import render_to_string +from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, SuperuserFactory, UserFactory from lms.djangoapps.courseware.model_data import FieldDataCache from lms.djangoapps.courseware.module_render import get_module_for_descriptor from lms.djangoapps.courseware.tabs import get_course_tab_list -from common.djangoapps.edxmako.shortcuts import render_to_string +from openedx.core.djangoapps.oauth_dispatch.jwt import create_jwt_for_user +from openedx.core.djangoapps.oauth_dispatch.tests.factories import ApplicationFactory +from openedx.core.djangoapps.user_api.models import RetirementState, UserRetirementStatus +from xmodule.modulestore import ModuleStoreEnum +from xmodule.modulestore.django import modulestore +from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase +from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory +from xmodule.tabs import CourseTab + from . import helpers from .decorators import edxnotes from .exceptions import EdxNotesParseError, EdxNotesServiceUnavailable from .plugins import EdxNotesTab -from openedx.core.djangoapps.oauth_dispatch.jwt import create_jwt_for_user # lint-amnesty, pylint: disable=wrong-import-order -from openedx.core.djangoapps.oauth_dispatch.tests.factories import ApplicationFactory # lint-amnesty, pylint: disable=wrong-import-order -from openedx.core.djangoapps.user_api.models import RetirementState, UserRetirementStatus # lint-amnesty, pylint: disable=wrong-import-order -from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, SuperuserFactory, UserFactory # lint-amnesty, pylint: disable=wrong-import-order -from xmodule.modulestore import ModuleStoreEnum # lint-amnesty, pylint: disable=wrong-import-order -from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order -from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order -from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory # lint-amnesty, pylint: disable=wrong-import-order -from xmodule.tabs import CourseTab # lint-amnesty, pylint: disable=wrong-import-order FEATURES = settings.FEATURES.copy() @@ -72,7 +72,7 @@ def enable_edxnotes_for_the_course(course, user_id): @edxnotes -class TestProblem(object): +class TestProblem: """ Test class (fake problem) decorated by edxnotes decorator. @@ -100,7 +100,7 @@ class EdxNotesDecoratorTest(ModuleStoreTestCase): """ def setUp(self): - super(EdxNotesDecoratorTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() ApplicationFactory(name="edx-notes") # Using old mongo because of locator comparison issues (see longer @@ -202,7 +202,7 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): """ Setup a dummy course content. """ - super(EdxNotesHelpersTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() # There are many tests that are comparing locators as returned from helper methods. When using # the split modulestore, some of those locators have version and branch information, but the @@ -324,23 +324,22 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): "num_pages": 1, "rows": [ { - u"quote": u"quote text", - u"text": u"text", - u"usage_id": text_type(self.html_module_1.location), - u"updated": datetime(2014, 11, 19, 8, 5, 16, 00000).isoformat(), + "quote": "quote text", + "text": "text", + "usage_id": str(self.html_module_1.location), + "updated": datetime(2014, 11, 19, 8, 5, 16, 00000).isoformat(), }, { - u"quote": u"quote text", - u"text": u"text", - u"usage_id": text_type(self.html_module_2.location), - u"updated": datetime(2014, 11, 19, 8, 6, 16, 00000).isoformat(), + "quote": "quote text", + "text": "text", + "usage_id": str(self.html_module_2.location), + "updated": datetime(2014, 11, 19, 8, 6, 16, 00000).isoformat(), } ] } ).encode('utf-8') - six.assertCountEqual( - self, + assert len( { "count": 2, "current_page": 1, @@ -350,57 +349,55 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): "num_pages": 1, "results": [ { - u"quote": u"quote text", - u"text": u"text", - u"chapter": { - u"display_name": self.chapter.display_name_with_default, - u"index": 0, - u"location": text_type(self.chapter.location), - u"children": [text_type(self.sequential.location)] + "quote": "quote text", + "text": "text", + "chapter": { + "display_name": self.chapter.display_name_with_default, + "index": 0, + "location": str(self.chapter.location), + "children": [str(self.sequential.location)] }, - u"section": { - u"display_name": self.sequential.display_name_with_default, - u"location": text_type(self.sequential.location), - u"children": [ - text_type(self.vertical.location), text_type(self.vertical_with_container.location) + "section": { + "display_name": self.sequential.display_name_with_default, + "location": str(self.sequential.location), + "children": [ + str(self.vertical.location), str(self.vertical_with_container.location) ] }, - u"unit": { - u"url": self._get_unit_url(self.course, self.chapter, self.sequential), - u"display_name": self.vertical.display_name_with_default, - u"location": text_type(self.vertical.location), + "unit": { + "url": self._get_unit_url(self.course, self.chapter, self.sequential), + "display_name": self.vertical.display_name_with_default, + "location": str(self.vertical.location), }, - u"usage_id": text_type(self.html_module_2.location), - u"updated": "Nov 19, 2014 at 08:06 UTC", + "usage_id": str(self.html_module_2.location), + "updated": "Nov 19, 2014 at 08:06 UTC", }, { - u"quote": u"quote text", - u"text": u"text", - u"chapter": { - u"display_name": self.chapter.display_name_with_default, - u"index": 0, - u"location": text_type(self.chapter.location), - u"children": [text_type(self.sequential.location)] + "quote": "quote text", + "text": "text", + "chapter": { + "display_name": self.chapter.display_name_with_default, + "index": 0, + "location": str(self.chapter.location), + "children": [str(self.sequential.location)] }, - u"section": { - u"display_name": self.sequential.display_name_with_default, - u"location": text_type(self.sequential.location), - u"children": [ - text_type(self.vertical.location), - text_type(self.vertical_with_container.location)] + "section": { + "display_name": self.sequential.display_name_with_default, + "location": str(self.sequential.location), + "children": [ + str(self.vertical.location), + str(self.vertical_with_container.location)] }, - u"unit": { - u"url": self._get_unit_url(self.course, self.chapter, self.sequential), - u"display_name": self.vertical.display_name_with_default, - u"location": text_type(self.vertical.location), + "unit": { + "url": self._get_unit_url(self.course, self.chapter, self.sequential), + "display_name": self.vertical.display_name_with_default, + "location": str(self.vertical.location), }, - u"usage_id": text_type(self.html_module_1.location), - u"updated": "Nov 19, 2014 at 08:05 UTC", + "usage_id": str(self.html_module_1.location), + "updated": "Nov 19, 2014 at 08:05 UTC", }, ] - }, - helpers.get_notes(self.request, self.course) - ) + }) == len(helpers.get_notes(self.request, self.course)) @patch("lms.djangoapps.edxnotes.helpers.requests.get", autospec=True) def test_get_notes_json_error(self, mock_get): @@ -432,22 +429,21 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): "num_pages": 1, "rows": [ { - u"quote": u"quote text", - u"text": u"text", - u"usage_id": text_type(self.html_module_1.location), - u"updated": datetime(2014, 11, 19, 8, 5, 16, 00000).isoformat(), + "quote": "quote text", + "text": "text", + "usage_id": str(self.html_module_1.location), + "updated": datetime(2014, 11, 19, 8, 5, 16, 00000).isoformat(), }, { - u"quote": u"quote text", - u"text": u"text", - u"usage_id": text_type(self.html_module_2.location), - u"updated": datetime(2014, 11, 19, 8, 6, 16, 00000).isoformat(), + "quote": "quote text", + "text": "text", + "usage_id": str(self.html_module_2.location), + "updated": datetime(2014, 11, 19, 8, 6, 16, 00000).isoformat(), } ] }).encode('utf-8') - six.assertCountEqual( - self, + assert len( { "count": 2, "current_page": 1, @@ -457,57 +453,55 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): "num_pages": 1, "results": [ { - u"quote": u"quote text", - u"text": u"text", - u"chapter": { - u"display_name": self.chapter.display_name_with_default, - u"index": 0, - u"location": text_type(self.chapter.location), - u"children": [text_type(self.sequential.location)] + "quote": "quote text", + "text": "text", + "chapter": { + "display_name": self.chapter.display_name_with_default, + "index": 0, + "location": str(self.chapter.location), + "children": [str(self.sequential.location)] }, - u"section": { - u"display_name": self.sequential.display_name_with_default, - u"location": text_type(self.sequential.location), - u"children": [ - text_type(self.vertical.location), - text_type(self.vertical_with_container.location)] + "section": { + "display_name": self.sequential.display_name_with_default, + "location": str(self.sequential.location), + "children": [ + str(self.vertical.location), + str(self.vertical_with_container.location)] }, - u"unit": { - u"url": self._get_unit_url(self.course, self.chapter, self.sequential), - u"display_name": self.vertical.display_name_with_default, - u"location": text_type(self.vertical.location), + "unit": { + "url": self._get_unit_url(self.course, self.chapter, self.sequential), + "display_name": self.vertical.display_name_with_default, + "location": str(self.vertical.location), }, - u"usage_id": text_type(self.html_module_2.location), - u"updated": "Nov 19, 2014 at 08:06 UTC", + "usage_id": str(self.html_module_2.location), + "updated": "Nov 19, 2014 at 08:06 UTC", }, { - u"quote": u"quote text", - u"text": u"text", - u"chapter": { - u"display_name": self.chapter.display_name_with_default, - u"index": 0, - u"location": text_type(self.chapter.location), - u"children": [text_type(self.sequential.location)] + "quote": "quote text", + "text": "text", + "chapter": { + "display_name": self.chapter.display_name_with_default, + "index": 0, + "location": str(self.chapter.location), + "children": [str(self.sequential.location)] }, - u"section": { - u"display_name": self.sequential.display_name_with_default, - u"location": text_type(self.sequential.location), - u"children": [ - text_type(self.vertical.location), - text_type(self.vertical_with_container.location)] + "section": { + "display_name": self.sequential.display_name_with_default, + "location": str(self.sequential.location), + "children": [ + str(self.vertical.location), + str(self.vertical_with_container.location)] }, - u"unit": { - u"url": self._get_unit_url(self.course, self.chapter, self.sequential), - u"display_name": self.vertical.display_name_with_default, - u"location": text_type(self.vertical.location), + "unit": { + "url": self._get_unit_url(self.course, self.chapter, self.sequential), + "display_name": self.vertical.display_name_with_default, + "location": str(self.vertical.location), }, - u"usage_id": text_type(self.html_module_1.location), - u"updated": "Nov 19, 2014 at 08:05 UTC", + "usage_id": str(self.html_module_1.location), + "updated": "Nov 19, 2014 at 08:05 UTC", }, ] - }, - helpers.get_notes(self.request, self.course) - ) + }) == len(helpers.get_notes(self.request, self.course)) @patch("lms.djangoapps.edxnotes.helpers.requests.get", autospec=True) def test_search_json_error(self, mock_get): @@ -531,11 +525,7 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): Tests no results. """ mock_get.return_value.content = json.dumps(NOTES_API_EMPTY_RESPONSE).encode('utf-8') - six.assertCountEqual( - self, - NOTES_VIEW_EMPTY_RESPONSE, - helpers.get_notes(self.request, self.course) - ) + assert len(NOTES_VIEW_EMPTY_RESPONSE) == len(helpers.get_notes(self.request, self.course)) @override_settings(EDXNOTES_PUBLIC_API="http://example.com") @override_settings(EDXNOTES_INTERNAL_API="http://example.com") @@ -566,45 +556,42 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): """ initial_collection = [ { - u"quote": u"quote text", - u"text": u"text", - u"usage_id": text_type(self.html_module_1.location), - u"updated": datetime(2014, 11, 19, 8, 5, 16, 00000).isoformat() + "quote": "quote text", + "text": "text", + "usage_id": str(self.html_module_1.location), + "updated": datetime(2014, 11, 19, 8, 5, 16, 00000).isoformat() }, { - u"quote": u"quote text", - u"text": u"text", - u"usage_id": text_type(self.course.id.make_usage_key("html", "test_item")), - u"updated": datetime(2014, 11, 19, 8, 6, 16, 00000).isoformat() + "quote": "quote text", + "text": "text", + "usage_id": str(self.course.id.make_usage_key("html", "test_item")), + "updated": datetime(2014, 11, 19, 8, 6, 16, 00000).isoformat() }, ] - six.assertCountEqual( - self, + assert len( [{ - u"quote": u"quote text", - u"text": u"text", - u"chapter": { - u"display_name": self.chapter.display_name_with_default, - u"index": 0, - u"location": text_type(self.chapter.location), - u"children": [text_type(self.sequential.location)] + "quote": "quote text", + "text": "text", + "chapter": { + "display_name": self.chapter.display_name_with_default, + "index": 0, + "location": str(self.chapter.location), + "children": [str(self.sequential.location)] }, - u"section": { - u"display_name": self.sequential.display_name_with_default, - u"location": text_type(self.sequential.location), - u"children": [text_type(self.vertical.location), text_type(self.vertical_with_container.location)] + "section": { + "display_name": self.sequential.display_name_with_default, + "location": str(self.sequential.location), + "children": [str(self.vertical.location), str(self.vertical_with_container.location)] }, - u"unit": { - u"url": self._get_unit_url(self.course, self.chapter, self.sequential), - u"display_name": self.vertical.display_name_with_default, - u"location": text_type(self.vertical.location), + "unit": { + "url": self._get_unit_url(self.course, self.chapter, self.sequential), + "display_name": self.vertical.display_name_with_default, + "location": str(self.vertical.location), }, - u"usage_id": text_type(self.html_module_1.location), - u"updated": datetime(2014, 11, 19, 8, 5, 16, 00000), - }], - helpers.preprocess_collection(self.user, self.course, initial_collection) - ) + "usage_id": str(self.html_module_1.location), + "updated": datetime(2014, 11, 19, 8, 5, 16, 00000), + }]) == len(helpers.preprocess_collection(self.user, self.course, initial_collection)) def test_preprocess_collection_has_access(self): """ @@ -612,46 +599,43 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): """ initial_collection = [ { - u"quote": u"quote text", - u"text": u"text", - u"usage_id": text_type(self.html_module_1.location), - u"updated": datetime(2014, 11, 19, 8, 5, 16, 00000).isoformat(), + "quote": "quote text", + "text": "text", + "usage_id": str(self.html_module_1.location), + "updated": datetime(2014, 11, 19, 8, 5, 16, 00000).isoformat(), }, { - u"quote": u"quote text", - u"text": u"text", - u"usage_id": text_type(self.html_module_2.location), - u"updated": datetime(2014, 11, 19, 8, 6, 16, 00000).isoformat(), + "quote": "quote text", + "text": "text", + "usage_id": str(self.html_module_2.location), + "updated": datetime(2014, 11, 19, 8, 6, 16, 00000).isoformat(), }, ] self.html_module_2.visible_to_staff_only = True self.store.update_item(self.html_module_2, self.user.id) - six.assertCountEqual( - self, + assert len( [{ - u"quote": u"quote text", - u"text": u"text", - u"chapter": { - u"display_name": self.chapter.display_name_with_default, - u"index": 0, - u"location": text_type(self.chapter.location), - u"children": [text_type(self.sequential.location)] + "quote": "quote text", + "text": "text", + "chapter": { + "display_name": self.chapter.display_name_with_default, + "index": 0, + "location": str(self.chapter.location), + "children": [str(self.sequential.location)] }, - u"section": { - u"display_name": self.sequential.display_name_with_default, - u"location": text_type(self.sequential.location), - u"children": [text_type(self.vertical.location), text_type(self.vertical_with_container.location)] + "section": { + "display_name": self.sequential.display_name_with_default, + "location": str(self.sequential.location), + "children": [str(self.vertical.location), str(self.vertical_with_container.location)] }, - u"unit": { - u"url": self._get_unit_url(self.course, self.chapter, self.sequential), - u"display_name": self.vertical.display_name_with_default, - u"location": text_type(self.vertical.location), + "unit": { + "url": self._get_unit_url(self.course, self.chapter, self.sequential), + "display_name": self.vertical.display_name_with_default, + "location": str(self.vertical.location), }, - u"usage_id": text_type(self.html_module_1.location), - u"updated": datetime(2014, 11, 19, 8, 5, 16, 00000), - }], - helpers.preprocess_collection(self.user, self.course, initial_collection) - ) + "usage_id": str(self.html_module_1.location), + "updated": datetime(2014, 11, 19, 8, 5, 16, 00000), + }]) == len(helpers.preprocess_collection(self.user, self.course, initial_collection)) @patch("lms.djangoapps.edxnotes.helpers.has_access", autospec=True) @patch("lms.djangoapps.edxnotes.helpers.modulestore", autospec=True) @@ -664,17 +648,13 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): mock_modulestore.return_value = store mock_has_access.return_value = True initial_collection = [{ - u"quote": u"quote text", - u"text": u"text", - u"usage_id": text_type(self.html_module_1.location), - u"updated": datetime(2014, 11, 19, 8, 5, 16, 00000).isoformat(), + "quote": "quote text", + "text": "text", + "usage_id": str(self.html_module_1.location), + "updated": datetime(2014, 11, 19, 8, 5, 16, 00000).isoformat(), }] - six.assertCountEqual( - self, - [], - helpers.preprocess_collection(self.user, self.course, initial_collection) - ) + assert not helpers.preprocess_collection(self.user, self.course, initial_collection) @override_settings(NOTES_DISABLED_TABS=['course_structure', 'tags']) def test_preprocess_collection_with_disabled_tabs(self, ): @@ -683,52 +663,49 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): """ initial_collection = [ { - u"quote": u"quote text1", - u"text": u"text1", - u"usage_id": text_type(self.html_module_1.location), - u"updated": datetime(2016, 1, 26, 8, 5, 16, 00000).isoformat(), + "quote": "quote text1", + "text": "text1", + "usage_id": str(self.html_module_1.location), + "updated": datetime(2016, 1, 26, 8, 5, 16, 00000).isoformat(), }, { - u"quote": u"quote text2", - u"text": u"text2", - u"usage_id": text_type(self.html_module_2.location), - u"updated": datetime(2016, 1, 26, 9, 6, 17, 00000).isoformat(), + "quote": "quote text2", + "text": "text2", + "usage_id": str(self.html_module_2.location), + "updated": datetime(2016, 1, 26, 9, 6, 17, 00000).isoformat(), }, ] - six.assertCountEqual( - self, + assert len( [ { 'section': {}, 'chapter': {}, "unit": { - u"url": self._get_unit_url(self.course, self.chapter, self.sequential), - u"display_name": self.vertical.display_name_with_default, - u"location": text_type(self.vertical.location), + "url": self._get_unit_url(self.course, self.chapter, self.sequential), + "display_name": self.vertical.display_name_with_default, + "location": str(self.vertical.location), }, - u'text': u'text1', - u'quote': u'quote text1', - u'usage_id': text_type(self.html_module_1.location), - u'updated': datetime(2016, 1, 26, 8, 5, 16) + 'text': 'text1', + 'quote': 'quote text1', + 'usage_id': str(self.html_module_1.location), + 'updated': datetime(2016, 1, 26, 8, 5, 16) }, { 'section': {}, 'chapter': {}, "unit": { - u"url": self._get_unit_url(self.course, self.chapter, self.sequential), - u"display_name": self.vertical.display_name_with_default, - u"location": text_type(self.vertical.location), + "url": self._get_unit_url(self.course, self.chapter, self.sequential), + "display_name": self.vertical.display_name_with_default, + "location": str(self.vertical.location), }, - u'text': u'text2', - u'quote': u'quote text2', - u'usage_id': text_type(self.html_module_2.location), - u'updated': datetime(2016, 1, 26, 9, 6, 17) + 'text': 'text2', + 'quote': 'quote text2', + 'usage_id': str(self.html_module_2.location), + 'updated': datetime(2016, 1, 26, 9, 6, 17) } - ], - helpers.preprocess_collection(self.user, self.course, initial_collection) - ) + ]) == len(helpers.preprocess_collection(self.user, self.course, initial_collection)) def test_get_module_context_sequential(self): """ @@ -736,9 +713,9 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): """ self.assertDictEqual( { - u"display_name": self.sequential.display_name_with_default, - u"location": text_type(self.sequential.location), - u"children": [text_type(self.vertical.location), text_type(self.vertical_with_container.location)], + "display_name": self.sequential.display_name_with_default, + "location": str(self.sequential.location), + "children": [str(self.vertical.location), str(self.vertical_with_container.location)], }, helpers.get_module_context(self.course, self.sequential) ) @@ -749,8 +726,8 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): """ self.assertDictEqual( { - u"display_name": self.html_module_1.display_name_with_default, - u"location": text_type(self.html_module_1.location), + "display_name": self.html_module_1.display_name_with_default, + "location": str(self.html_module_1.location), }, helpers.get_module_context(self.course, self.html_module_1) ) @@ -761,19 +738,19 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): """ self.assertDictEqual( { - u"display_name": self.chapter.display_name_with_default, - u"index": 0, - u"location": text_type(self.chapter.location), - u"children": [text_type(self.sequential.location)], + "display_name": self.chapter.display_name_with_default, + "index": 0, + "location": str(self.chapter.location), + "children": [str(self.sequential.location)], }, helpers.get_module_context(self.course, self.chapter) ) self.assertDictEqual( { - u"display_name": self.chapter_2.display_name_with_default, - u"index": 1, - u"location": text_type(self.chapter_2.location), - u"children": [], + "display_name": self.chapter_2.display_name_with_default, + "index": 1, + "location": str(self.chapter_2.location), + "children": [], }, helpers.get_module_context(self.course, self.chapter_2) ) @@ -804,7 +781,7 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): }, params={ "user": "anonymous_id", - "course_id": text_type(self.course.id), + "course_id": str(self.course.id), "text": "text", "highlight": True, 'page': 1, @@ -834,7 +811,7 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): }, params={ "user": "anonymous_id", - "course_id": text_type(self.course.id), + "course_id": str(self.course.id), 'page': helpers.DEFAULT_PAGE, 'page_size': helpers.DEFAULT_PAGE_SIZE, }, @@ -865,7 +842,7 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): assert helpers.get_course_position(mock_course_module) == { 'display_name': 'Test Chapter Display Name', - 'url': '/courses/{}/courseware/chapter_url_name/'.format(self.course.id) + 'url': f'/courses/{self.course.id}/courseware/chapter_url_name/', } def test_get_course_position_no_section(self): @@ -896,7 +873,7 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): assert helpers.get_course_position(mock_course_module) == { 'display_name': 'Test Section Display Name', - 'url': '/courses/{}/courseware/chapter_url_name/section_url_name/'.format(self.course.id) + 'url': f'/courses/{self.course.id}/courseware/chapter_url_name/section_url_name/', } def test_get_index(self): @@ -904,8 +881,8 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): Tests `get_index` method returns unit url. """ children = self.sequential.children - assert 0 == helpers.get_index(text_type(self.vertical.location), children) - assert 1 == helpers.get_index(text_type(self.vertical_with_container.location), children) + assert 0 == helpers.get_index(str(self.vertical.location), children) + assert 1 == helpers.get_index(str(self.vertical_with_container.location), children) @ddt.unpack @ddt.data( @@ -930,7 +907,7 @@ class EdxNotesHelpersTest(ModuleStoreTestCase): host = 'https://' + self.request.get_host() else: host = 'http://' + self.request.get_host() - notes_url = host + reverse("notes", args=[text_type(self.course.id)]) + notes_url = host + reverse("notes", args=[str(self.course.id)]) def verify_url(constructed, expected): """ @@ -975,15 +952,15 @@ class EdxNotesViewsTest(ModuleStoreTestCase): """ def setUp(self): ApplicationFactory(name="edx-notes") - super(EdxNotesViewsTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.course = CourseFactory(edxnotes=True) self.user = UserFactory() CourseEnrollmentFactory(user=self.user, course_id=self.course.id) # lint-amnesty, pylint: disable=no-member self.client.login(username=self.user.username, password=UserFactory._DEFAULT_PASSWORD) # lint-amnesty, pylint: disable=protected-access - self.notes_page_url = reverse("edxnotes", args=[text_type(self.course.id)]) # lint-amnesty, pylint: disable=no-member - self.notes_url = reverse("notes", args=[text_type(self.course.id)]) # lint-amnesty, pylint: disable=no-member - self.get_token_url = reverse("get_token", args=[text_type(self.course.id)]) # lint-amnesty, pylint: disable=no-member - self.visibility_url = reverse("edxnotes_visibility", args=[text_type(self.course.id)]) # lint-amnesty, pylint: disable=no-member + self.notes_page_url = reverse("edxnotes", args=[str(self.course.id)]) # lint-amnesty, pylint: disable=no-member + self.notes_url = reverse("notes", args=[str(self.course.id)]) # lint-amnesty, pylint: disable=no-member + self.get_token_url = reverse("get_token", args=[str(self.course.id)]) # lint-amnesty, pylint: disable=no-member + self.visibility_url = reverse("edxnotes_visibility", args=[str(self.course.id)]) # lint-amnesty, pylint: disable=no-member def _get_course_module(self): """ @@ -1167,7 +1144,7 @@ class EdxNotesRetireAPITest(ModuleStoreTestCase): """ def setUp(self): ApplicationFactory(name="edx-notes") - super(EdxNotesRetireAPITest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() # setup relevant states RetirementState.objects.create(state_name='PENDING', state_execution_order=1) @@ -1282,7 +1259,7 @@ class EdxNotesPluginTest(ModuleStoreTestCase): """ def setUp(self): - super(EdxNotesPluginTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.course = CourseFactory.create(edxnotes=True) self.user = UserFactory() CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id) diff --git a/lms/djangoapps/edxnotes/views.py b/lms/djangoapps/edxnotes/views.py index 1ba86db6df..0edb6b1207 100644 --- a/lms/djangoapps/edxnotes/views.py +++ b/lms/djangoapps/edxnotes/views.py @@ -16,12 +16,12 @@ from opaque_keys.edx.keys import CourseKey from rest_framework import permissions, status from rest_framework.response import Response from rest_framework.views import APIView -from six import text_type +from common.djangoapps.edxmako.shortcuts import render_to_response +from common.djangoapps.util.json_request import JsonResponse, JsonResponseBadRequest from lms.djangoapps.courseware.courses import get_course_with_access from lms.djangoapps.courseware.model_data import FieldDataCache from lms.djangoapps.courseware.module_render import get_module_for_descriptor -from common.djangoapps.edxmako.shortcuts import render_to_response from lms.djangoapps.edxnotes.exceptions import EdxNotesParseError, EdxNotesServiceUnavailable from lms.djangoapps.edxnotes.helpers import ( DEFAULT_PAGE, @@ -35,7 +35,6 @@ from lms.djangoapps.edxnotes.helpers import ( ) from openedx.core.djangoapps.user_api.accounts.permissions import CanRetireUser from openedx.core.djangoapps.user_api.models import RetirementStateError, UserRetirementStatus -from common.djangoapps.util.json_request import JsonResponse, JsonResponseBadRequest log = logging.getLogger(__name__) @@ -175,7 +174,7 @@ def notes(request, course_id): text=text ) except (EdxNotesParseError, EdxNotesServiceUnavailable) as err: - return JsonResponseBadRequest({"error": text_type(err)}, status=500) + return JsonResponseBadRequest({"error": str(err)}, status=500) return HttpResponse(json.dumps(notes_info, cls=NoteJSONEncoder), content_type="application/json") # lint-amnesty, pylint: disable=http-response-with-content-type-json, http-response-with-json-dumps @@ -210,7 +209,7 @@ def edxnotes_visibility(request, course_id): return JsonResponse(status=200) except (ValueError, KeyError): log.warning( - u"Could not decode request body as JSON and find a boolean visibility field: '%s'", request.body + "Could not decode request body as JSON and find a boolean visibility field: '%s'", request.body ) return JsonResponseBadRequest() @@ -259,8 +258,8 @@ class RetireUserView(APIView): except UserRetirementStatus.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) except RetirementStateError as exc: - return Response(text_type(exc), status=status.HTTP_405_METHOD_NOT_ALLOWED) + return Response(str(exc), status=status.HTTP_405_METHOD_NOT_ALLOWED) except Exception as exc: # pylint: disable=broad-except - return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response(str(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR) return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/lms/djangoapps/email_marketing/migrations/0001_initial.py b/lms/djangoapps/email_marketing/migrations/0001_initial.py index e5f0a24476..0264296e47 100644 --- a/lms/djangoapps/email_marketing/migrations/0001_initial.py +++ b/lms/djangoapps/email_marketing/migrations/0001_initial.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - import django.db.models.deletion from django.conf import settings from django.db import migrations, models diff --git a/lms/djangoapps/email_marketing/migrations/0002_auto_20160623_1656.py b/lms/djangoapps/email_marketing/migrations/0002_auto_20160623_1656.py index 73df493754..049b6b3a29 100644 --- a/lms/djangoapps/email_marketing/migrations/0002_auto_20160623_1656.py +++ b/lms/djangoapps/email_marketing/migrations/0002_auto_20160623_1656.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - from django.db import migrations, models diff --git a/lms/djangoapps/email_marketing/migrations/0003_auto_20160715_1145.py b/lms/djangoapps/email_marketing/migrations/0003_auto_20160715_1145.py index 40b727bef9..f0840a4d4c 100644 --- a/lms/djangoapps/email_marketing/migrations/0003_auto_20160715_1145.py +++ b/lms/djangoapps/email_marketing/migrations/0003_auto_20160715_1145.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - from django.db import migrations, models diff --git a/lms/djangoapps/email_marketing/migrations/0004_emailmarketingconfiguration_welcome_email_send_delay.py b/lms/djangoapps/email_marketing/migrations/0004_emailmarketingconfiguration_welcome_email_send_delay.py index f603b7297d..d6cc8334fc 100644 --- a/lms/djangoapps/email_marketing/migrations/0004_emailmarketingconfiguration_welcome_email_send_delay.py +++ b/lms/djangoapps/email_marketing/migrations/0004_emailmarketingconfiguration_welcome_email_send_delay.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - from django.db import migrations, models diff --git a/lms/djangoapps/email_marketing/migrations/0005_emailmarketingconfiguration_user_registration_cookie_timeout_delay.py b/lms/djangoapps/email_marketing/migrations/0005_emailmarketingconfiguration_user_registration_cookie_timeout_delay.py index 3a1d82dfc8..bd71d3a3e5 100644 --- a/lms/djangoapps/email_marketing/migrations/0005_emailmarketingconfiguration_user_registration_cookie_timeout_delay.py +++ b/lms/djangoapps/email_marketing/migrations/0005_emailmarketingconfiguration_user_registration_cookie_timeout_delay.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - from django.db import migrations, models diff --git a/lms/djangoapps/email_marketing/migrations/0006_auto_20170711_0615.py b/lms/djangoapps/email_marketing/migrations/0006_auto_20170711_0615.py index 7ed6b3d45f..56c4a06d43 100644 --- a/lms/djangoapps/email_marketing/migrations/0006_auto_20170711_0615.py +++ b/lms/djangoapps/email_marketing/migrations/0006_auto_20170711_0615.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - from django.db import migrations, models diff --git a/lms/djangoapps/email_marketing/migrations/0007_auto_20170809_0653.py b/lms/djangoapps/email_marketing/migrations/0007_auto_20170809_0653.py index a417fe4f27..f0e08c0576 100644 --- a/lms/djangoapps/email_marketing/migrations/0007_auto_20170809_0653.py +++ b/lms/djangoapps/email_marketing/migrations/0007_auto_20170809_0653.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - from django.db import migrations, models diff --git a/lms/djangoapps/email_marketing/migrations/0008_auto_20170809_0539.py b/lms/djangoapps/email_marketing/migrations/0008_auto_20170809_0539.py index 69a0bff8fa..cf30a0db28 100644 --- a/lms/djangoapps/email_marketing/migrations/0008_auto_20170809_0539.py +++ b/lms/djangoapps/email_marketing/migrations/0008_auto_20170809_0539.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - from django.db import migrations, models diff --git a/lms/djangoapps/email_marketing/migrations/0009_remove_emailmarketingconfiguration_sailthru_activation_template.py b/lms/djangoapps/email_marketing/migrations/0009_remove_emailmarketingconfiguration_sailthru_activation_template.py index f5abbb5352..9408cf4c17 100644 --- a/lms/djangoapps/email_marketing/migrations/0009_remove_emailmarketingconfiguration_sailthru_activation_template.py +++ b/lms/djangoapps/email_marketing/migrations/0009_remove_emailmarketingconfiguration_sailthru_activation_template.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - from django.db import migrations, models diff --git a/lms/djangoapps/email_marketing/migrations/0010_auto_20180425_0800.py b/lms/djangoapps/email_marketing/migrations/0010_auto_20180425_0800.py index 27f2c9ab39..64dca9977e 100644 --- a/lms/djangoapps/email_marketing/migrations/0010_auto_20180425_0800.py +++ b/lms/djangoapps/email_marketing/migrations/0010_auto_20180425_0800.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.12 on 2018-04-25 12:00 diff --git a/lms/djangoapps/email_marketing/models.py b/lms/djangoapps/email_marketing/models.py index de217c2930..ee82309f84 100644 --- a/lms/djangoapps/email_marketing/models.py +++ b/lms/djangoapps/email_marketing/models.py @@ -17,7 +17,7 @@ class EmailMarketingConfiguration(ConfigurationModel): .. no_pii: """ - class Meta(object): + class Meta: app_label = "email_marketing" sailthru_key = models.fields.CharField( @@ -169,5 +169,5 @@ class EmailMarketingConfiguration(ConfigurationModel): ) def __str__(self): - return u"Email marketing configuration: New user list %s, Welcome template: %s" % \ + return "Email marketing configuration: New user list %s, Welcome template: %s" % \ (self.sailthru_new_user_list, self.sailthru_welcome_template) diff --git a/lms/djangoapps/email_marketing/signals.py b/lms/djangoapps/email_marketing/signals.py index caca80af7c..0a3dc7a2c1 100644 --- a/lms/djangoapps/email_marketing/signals.py +++ b/lms/djangoapps/email_marketing/signals.py @@ -6,18 +6,22 @@ This module contains signals needed for email integration import datetime import logging from random import randint -from typing import Dict, Any, Optional, Tuple +from typing import Any, Dict, Optional, Tuple import crum from celery.exceptions import TimeoutError as CeleryTimeoutError from django.conf import settings from django.dispatch import receiver +from edx_toggles.toggles import LegacyWaffleSwitchNamespace from sailthru.sailthru_error import SailthruClientError -from six import text_type from common.djangoapps import third_party_auth from common.djangoapps.course_modes.models import CourseMode -from edx_toggles.toggles import LegacyWaffleSwitchNamespace # lint-amnesty, pylint: disable=wrong-import-order +from common.djangoapps.student.helpers import does_user_profile_exist +from common.djangoapps.student.models import UserProfile +from common.djangoapps.student.signals import SAILTHRU_AUDIT_PURCHASE +from common.djangoapps.track import segment +from common.djangoapps.util.model_utils import USER_FIELD_CHANGED, USER_FIELDS_CHANGED from lms.djangoapps.email_marketing.tasks import ( get_email_cookies_via_sailthru, update_course_enrollment, @@ -27,11 +31,6 @@ from lms.djangoapps.email_marketing.tasks import ( from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY from openedx.core.djangoapps.user_authn.cookies import CREATE_LOGON_COOKIE from openedx.core.djangoapps.user_authn.views.register import REGISTER_USER -from common.djangoapps.student.helpers import does_user_profile_exist -from common.djangoapps.student.signals import SAILTHRU_AUDIT_PURCHASE -from common.djangoapps.student.models import UserProfile -from common.djangoapps.track import segment -from common.djangoapps.util.model_utils import USER_FIELD_CHANGED, USER_FIELDS_CHANGED from .models import EmailMarketingConfiguration @@ -109,17 +108,17 @@ def add_email_marketing_cookies(sender, response=None, user=None, _log_sailthru_api_call_time(time_before_call) except CeleryTimeoutError as exc: - log.error(u"Timeout error while attempting to obtain cookie from Sailthru: %s", text_type(exc)) + log.error("Timeout error while attempting to obtain cookie from Sailthru: %s", str(exc)) return response except SailthruClientError as exc: - log.error(u"Exception attempting to obtain cookie from Sailthru: %s", text_type(exc)) + log.error("Exception attempting to obtain cookie from Sailthru: %s", str(exc)) return response except Exception: # lint-amnesty, pylint: disable=broad-except - log.error(u"Exception Connecting to celery task for %s", user.email) + log.error("Exception Connecting to celery task for %s", user.email) return response if not cookie: - log.error(u"No cookie returned attempting to obtain cookie from Sailthru for %s", user.email) + log.error("No cookie returned attempting to obtain cookie from Sailthru for %s", user.email) return response else: response.set_cookie( @@ -130,7 +129,7 @@ def add_email_marketing_cookies(sender, response=None, user=None, path='/', secure=request.is_secure() ) - log.info(u"sailthru_hid cookie:%s successfully retrieved for user %s", cookie, user.email) + log.info("sailthru_hid cookie:%s successfully retrieved for user %s", cookie, user.email) return response @@ -287,7 +286,7 @@ def _create_sailthru_user_vars(user, profile, registration=None): if profile.year_of_birth: sailthru_vars['year_of_birth'] = profile.year_of_birth - sailthru_vars['country'] = text_type(profile.country.code) + sailthru_vars['country'] = str(profile.country.code) if registration: sailthru_vars['activation_key'] = registration.activation_key @@ -315,7 +314,7 @@ def _log_sailthru_api_call_time(time_before_call): time_after_call = datetime.datetime.now() delta_sailthru_api_call_time = time_after_call - time_before_call - log.info(u"Started at %s and ended at %s, time spent:%s milliseconds", + log.info("Started at %s and ended at %s, time spent:%s milliseconds", time_before_call.isoformat(' '), time_after_call.isoformat(' '), delta_sailthru_api_call_time.microseconds / 1000) diff --git a/lms/djangoapps/email_marketing/tasks.py b/lms/djangoapps/email_marketing/tasks.py index 7ce99d09e8..29ee7bc76d 100644 --- a/lms/djangoapps/email_marketing/tasks.py +++ b/lms/djangoapps/email_marketing/tasks.py @@ -7,7 +7,6 @@ import logging import time from datetime import datetime, timedelta -import six from celery import shared_task from django.conf import settings from django.core.cache import cache @@ -40,13 +39,13 @@ def get_email_cookies_via_sailthru(self, user_email, post_parms): # lint-amnest try: sailthru_client = SailthruClient(email_config.sailthru_key, email_config.sailthru_secret) log.info( - u'Sending to Sailthru the user interest cookie [%s] for user [%s]', + 'Sending to Sailthru the user interest cookie [%s] for user [%s]', post_parms.get('cookies', ''), user_email ) sailthru_response = sailthru_client.api_post("user", post_parms) except SailthruClientError as exc: - log.error(u"Exception attempting to obtain cookie from Sailthru: %s", six.text_type(exc)) + log.error("Exception attempting to obtain cookie from Sailthru: %s", str(exc)) raise SailthruClientError # lint-amnesty, pylint: disable=raise-missing-from if sailthru_response.is_ok(): @@ -54,11 +53,11 @@ def get_email_cookies_via_sailthru(self, user_email, post_parms): # lint-amnest cookie = sailthru_response.json['keys']['cookie'] return cookie else: - log.error(u"No cookie returned attempting to obtain cookie from Sailthru for %s", user_email) + log.error("No cookie returned attempting to obtain cookie from Sailthru for %s", user_email) else: error = sailthru_response.get_error() # generally invalid email address - log.info(u"Error attempting to obtain cookie from Sailthru: %s", error.get_message()) + log.info("Error attempting to obtain cookie from Sailthru: %s", error.get_message()) return None @@ -93,14 +92,14 @@ def update_user(self, sailthru_vars, email, site=None, new_user=False, activatio site=site)) except SailthruClientError as exc: - log.error(u"Exception attempting to add/update user %s in Sailthru - %s", email, six.text_type(exc)) + log.error("Exception attempting to add/update user %s in Sailthru - %s", email, str(exc)) raise self.retry(exc=exc, countdown=email_config.sailthru_retry_interval, max_retries=email_config.sailthru_max_retries) if not sailthru_response.is_ok(): error = sailthru_response.get_error() - log.error(u"Error attempting to add/update user in Sailthru: %s", error.get_message()) + log.error("Error attempting to add/update user in Sailthru: %s", error.get_message()) if _retryable_sailthru_error(error): raise self.retry(countdown=email_config.sailthru_retry_interval, max_retries=email_config.sailthru_max_retries) @@ -120,9 +119,9 @@ def update_user(self, sailthru_vars, email, site=None, new_user=False, activatio ) except SailthruClientError as exc: log.error( - u"Exception attempting to send welcome email to user %s in Sailthru - %s", + "Exception attempting to send welcome email to user %s in Sailthru - %s", email, - six.text_type(exc) + str(exc) ) raise self.retry(exc=exc, countdown=email_config.sailthru_retry_interval, @@ -130,7 +129,7 @@ def update_user(self, sailthru_vars, email, site=None, new_user=False, activatio if not sailthru_response.is_ok(): error = sailthru_response.get_error() - log.error(u"Error attempting to send welcome email to user in Sailthru: %s", error.get_message()) + log.error("Error attempting to send welcome email to user in Sailthru: %s", error.get_message()) if _retryable_sailthru_error(error): raise self.retry(countdown=email_config.sailthru_retry_interval, max_retries=email_config.sailthru_max_retries) @@ -173,14 +172,14 @@ def update_user_email(self, new_email, old_email): sailthru_client = SailthruClient(email_config.sailthru_key, email_config.sailthru_secret) sailthru_response = sailthru_client.api_post("user", sailthru_parms) except SailthruClientError as exc: - log.error(u"Exception attempting to update email for %s in Sailthru - %s", old_email, six.text_type(exc)) + log.error("Exception attempting to update email for %s in Sailthru - %s", old_email, str(exc)) raise self.retry(exc=exc, countdown=email_config.sailthru_retry_interval, max_retries=email_config.sailthru_max_retries) if not sailthru_response.is_ok(): error = sailthru_response.get_error() - log.error(u"Error attempting to update user email address in Sailthru: %s", error.get_message()) + log.error("Error attempting to update user email address in Sailthru: %s", error.get_message()) if _retryable_sailthru_error(error): raise self.retry(countdown=email_config.sailthru_retry_interval, max_retries=email_config.sailthru_max_retries) @@ -262,12 +261,12 @@ def _get_list_from_email_marketing_provider(sailthru_client): try: sailthru_get_response = sailthru_client.api_get("list", {}) except SailthruClientError as exc: - log.error(u"Exception attempting to get list from Sailthru - %s", six.text_type(exc)) + log.error("Exception attempting to get list from Sailthru - %s", str(exc)) return {} if not sailthru_get_response.is_ok(): error = sailthru_get_response.get_error() - log.info(u"Error attempting to read list record from Sailthru: %s", error.get_message()) + log.info("Error attempting to read list record from Sailthru: %s", error.get_message()) return {} list_map = dict() @@ -288,12 +287,12 @@ def _create_user_list(sailthru_client, list_name): try: sailthru_response = sailthru_client.api_post("list", list_params) except SailthruClientError as exc: - log.error(u"Exception attempting to list record for key %s in Sailthru - %s", list_name, six.text_type(exc)) + log.error("Exception attempting to list record for key %s in Sailthru - %s", list_name, str(exc)) return False if not sailthru_response.is_ok(): error = sailthru_response.get_error() - log.error(u"Error attempting to create list in Sailthru: %s", error.get_message()) + log.error("Error attempting to create list in Sailthru: %s", error.get_message()) return False return True @@ -365,7 +364,7 @@ def build_course_url(course_key): a complete url of the course info page """ return '{base_url}/courses/{course_key}/info'.format(base_url=settings.LMS_ROOT_URL, - course_key=six.text_type(course_key)) + course_key=str(course_key)) # TODO: Remove in AA-607 @@ -384,7 +383,7 @@ def update_unenrolled_list(sailthru_client, email, course_url, unenroll): sailthru_response = sailthru_client.api_get("user", {"id": email, "fields": {"vars": 1}}) if not sailthru_response.is_ok(): error = sailthru_response.get_error() - log.error(u"Error attempting to read user record from Sailthru: %s", error.get_message()) + log.error("Error attempting to read user record from Sailthru: %s", error.get_message()) return not _retryable_sailthru_error(error) response_json = sailthru_response.json @@ -413,13 +412,13 @@ def update_unenrolled_list(sailthru_client, email, course_url, unenroll): if not sailthru_response.is_ok(): error = sailthru_response.get_error() - log.error(u"Error attempting to update user record in Sailthru: %s", error.get_message()) + log.error("Error attempting to update user record in Sailthru: %s", error.get_message()) return not _retryable_sailthru_error(error) return True except SailthruClientError as exc: - log.exception(u"Exception attempting to update user record for %s in Sailthru - %s", email, six.text_type(exc)) + log.exception("Exception attempting to update user record for %s in Sailthru - %s", email, str(exc)) return False @@ -444,7 +443,7 @@ def _get_course_content(course_id, course_url, sailthru_client, config): """ # check cache first - cache_key = "{}:{}".format(course_id, course_url) + cache_key = f"{course_id}:{course_url}" response = cache.get(cache_key) if not response: try: @@ -468,7 +467,7 @@ def _build_purchase_item(course_id, course_url, cost_in_cents, mode, course_data # build item description item = { - 'id': "{}-{}".format(course_id, mode), + 'id': f"{course_id}-{mode}", 'url': course_url, 'price': cost_in_cents, 'qty': 1, @@ -479,13 +478,13 @@ def _build_purchase_item(course_id, course_url, cost_in_cents, mode, course_data item['title'] = course_data['title'] else: # can't find, just invent title - item['title'] = u'Course {} mode: {}'.format(course_id, mode) + item['title'] = f'Course {course_id} mode: {mode}' if 'tags' in course_data: item['tags'] = course_data['tags'] # add vars to item - item['vars'] = dict(course_data.get('vars', {}), mode=mode, course_run_id=six.text_type(course_id)) + item['vars'] = dict(course_data.get('vars', {}), mode=mode, course_run_id=str(course_id)) return item @@ -508,10 +507,10 @@ def _record_purchase(sailthru_client, email, item, options): if not sailthru_response.is_ok(): error = sailthru_response.get_error() - log.error(u"Error attempting to record purchase in Sailthru: %s", error.get_message()) + log.error("Error attempting to record purchase in Sailthru: %s", error.get_message()) return not _retryable_sailthru_error(error) except SailthruClientError as exc: - log.exception(u"Exception attempting to record purchase for %s in Sailthru - %s", email, six.text_type(exc)) + log.exception("Exception attempting to record purchase for %s in Sailthru - %s", email, str(exc)) return False return True diff --git a/lms/djangoapps/email_marketing/tests/test_signals.py b/lms/djangoapps/email_marketing/tests/test_signals.py index 505759ffbe..aef30e561a 100644 --- a/lms/djangoapps/email_marketing/tests/test_signals.py +++ b/lms/djangoapps/email_marketing/tests/test_signals.py @@ -1,25 +1,29 @@ -# -*- coding: utf-8 -*- - """Tests of email marketing signal handlers.""" import datetime import logging +from unittest.mock import ANY, Mock, patch import ddt -import six from django.conf import settings from django.contrib.auth.models import AnonymousUser from django.contrib.sites.models import Site from django.test import TestCase from django.test.client import RequestFactory from freezegun import freeze_time -from mock import ANY, Mock, patch from opaque_keys.edx.keys import CourseKey from sailthru.sailthru_error import SailthruClientError from sailthru.sailthru_response import SailthruResponse from testfixtures import LogCapture +from common.djangoapps.student.models import Registration, User +from common.djangoapps.student.tests.factories import ( # lint-amnesty, pylint: disable=unused-import + CourseEnrollmentFactory, + UserFactory, + UserProfileFactory +) +from common.djangoapps.util.json_request import JsonResponse from lms.djangoapps.email_marketing.tasks import ( # lint-amnesty, pylint: disable=unused-import _create_user_list, _get_list_from_email_marketing_provider, @@ -30,9 +34,6 @@ from lms.djangoapps.email_marketing.tasks import ( # lint-amnesty, pylint: disa update_user_email ) from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY -from common.djangoapps.student.models import Registration, User -from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory, UserProfileFactory # lint-amnesty, pylint: disable=unused-import -from common.djangoapps.util.json_request import JsonResponse from ..models import EmailMarketingConfiguration from ..signals import ( @@ -92,7 +93,7 @@ class EmailMarketingTests(TestCase): self.site = Site.objects.get_current() self.request.site = self.site - super(EmailMarketingTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() @freeze_time(datetime.datetime.now()) @patch('lms.djangoapps.email_marketing.signals.crum.get_current_request') @@ -118,7 +119,7 @@ class EmailMarketingTests(TestCase): 'Started at {start} and ended at {end}, time spent:{delta} milliseconds'.format( start=datetime.datetime.now().isoformat(' '), end=datetime.datetime.now().isoformat(' '), - delta=0 if six.PY2 else 0.0) + delta=0.0) ), (LOGGER_NAME, 'INFO', 'sailthru_hid cookie:{cookies[cookie]} successfully retrieved for user {user}'.format( @@ -561,7 +562,7 @@ class EmailMarketingTests(TestCase): assert not mock_update_user.called -class MockSailthruResponse(object): +class MockSailthruResponse: """ Mock object for SailthruResponse """ @@ -584,7 +585,7 @@ class MockSailthruResponse(object): return MockSailthruError(self.error, self.code) -class MockSailthruError(object): +class MockSailthruError: """ Mock object for Sailthru Error """ @@ -612,7 +613,7 @@ class SailthruTests(TestCase): """ def setUp(self): - super(SailthruTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory() self.course_id = CourseKey.from_string('edX/toy/2012_Fall') self.course_url = 'http://lms.testserver.fake/courses/edX/toy/2012_Fall/info' @@ -664,6 +665,6 @@ class SailthruTests(TestCase): while sending it to sail through. """ switch.return_value = True - self.user.email = u'tèst@edx.org' + self.user.email = 'tèst@edx.org' update_sailthru(None, self.user, 'audit', str(self.course_id)) assert mock_sailthru_purchase.called diff --git a/lms/djangoapps/experiments/apps.py b/lms/djangoapps/experiments/apps.py index 1c73cb3a92..8289af649e 100644 --- a/lms/djangoapps/experiments/apps.py +++ b/lms/djangoapps/experiments/apps.py @@ -6,4 +6,4 @@ class ExperimentsConfig(AppConfig): """ Application Configuration for experiments. """ - name = u'lms.djangoapps.experiments' + name = 'lms.djangoapps.experiments' diff --git a/lms/djangoapps/experiments/factories.py b/lms/djangoapps/experiments/factories.py index b18749ac68..fa0d5d3398 100644 --- a/lms/djangoapps/experiments/factories.py +++ b/lms/djangoapps/experiments/factories.py @@ -6,12 +6,12 @@ Experimentation factories import factory import factory.fuzzy -from lms.djangoapps.experiments.models import ExperimentData, ExperimentKeyValue from common.djangoapps.student.tests.factories import UserFactory +from lms.djangoapps.experiments.models import ExperimentData, ExperimentKeyValue class ExperimentDataFactory(factory.DjangoModelFactory): # lint-amnesty, pylint: disable=missing-class-docstring - class Meta(object): + class Meta: model = ExperimentData user = factory.SubFactory(UserFactory) @@ -21,7 +21,7 @@ class ExperimentDataFactory(factory.DjangoModelFactory): # lint-amnesty, pylint class ExperimentKeyValueFactory(factory.DjangoModelFactory): # lint-amnesty, pylint: disable=missing-class-docstring - class Meta(object): + class Meta: model = ExperimentKeyValue experiment_id = factory.fuzzy.FuzzyInteger(0) diff --git a/lms/djangoapps/experiments/filters.py b/lms/djangoapps/experiments/filters.py index 67fc6c4dd8..4fb38c1756 100644 --- a/lms/djangoapps/experiments/filters.py +++ b/lms/djangoapps/experiments/filters.py @@ -9,12 +9,12 @@ from lms.djangoapps.experiments.models import ExperimentData, ExperimentKeyValue class ExperimentDataFilter(django_filters.FilterSet): - class Meta(object): + class Meta: model = ExperimentData fields = ['experiment_id', 'key', ] class ExperimentKeyValueFilter(django_filters.FilterSet): - class Meta(object): + class Meta: model = ExperimentKeyValue fields = ['experiment_id', 'key', ] diff --git a/lms/djangoapps/experiments/flags.py b/lms/djangoapps/experiments/flags.py index 9af33fd93f..d824a31d9b 100644 --- a/lms/djangoapps/experiments/flags.py +++ b/lms/djangoapps/experiments/flags.py @@ -10,9 +10,9 @@ import pytz from crum import get_current_request from edx_django_utils.cache import RequestCache +from common.djangoapps.track import segment from lms.djangoapps.experiments.stable_bucketing import stable_bucketing_hash_group from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag -from common.djangoapps.track import segment log = logging.getLogger(__name__) @@ -76,7 +76,7 @@ class ExperimentWaffleFlag(CourseWaffleFlag): self.num_buckets = num_buckets self.experiment_id = experiment_id self.bucket_flags = [ - CourseWaffleFlag(waffle_namespace, '{}.{}'.format(flag_name, bucket), module_name) + CourseWaffleFlag(waffle_namespace, f'{flag_name}.{bucket}', module_name) for bucket in range(num_buckets) ] self.use_course_aware_bucketing = use_course_aware_bucketing @@ -205,9 +205,9 @@ class ExperimentWaffleFlag(CourseWaffleFlag): # buckets for different course-runs. experiment_name = bucketing_group_name = self.name if course_key: - experiment_name += ".{}".format(course_key) + experiment_name += f".{course_key}" if course_key and self.use_course_aware_bucketing: - bucketing_group_name += ".{}".format(course_key) + bucketing_group_name += f".{course_key}" # Check if we have a cache for this request already request_cache = RequestCache('experiments') @@ -239,7 +239,7 @@ class ExperimentWaffleFlag(CourseWaffleFlag): bucketing_group_name, self.num_buckets, user ) - session_key = 'tracked.{}'.format(experiment_name) + session_key = f'tracked.{experiment_name}' anonymous = not hasattr(request, 'user') or not request.user.id if ( track and hasattr(request, 'session') and diff --git a/lms/djangoapps/experiments/migrations/0001_initial.py b/lms/djangoapps/experiments/migrations/0001_initial.py index 5529da6d4b..63660c9884 100644 --- a/lms/djangoapps/experiments/migrations/0001_initial.py +++ b/lms/djangoapps/experiments/migrations/0001_initial.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- - - - from django.db import migrations, models from django.conf import settings import django.utils.timezone @@ -21,7 +17,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)), ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)), - ('experiment_id', models.PositiveSmallIntegerField(verbose_name=u'Experiment ID', db_index=True)), + ('experiment_id', models.PositiveSmallIntegerField(verbose_name='Experiment ID', db_index=True)), ('key', models.CharField(max_length=255)), ('value', models.TextField()), ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), @@ -33,10 +29,10 @@ class Migration(migrations.Migration): ), migrations.AlterUniqueTogether( name='experimentdata', - unique_together=set([('user', 'experiment_id', 'key')]), + unique_together={('user', 'experiment_id', 'key')}, ), migrations.AlterIndexTogether( name='experimentdata', - index_together=set([('user', 'experiment_id')]), + index_together={('user', 'experiment_id')}, ), ] diff --git a/lms/djangoapps/experiments/migrations/0002_auto_20170627_1402.py b/lms/djangoapps/experiments/migrations/0002_auto_20170627_1402.py index f5b4271682..6742df419e 100644 --- a/lms/djangoapps/experiments/migrations/0002_auto_20170627_1402.py +++ b/lms/djangoapps/experiments/migrations/0002_auto_20170627_1402.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- - - - from django.db import migrations, models import django.utils.timezone import model_utils.fields @@ -20,7 +16,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)), ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)), - ('experiment_id', models.PositiveSmallIntegerField(verbose_name=u'Experiment ID', db_index=True)), + ('experiment_id', models.PositiveSmallIntegerField(verbose_name='Experiment ID', db_index=True)), ('key', models.CharField(max_length=255)), ('value', models.TextField()), ], @@ -31,6 +27,6 @@ class Migration(migrations.Migration): ), migrations.AlterUniqueTogether( name='experimentkeyvalue', - unique_together=set([('experiment_id', 'key')]), + unique_together={('experiment_id', 'key')}, ), ] diff --git a/lms/djangoapps/experiments/migrations/0003_auto_20170713_1148.py b/lms/djangoapps/experiments/migrations/0003_auto_20170713_1148.py index c9a89a6e06..1d2fea1a69 100644 --- a/lms/djangoapps/experiments/migrations/0003_auto_20170713_1148.py +++ b/lms/djangoapps/experiments/migrations/0003_auto_20170713_1148.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- - - - from django.db import migrations, models diff --git a/lms/djangoapps/experiments/migrations/0004_historicalexperimentkeyvalue.py b/lms/djangoapps/experiments/migrations/0004_historicalexperimentkeyvalue.py index fafb354671..6cf739958e 100644 --- a/lms/djangoapps/experiments/migrations/0004_historicalexperimentkeyvalue.py +++ b/lms/djangoapps/experiments/migrations/0004_historicalexperimentkeyvalue.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.28 on 2020-03-03 16:26 diff --git a/lms/djangoapps/experiments/models.py b/lms/djangoapps/experiments/models.py index 503d6f8e71..2e69d185d3 100644 --- a/lms/djangoapps/experiments/models.py +++ b/lms/djangoapps/experiments/models.py @@ -17,12 +17,12 @@ class ExperimentData(TimeStampedModel): """ user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) experiment_id = models.PositiveSmallIntegerField( - null=False, blank=False, db_index=True, verbose_name=u'Experiment ID' + null=False, blank=False, db_index=True, verbose_name='Experiment ID' ) key = models.CharField(null=False, blank=False, max_length=255) value = models.TextField() - class Meta(object): + class Meta: index_together = ( ('user', 'experiment_id'), ) @@ -40,14 +40,14 @@ class ExperimentKeyValue(TimeStampedModel): .. no_pii: """ experiment_id = models.PositiveSmallIntegerField( - null=False, blank=False, db_index=True, verbose_name=u'Experiment ID' + null=False, blank=False, db_index=True, verbose_name='Experiment ID' ) key = models.CharField(null=False, blank=False, max_length=255) value = models.TextField() history = HistoricalRecords() - class Meta(object): + class Meta: verbose_name = 'Experiment Key-Value Pair' verbose_name_plural = 'Experiment Key-Value Pairs' unique_together = ( diff --git a/lms/djangoapps/experiments/permissions.py b/lms/djangoapps/experiments/permissions.py index 2f58f1cdda..6a5a6104f4 100644 --- a/lms/djangoapps/experiments/permissions.py +++ b/lms/djangoapps/experiments/permissions.py @@ -19,7 +19,7 @@ class IsStaffOrOwner(permissions.IsStaffOrOwner): # Non-staff users can only create data for themselves. if view.action == 'create': username = request.user.username - return super(IsStaffOrOwner, self).has_permission(request, view) or ( # lint-amnesty, pylint: disable=super-with-arguments + return super().has_permission(request, view) or ( username == request.data.get('user', username)) # The view will handle filtering for the current user diff --git a/lms/djangoapps/experiments/serializers.py b/lms/djangoapps/experiments/serializers.py index 2eef90319b..2b88a13f2a 100644 --- a/lms/djangoapps/experiments/serializers.py +++ b/lms/djangoapps/experiments/serializers.py @@ -15,7 +15,7 @@ class ExperimentDataCreateSerializer(serializers.ModelSerializer): # lint-amnes user = serializers.SlugRelatedField(slug_field='username', default=serializers.CurrentUserDefault(), required=False, queryset=User.objects.all()) - class Meta(object): + class Meta: model = ExperimentData fields = ('id', 'experiment_id', 'user', 'key', 'value', 'created', 'modified',) @@ -28,6 +28,6 @@ class ExperimentDataSerializer(serializers.ModelSerializer): class ExperimentKeyValueSerializer(serializers.ModelSerializer): - class Meta(object): + class Meta: model = ExperimentKeyValue fields = ('id', 'experiment_id', 'key', 'value', 'created', 'modified',) diff --git a/lms/djangoapps/experiments/tests/test_flags.py b/lms/djangoapps/experiments/tests/test_flags.py index 8b509d713f..9387976a00 100644 --- a/lms/djangoapps/experiments/tests/test_flags.py +++ b/lms/djangoapps/experiments/tests/test_flags.py @@ -2,23 +2,24 @@ Tests for experimentation feature flags """ +from unittest.mock import patch + import ddt import pytz from crum import set_current_request from dateutil import parser from django.test.client import RequestFactory from edx_django_utils.cache import RequestCache -from mock import patch +from edx_toggles.toggles.testutils import override_waffle_flag from opaque_keys.edx.keys import CourseKey -from edx_toggles.toggles.testutils import override_waffle_flag -from lms.djangoapps.experiments.testutils import override_experiment_waffle_flag +from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory from lms.djangoapps.experiments.factories import ExperimentKeyValueFactory from lms.djangoapps.experiments.flags import ExperimentWaffleFlag +from lms.djangoapps.experiments.testutils import override_experiment_waffle_flag from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag from openedx.core.djangoapps.waffle_utils.models import WaffleFlagCourseOverrideModel -from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase diff --git a/lms/djangoapps/experiments/tests/test_utils.py b/lms/djangoapps/experiments/tests/test_utils.py index e1d7a2b006..d3223c33c5 100644 --- a/lms/djangoapps/experiments/tests/test_utils.py +++ b/lms/djangoapps/experiments/tests/test_utils.py @@ -4,10 +4,12 @@ Tests of experiment functionality from datetime import timedelta from decimal import Decimal -from django.utils.timezone import now -from unittest import TestCase # lint-amnesty, pylint: disable=wrong-import-order +from unittest import TestCase +from django.utils.timezone import now from opaque_keys.edx.keys import CourseKey + +from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory from lms.djangoapps.course_blocks.transformers.tests.helpers import ModuleStoreTestCase from lms.djangoapps.courseware import courses # lint-amnesty, pylint: disable=unused-import from lms.djangoapps.experiments.utils import ( @@ -17,7 +19,6 @@ from lms.djangoapps.experiments.utils import ( get_unenrolled_courses, is_enrolled_in_course_run ) -from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory from xmodule.modulestore.tests.factories import CourseFactory @@ -27,7 +28,7 @@ class ExperimentUtilsTests(ModuleStoreTestCase, TestCase): """ def setUp(self): - super(ExperimentUtilsTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() # Create a course run self.run_a_price = '86.00' @@ -103,7 +104,7 @@ class ExperimentUtilsTests(ModuleStoreTestCase, TestCase): courses = [course_a] # lint-amnesty, pylint: disable=redefined-outer-name price, skus = get_program_price_and_skus(courses) - expected_price = u'$199.23' + expected_price = '$199.23' assert expected_price == price assert 1 == len(skus) assert self.entitlement_a_sku in skus @@ -116,7 +117,7 @@ class ExperimentUtilsTests(ModuleStoreTestCase, TestCase): courses = [course_a, course_b] # lint-amnesty, pylint: disable=redefined-outer-name price, skus = get_program_price_and_skus(courses) - expected_price = u'$285.23' + expected_price = '$285.23' assert expected_price == price assert 2 == len(skus) assert self.run_a_sku in skus diff --git a/lms/djangoapps/experiments/tests/test_views.py b/lms/djangoapps/experiments/tests/test_views.py index 31637dcee6..b82cb1fca2 100644 --- a/lms/djangoapps/experiments/tests/test_views.py +++ b/lms/djangoapps/experiments/tests/test_views.py @@ -4,25 +4,27 @@ Tests for experimentation views import unittest +from datetime import timedelta +from unittest.mock import patch import six.moves.urllib.error import six.moves.urllib.parse import six.moves.urllib.request -from datetime import timedelta from django.conf import settings from django.core.handlers.wsgi import WSGIRequest -from django.utils.timezone import now from django.test.utils import override_settings from django.urls import reverse -from lms.djangoapps.course_blocks.transformers.tests.helpers import ModuleStoreTestCase -from mock import patch # lint-amnesty, pylint: disable=wrong-import-order -from rest_framework.test import APITestCase # lint-amnesty, pylint: disable=wrong-import-order +from django.utils.timezone import now +from rest_framework.test import APITestCase -from lms.djangoapps.experiments.factories import ExperimentDataFactory, ExperimentKeyValueFactory -from lms.djangoapps.experiments.models import ExperimentData, ExperimentKeyValue # lint-amnesty, pylint: disable=unused-import -from lms.djangoapps.experiments.serializers import ExperimentDataSerializer from common.djangoapps.student.tests.factories import UserFactory - +from lms.djangoapps.course_blocks.transformers.tests.helpers import ModuleStoreTestCase +from lms.djangoapps.experiments.factories import ExperimentDataFactory, ExperimentKeyValueFactory +from lms.djangoapps.experiments.models import ( # lint-amnesty, pylint: disable=unused-import + ExperimentData, + ExperimentKeyValue +) +from lms.djangoapps.experiments.serializers import ExperimentDataSerializer from xmodule.modulestore.tests.factories import CourseFactory CROSS_DOMAIN_REFERER = 'https://ecommerce.edx.org' @@ -75,18 +77,18 @@ class ExperimentDataViewSetTests(APITestCase, ModuleStoreTestCase): # lint-amne data = ExperimentDataFactory.create_batch(3, user=user, experiment_id=experiment_id) qs = six.moves.urllib.parse.urlencode({'experiment_id': experiment_id}) - response = self.client.get('{url}?{qs}'.format(url=url, qs=qs)) + response = self.client.get(f'{url}?{qs}') assert response.status_code == 200 assert response.data['results'] == ExperimentDataSerializer(data, many=True).data datum = data[0] qs = six.moves.urllib.parse.urlencode({'key': datum.key}) - response = self.client.get('{url}?{qs}'.format(url=url, qs=qs)) + response = self.client.get(f'{url}?{qs}') assert response.status_code == 200 assert response.data['results'] == ExperimentDataSerializer([datum], many=True).data qs = six.moves.urllib.parse.urlencode({'experiment_id': experiment_id, 'key': datum.key}) - response = self.client.get('{url}?{qs}'.format(url=url, qs=qs)) + response = self.client.get(f'{url}?{qs}') assert response.status_code == 200 assert response.data['results'] == ExperimentDataSerializer([datum], many=True).data @@ -197,7 +199,7 @@ class ExperimentCrossDomainTests(APITestCase): """Tests for handling cross-domain requests""" def setUp(self): - super(ExperimentCrossDomainTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.client = self.client_class(enforce_csrf_checks=True) @cross_domain_config diff --git a/lms/djangoapps/experiments/tests/test_views_custom.py b/lms/djangoapps/experiments/tests/test_views_custom.py index 3d43f0e422..d165f675af 100644 --- a/lms/djangoapps/experiments/tests/test_views_custom.py +++ b/lms/djangoapps/experiments/tests/test_views_custom.py @@ -6,17 +6,16 @@ Tests for experimentation views from datetime import timedelta from uuid import uuid4 -import six from django.urls import reverse from django.utils.timezone import now +from edx_toggles.toggles.testutils import override_waffle_flag from rest_framework.test import APITestCase from common.djangoapps.course_modes.models import CourseMode from common.djangoapps.course_modes.tests.factories import CourseModeFactory -from edx_toggles.toggles.testutils import override_waffle_flag # lint-amnesty, pylint: disable=wrong-import-order +from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory from lms.djangoapps.course_blocks.transformers.tests.helpers import ModuleStoreTestCase from lms.djangoapps.experiments.views_custom import MOBILE_UPSELL_FLAG -from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory from xmodule.modulestore.tests.factories import CourseFactory CROSS_DOMAIN_REFERER = 'https://ecommerce.edx.org' @@ -36,11 +35,11 @@ class Rev934Tests(APITestCase, ModuleStoreTestCase): """Test mobile app upsell API""" @classmethod def setUpClass(cls): - super(Rev934Tests, cls).setUpClass() + super().setUpClass() cls.url = reverse('api_experiments:rev_934') def setUp(self): - super(Rev934Tests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory(username='robot-mue-1-6pnjv') # Username that hashes to bucket 1 self.client.login( username=self.user.username, @@ -92,7 +91,7 @@ class Rev934Tests(APITestCase, ModuleStoreTestCase): mode_slug=CourseMode.VERIFIED, course_id=course.id, min_price=10, - sku=six.text_type(uuid4().hex) + sku=str(uuid4().hex) ) response = self.client.get(self.url, {'course_id': str(course.id)}) @@ -102,7 +101,7 @@ class Rev934Tests(APITestCase, ModuleStoreTestCase): assert bool(result['basket_url']) expected = { 'show_upsell': True, - 'price': u'$10', + 'price': '$10', 'basket_url': result['basket_url'], # Example basket_url: u'/verify_student/upgrade/org.0/course_0/test/' } @@ -119,7 +118,7 @@ class Rev934Tests(APITestCase, ModuleStoreTestCase): mode_slug=CourseMode.VERIFIED, course_id=course.id, min_price=10, - sku=six.text_type(uuid4().hex), + sku=str(uuid4().hex), expiration_datetime=now() - timedelta(days=30), ) @@ -146,7 +145,7 @@ class Rev934Tests(APITestCase, ModuleStoreTestCase): mode_slug=CourseMode.VERIFIED, course_id=course.id, min_price=10, - sku=six.text_type(uuid4().hex) + sku=str(uuid4().hex) ) response = self.client.get(self.url, {'course_id': str(course.id)}) @@ -170,7 +169,7 @@ class Rev934Tests(APITestCase, ModuleStoreTestCase): mode_slug=CourseMode.VERIFIED, course_id=course.id, min_price=10, - sku=six.text_type(uuid4().hex) + sku=str(uuid4().hex) ) response = self.client.get(self.url, {'course_id': str(course.id)}) @@ -193,7 +192,7 @@ class Rev934Tests(APITestCase, ModuleStoreTestCase): mode_slug=CourseMode.VERIFIED, course_id=course.id, min_price=10, - sku=six.text_type(uuid4().hex) + sku=str(uuid4().hex) ) CourseEnrollmentFactory.create( is_active=True, diff --git a/lms/djangoapps/experiments/testutils.py b/lms/djangoapps/experiments/testutils.py index 3a3e6757c8..1a14fd35c8 100644 --- a/lms/djangoapps/experiments/testutils.py +++ b/lms/djangoapps/experiments/testutils.py @@ -1,7 +1,6 @@ # lint-amnesty, pylint: disable=missing-module-docstring from contextlib import contextmanager - -from mock import patch +from unittest.mock import patch from edx_toggles.toggles.testutils import override_waffle_flag diff --git a/lms/djangoapps/experiments/utils.py b/lms/djangoapps/experiments/utils.py index 4db140bde7..76a0ed5732 100644 --- a/lms/djangoapps/experiments/utils.py +++ b/lms/djangoapps/experiments/utils.py @@ -6,14 +6,14 @@ Utilities to facilitate experimentation import logging from decimal import Decimal -import six from django.utils.timezone import now +from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey -from common.djangoapps.course_modes.models import format_course_price, get_cosmetic_verified_display_price, CourseMode -from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace # lint-amnesty, pylint: disable=wrong-import-order +from common.djangoapps.course_modes.models import CourseMode, format_course_price, get_cosmetic_verified_display_price from common.djangoapps.entitlements.models import CourseEntitlement +from common.djangoapps.student.models import CourseEnrollment from lms.djangoapps.commerce.utils import EcommerceService from lms.djangoapps.courseware.access import has_staff_access_to_preview_mode from lms.djangoapps.courseware.utils import can_show_verified_upgrade, verified_upgrade_deadline_link @@ -21,7 +21,6 @@ from openedx.core.djangoapps.catalog.utils import get_programs from openedx.core.djangoapps.django_comment_common.models import Role from openedx.core.djangoapps.schedules.models import Schedule from openedx.features.course_duration_limits.access import get_user_course_duration, get_user_course_expiration_date -from common.djangoapps.student.models import CourseEnrollment from xmodule.partitions.partitions_service import get_all_partitions_for_course, get_user_partition_groups # Import this for backwards compatibility (so that anyone importing this function from here doesn't break) @@ -31,7 +30,7 @@ logger = logging.getLogger(__name__) # TODO: clean up as part of REVEM-199 (START) -experiments_namespace = LegacyWaffleFlagNamespace(name=u'experiments') +experiments_namespace = LegacyWaffleFlagNamespace(name='experiments') # .. toggle_name: experiments.add_programs # .. toggle_implementation: WaffleFlag @@ -44,7 +43,7 @@ experiments_namespace = LegacyWaffleFlagNamespace(name=u'experiments') # .. toggle_warnings: This temporary feature toggle does not have a target removal date. PROGRAM_INFO_FLAG = LegacyWaffleFlag( waffle_namespace=experiments_namespace, - flag_name=u'add_programs', + flag_name='add_programs', module_name=__name__, ) @@ -57,7 +56,7 @@ PROGRAM_INFO_FLAG = LegacyWaffleFlag( # .. toggle_target_removal_date: None # .. toggle_tickets: REVEM-118 # .. toggle_warnings: This temporary feature toggle does not have a target removal date. -DASHBOARD_INFO_FLAG = LegacyWaffleFlag(experiments_namespace, u'add_dashboard_info', __name__) +DASHBOARD_INFO_FLAG = LegacyWaffleFlag(experiments_namespace, 'add_dashboard_info', __name__) # TODO END: clean up as part of REVEM-199 (End) # TODO: Clean up as part of REV-1205 (START) @@ -72,7 +71,7 @@ DASHBOARD_INFO_FLAG = LegacyWaffleFlag(experiments_namespace, u'add_dashboard_in # .. toggle_warnings: This temporary feature toggle does not have a target removal date. UPSELL_TRACKING_FLAG = LegacyWaffleFlag( waffle_namespace=experiments_namespace, - flag_name=u'add_upsell_tracking', + flag_name='add_upsell_tracking', module_name=__name__, ) # TODO END: Clean up as part of REV-1205 (End) @@ -88,13 +87,13 @@ def check_and_get_upgrade_link_and_date(user, enrollment=None, course=None): otherwise, returns None for both the link and date. """ if enrollment is None and course is None: - logger.warning(u'Must specify either an enrollment or a course') + logger.warning('Must specify either an enrollment or a course') return (None, None, None) if enrollment: if course and enrollment.course_id != course.id: - logger.warning(u'{} refers to a different course than {} which was supplied. Enrollment course id={}, ' - u'repr={!r}, deprecated={}. Course id={}, repr={!r}, deprecated={}.' + logger.warning('{} refers to a different course than {} which was supplied. Enrollment course id={}, ' + 'repr={!r}, deprecated={}. Course id={}, repr={!r}, deprecated={}.' .format(enrollment, course, enrollment.course_id, @@ -108,15 +107,15 @@ def check_and_get_upgrade_link_and_date(user, enrollment=None, course=None): return (None, None, None) if enrollment.user_id != user.id: - logger.warning(u'{} refers to a different user than {} which was supplied. ' - u'Enrollment user id={}, repr={!r}. ' - u'User id={}, repr={!r}.'.format(enrollment, - user, - enrollment.user_id, - enrollment.user_id, - user.id, - user.id, - ) + logger.warning('{} refers to a different user than {} which was supplied. ' + 'Enrollment user id={}, repr={!r}. ' + 'User id={}, repr={!r}.'.format(enrollment, + user, + enrollment.user_id, + enrollment.user_id, + user.id, + user.id, + ) ) return (None, None, None) @@ -154,7 +153,7 @@ def get_program_price_and_skus(courses): skus = None else: program_price = format_course_price(program_price) - program_price = six.text_type(program_price) + program_price = str(program_price) return program_price, skus @@ -237,7 +236,7 @@ def is_enrolled_in_course_run(course_run, enrollment_course_ids): return course_run_key in enrollment_course_ids except InvalidKeyError: logger.warning( - u'Unable to determine if user was enrolled since the course key {} is invalid'.format(key) + f'Unable to determine if user was enrolled since the course key {key} is invalid' ) return False # Invalid course run key. Assume user is not enrolled. @@ -373,8 +372,8 @@ def get_experiment_user_metadata_context(course, user): } if not context.get('course_id'): - user_metadata['course_id'] = six.text_type(course_key) - elif isinstance(course_key, six.string_types): + user_metadata['course_id'] = str(course_key) + elif isinstance(course_key, str): user_metadata['course_id'] = course_key context['user_metadata'] = user_metadata @@ -411,7 +410,7 @@ def get_base_experiment_metadata_context(course, user, enrollment, user_enrollme return { 'upgrade_link': upgrade_link, - 'upgrade_price': six.text_type(get_cosmetic_verified_display_price(course)), + 'upgrade_price': str(get_cosmetic_verified_display_price(course)), 'enrollment_mode': enrollment_mode, 'enrollment_time': enrollment_time, 'schedule_start': schedule_start, diff --git a/lms/djangoapps/experiments/views.py b/lms/djangoapps/experiments/views.py index 599cb34502..e25c593904 100644 --- a/lms/djangoapps/experiments/views.py +++ b/lms/djangoapps/experiments/views.py @@ -5,23 +5,23 @@ Experimentation views from django.contrib.auth import get_user_model from django.db import transaction # lint-amnesty, pylint: disable=unused-import -from django_filters.rest_framework import DjangoFilterBackend from django.http import Http404 +from django_filters.rest_framework import DjangoFilterBackend from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication from edx_rest_framework_extensions.auth.session.authentication import SessionAuthenticationAllowInactiveUser -from lms.djangoapps.courseware import courses -from opaque_keys.edx.keys import CourseKey # lint-amnesty, pylint: disable=wrong-import-order -from rest_framework import permissions, viewsets # lint-amnesty, pylint: disable=wrong-import-order +from opaque_keys.edx.keys import CourseKey +from rest_framework import permissions, viewsets from rest_framework.response import Response # lint-amnesty, pylint: disable=unused-import, wrong-import-order -from rest_framework.views import APIView # lint-amnesty, pylint: disable=wrong-import-order -from common.djangoapps.util.json_request import JsonResponse +from rest_framework.views import APIView +from common.djangoapps.student.models import get_user_by_username_or_email +from common.djangoapps.util.json_request import JsonResponse +from lms.djangoapps.courseware import courses from lms.djangoapps.experiments import filters, serializers from lms.djangoapps.experiments.models import ExperimentData, ExperimentKeyValue from lms.djangoapps.experiments.permissions import IsStaffOrOwner, IsStaffOrReadOnly, IsStaffOrReadOnlyForSelf from lms.djangoapps.experiments.utils import get_experiment_user_metadata_context from openedx.core.djangoapps.cors_csrf.authentication import SessionAuthenticationCrossDomainCsrf -from common.djangoapps.student.models import get_user_by_username_or_email User = get_user_model() # pylint: disable=invalid-name @@ -42,7 +42,7 @@ class ExperimentDataViewSet(viewsets.ModelViewSet): # lint-amnesty, pylint: dis def filter_queryset(self, queryset): queryset = queryset.filter(user=self.request.user) - return super(ExperimentDataViewSet, self).filter_queryset(queryset) # lint-amnesty, pylint: disable=super-with-arguments + return super().filter_queryset(queryset) def get_serializer_class(self): if self.action == 'create': diff --git a/lms/djangoapps/experiments/views_custom.py b/lms/djangoapps/experiments/views_custom.py index 87a2b94473..0d4c436887 100644 --- a/lms/djangoapps/experiments/views_custom.py +++ b/lms/djangoapps/experiments/views_custom.py @@ -2,21 +2,19 @@ The Discount API Views should return information about discounts that apply to the user and course. """ -# -*- coding: utf-8 -*- - - -import six from django.http import HttpResponseBadRequest from django.utils.decorators import method_decorator from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication from edx_rest_framework_extensions.auth.session.authentication import SessionAuthenticationAllowInactiveUser +from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey from rest_framework.response import Response from rest_framework.views import APIView from common.djangoapps.course_modes.models import get_cosmetic_verified_display_price -from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace # lint-amnesty, pylint: disable=wrong-import-order +from common.djangoapps.student.models import CourseEnrollment +from common.djangoapps.track import segment from lms.djangoapps.commerce.utils import EcommerceService from lms.djangoapps.courseware.utils import can_show_verified_upgrade from lms.djangoapps.experiments.stable_bucketing import stable_bucketing_hash_group @@ -25,8 +23,6 @@ from openedx.core.djangoapps.cors_csrf.decorators import ensure_csrf_cookie_cros from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser from openedx.core.lib.api.permissions import ApiKeyHeaderPermissionIsAuthenticated from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin -from common.djangoapps.student.models import CourseEnrollment -from common.djangoapps.track import segment # .. toggle_name: experiments.mobile_upsell_rev934 # .. toggle_implementation: WaffleFlag @@ -38,8 +34,8 @@ from common.djangoapps.track import segment # .. toggle_tickets: REV-934 # .. toggle_warnings: This temporary feature toggle does not have a target removal date. MOBILE_UPSELL_FLAG = LegacyWaffleFlag( - waffle_namespace=LegacyWaffleFlagNamespace(name=u'experiments'), - flag_name=u'mobile_upsell_rev934', + waffle_namespace=LegacyWaffleFlagNamespace(name='experiments'), + flag_name='mobile_upsell_rev934', module_name=__name__, ) MOBILE_UPSELL_EXPERIMENT = 'mobile_upsell_experiment' @@ -135,7 +131,7 @@ class Rev934(DeveloperErrorViewMixin, APIView): user_upsell = True basket_url = EcommerceService().upgrade_url(user, course.id) - upgrade_price = six.text_type(get_cosmetic_verified_display_price(course)) + upgrade_price = str(get_cosmetic_verified_display_price(course)) could_upsell = bool(user_upsell and basket_url) bucket = stable_bucketing_hash_group(MOBILE_UPSELL_EXPERIMENT, 2, user)