refactor: pyupgrade in openedx ccxcon and certificates (#26852)
This commit is contained in:
@@ -4,9 +4,8 @@ Module containing API functions for the CCXCon
|
||||
|
||||
|
||||
import logging
|
||||
from urllib import parse
|
||||
|
||||
import six
|
||||
import six.moves.urllib.parse # lint-amnesty, pylint: disable=import-error, wrong-import-order
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import URLValidator
|
||||
from django.http import Http404
|
||||
@@ -92,31 +91,31 @@ def course_info_to_ccxcon(course_key):
|
||||
try:
|
||||
course = get_course_by_id(course_key)
|
||||
except Http404:
|
||||
log.error(u'Master Course with key "%s" not found', six.text_type(course_key))
|
||||
log.error('Master Course with key "%s" not found', str(course_key))
|
||||
return
|
||||
if not course.enable_ccx:
|
||||
log.debug(u'ccx not enabled for course key "%s"', six.text_type(course_key))
|
||||
log.debug('ccx not enabled for course key "%s"', str(course_key))
|
||||
return
|
||||
if not course.ccx_connector:
|
||||
log.debug(u'ccx connector not defined for course key "%s"', six.text_type(course_key))
|
||||
log.debug('ccx connector not defined for course key "%s"', str(course_key))
|
||||
return
|
||||
if not is_valid_url(course.ccx_connector):
|
||||
log.error(
|
||||
u'ccx connector URL "%s" for course key "%s" is not a valid URL.',
|
||||
course.ccx_connector, six.text_type(course_key)
|
||||
'ccx connector URL "%s" for course key "%s" is not a valid URL.',
|
||||
course.ccx_connector, str(course_key)
|
||||
)
|
||||
return
|
||||
# get the oauth credential for this URL
|
||||
try:
|
||||
ccxcon = CCXCon.objects.get(url=course.ccx_connector)
|
||||
except CCXCon.DoesNotExist:
|
||||
log.error(u'ccx connector Oauth credentials not configured for URL "%s".', course.ccx_connector)
|
||||
log.error('ccx connector Oauth credentials not configured for URL "%s".', course.ccx_connector)
|
||||
return
|
||||
|
||||
# get an oauth client with a valid token
|
||||
|
||||
oauth_ccxcon = get_oauth_client(
|
||||
server_token_url=six.moves.urllib.parse.urljoin(course.ccx_connector, CCXCON_TOKEN_URL),
|
||||
server_token_url=parse.urljoin(course.ccx_connector, CCXCON_TOKEN_URL),
|
||||
client_id=ccxcon.oauth_client_id,
|
||||
client_secret=ccxcon.oauth_client_secret
|
||||
)
|
||||
@@ -129,7 +128,7 @@ def course_info_to_ccxcon(course_key):
|
||||
course_details = CourseDetails.fetch(course_key)
|
||||
|
||||
payload = {
|
||||
'course_id': six.text_type(course_key),
|
||||
'course_id': str(course_key),
|
||||
'title': course.display_name,
|
||||
'author_name': None,
|
||||
'overview': course_details.overview,
|
||||
@@ -140,7 +139,7 @@ def course_info_to_ccxcon(course_key):
|
||||
headers = {'content-type': 'application/json'}
|
||||
|
||||
# make the POST request
|
||||
add_course_url = six.moves.urllib.parse.urljoin(course.ccx_connector, CCXCON_COURSEXS_URL)
|
||||
add_course_url = parse.urljoin(course.ccx_connector, CCXCON_COURSEXS_URL)
|
||||
resp = oauth_ccxcon.post(
|
||||
url=add_course_url,
|
||||
json=payload,
|
||||
@@ -149,11 +148,11 @@ def course_info_to_ccxcon(course_key):
|
||||
)
|
||||
|
||||
if resp.status_code >= 500:
|
||||
raise CCXConnServerError(u'Server returned error Status: %s, Content: %s', resp.status_code, resp.content) # lint-amnesty, pylint: disable=raising-format-tuple
|
||||
raise CCXConnServerError('Server returned error Status: %s, Content: %s', resp.status_code, resp.content) # lint-amnesty, pylint: disable=raising-format-tuple
|
||||
if resp.status_code >= 400:
|
||||
log.error(u"Error creating course on ccxcon. Status: %s, Content: %s", resp.status_code, resp.content)
|
||||
log.error("Error creating course on ccxcon. Status: %s, Content: %s", resp.status_code, resp.content)
|
||||
# this API performs a POST request both for POST and PATCH, but the POST returns 201 and the PATCH returns 200
|
||||
elif resp.status_code != HTTP_200_OK and resp.status_code != HTTP_201_CREATED:
|
||||
log.error(u'Server returned unexpected status code %s', resp.status_code)
|
||||
log.error('Server returned unexpected status code %s', resp.status_code)
|
||||
else:
|
||||
log.debug(u'Request successful. Status: %s, Content: %s', resp.status_code, resp.content)
|
||||
log.debug('Request successful. Status: %s, Content: %s', resp.status_code, resp.content)
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -20,13 +20,13 @@ class CCXCon(models.Model):
|
||||
oauth_client_secret = models.CharField(max_length=255)
|
||||
title = models.CharField(max_length=255)
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
app_label = 'ccxcon'
|
||||
verbose_name = 'CCX Connector'
|
||||
verbose_name_plural = 'CCX Connectors'
|
||||
|
||||
def __repr__(self):
|
||||
return '<CCXCon {}>'.format(self.title)
|
||||
return f'<CCXCon {self.title}>'
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
"""
|
||||
Signal handler for posting course updated to CCXCon
|
||||
"""
|
||||
|
||||
|
||||
import six
|
||||
from django.dispatch.dispatcher import receiver
|
||||
|
||||
from xmodule.modulestore.django import SignalHandler
|
||||
@@ -18,4 +15,4 @@ def _listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable
|
||||
# update the course information on ccxcon using celery
|
||||
# import here, because signal is registered at startup, but items in tasks are not yet able to be loaded
|
||||
from openedx.core.djangoapps.ccxcon import tasks
|
||||
tasks.update_ccxcon.delay(six.text_type(course_key))
|
||||
tasks.update_ccxcon.delay(str(course_key))
|
||||
|
||||
@@ -28,9 +28,9 @@ def update_ccxcon(course_id, cur_retry=0):
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
try:
|
||||
api.course_info_to_ccxcon(course_key)
|
||||
log.info(u'Course update to CCXCon returned no errors. Course key: %s', course_id)
|
||||
log.info('Course update to CCXCon returned no errors. Course key: %s', course_id)
|
||||
except (ConnectionError, HTTPError, RequestException, TooManyRedirects, api.CCXConnServerError) as exp:
|
||||
log.error(u'Course update to CCXCon failed for course_id %s with error: %s', course_id, exp)
|
||||
log.error('Course update to CCXCon failed for course_id %s with error: %s', course_id, exp)
|
||||
# in case the maximum amount of retries has not been reached,
|
||||
# insert another task delayed exponentially up to 5 retries
|
||||
if cur_retry < 5:
|
||||
@@ -38,4 +38,4 @@ def update_ccxcon(course_id, cur_retry=0):
|
||||
kwargs={'course_id': course_id, 'cur_retry': cur_retry + 1},
|
||||
countdown=10 ** cur_retry # number of seconds the task should be delayed
|
||||
)
|
||||
log.info(u'Requeued celery task for course key %s ; retry # %s', course_id, cur_retry + 1)
|
||||
log.info('Requeued celery task for course key %s ; retry # %s', course_id, cur_retry + 1)
|
||||
|
||||
@@ -12,7 +12,7 @@ class CcxConFactory(DjangoModelFactory):
|
||||
"""
|
||||
Model factory for the CCXCon model
|
||||
"""
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = CCXCon
|
||||
|
||||
oauth_client_id = 'asdfjasdljfasdkjffsdfjksd98fsd8y24fdsiuhsfdsf'
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
"""
|
||||
Unit tests for the API module
|
||||
"""
|
||||
|
||||
|
||||
import datetime
|
||||
from unittest import mock
|
||||
from urllib import parse
|
||||
|
||||
import pytest
|
||||
import mock
|
||||
import pytz
|
||||
import six.moves.urllib.parse # lint-amnesty, pylint: disable=import-error, wrong-import-order
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from six.moves import range
|
||||
|
||||
from openedx.core.djangoapps.ccxcon import api as ccxconapi
|
||||
from common.djangoapps.student.tests.factories import AdminFactory
|
||||
@@ -42,7 +40,7 @@ class APIsTestCase(SharedModuleStoreTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(APIsTestCase, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
cls.course = course = CourseFactory.create()
|
||||
cls.course_key = cls.course.location.course_key
|
||||
|
||||
@@ -65,7 +63,7 @@ class APIsTestCase(SharedModuleStoreTestCase):
|
||||
cls.verticals = flatten([
|
||||
[
|
||||
ItemFactory.create(
|
||||
start=start, due=due, parent=sequential, graded=True, format='Homework', category=u'vertical'
|
||||
start=start, due=due, parent=sequential, graded=True, format='Homework', category='vertical'
|
||||
) for _ in range(2)
|
||||
] for sequential in cls.sequentials
|
||||
])
|
||||
@@ -83,7 +81,7 @@ class APIsTestCase(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Set up tests
|
||||
"""
|
||||
super(APIsTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
# Create instructor account
|
||||
self.instructor = AdminFactory.create()
|
||||
# create an instance of modulestore
|
||||
@@ -163,7 +161,7 @@ class APIsTestCase(SharedModuleStoreTestCase):
|
||||
# no args used for the call
|
||||
assert k_args == tuple()
|
||||
assert k_kwargs.get('url') ==\
|
||||
six.moves.urllib.parse.urljoin(self.course.ccx_connector, ccxconapi.CCXCON_COURSEXS_URL)
|
||||
parse.urljoin(self.course.ccx_connector, ccxconapi.CCXCON_COURSEXS_URL)
|
||||
|
||||
# second call with different status code
|
||||
mock_response.status_code = 200
|
||||
@@ -176,7 +174,7 @@ class APIsTestCase(SharedModuleStoreTestCase):
|
||||
# no args used for the call
|
||||
assert k_args == tuple()
|
||||
assert k_kwargs.get('url') ==\
|
||||
six.moves.urllib.parse.urljoin(self.course.ccx_connector, ccxconapi.CCXCON_COURSEXS_URL)
|
||||
parse.urljoin(self.course.ccx_connector, ccxconapi.CCXCON_COURSEXS_URL)
|
||||
|
||||
@mock.patch('requests_oauthlib.oauth2_session.OAuth2Session.fetch_token', fetch_token_mock)
|
||||
@mock.patch('requests_oauthlib.oauth2_session.OAuth2Session.post')
|
||||
|
||||
@@ -3,7 +3,7 @@ Test for contentstore signals receiver
|
||||
"""
|
||||
|
||||
|
||||
import mock
|
||||
from unittest import mock
|
||||
from django.test import TestCase
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
@@ -26,7 +26,7 @@ class CCXConSignalTestCase(TestCase):
|
||||
mock_response = mock.MagicMock(return_value=None)
|
||||
mock_upc.return_value = mock_response
|
||||
|
||||
course_id = u'course-v1:OrgFoo+CN199+CR-FALL01'
|
||||
course_id = 'course-v1:OrgFoo+CN199+CR-FALL01'
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
|
||||
signal_handler = SignalHandler(modulestore())
|
||||
|
||||
@@ -3,7 +3,7 @@ Tests for the CCXCon celery tasks
|
||||
"""
|
||||
|
||||
|
||||
import mock
|
||||
from unittest import mock
|
||||
from django.test import TestCase
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
@@ -23,7 +23,7 @@ class CCXConTaskTestCase(TestCase):
|
||||
mock_response = mock.Mock()
|
||||
mock_citc.return_value = mock_response
|
||||
|
||||
course_id = u'course-v1:OrgFoo+CN199+CR-FALL01'
|
||||
course_id = 'course-v1:OrgFoo+CN199+CR-FALL01'
|
||||
tasks.update_ccxcon.delay(course_id)
|
||||
|
||||
mock_citc.assert_called_once_with(CourseKey.from_string(course_id))
|
||||
@@ -34,7 +34,7 @@ class CCXConTaskTestCase(TestCase):
|
||||
Test task with exception that triggers a retry
|
||||
"""
|
||||
mock_citc.side_effect = api.CCXConnServerError()
|
||||
course_id = u'course-v1:OrgFoo+CN199+CR-FALL01'
|
||||
course_id = 'course-v1:OrgFoo+CN199+CR-FALL01'
|
||||
tasks.update_ccxcon.delay(course_id)
|
||||
|
||||
assert mock_citc.call_count == 6
|
||||
|
||||
@@ -6,7 +6,6 @@ The public API for certificates.
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
import six # lint-amnesty, pylint: disable=unused-import
|
||||
from pytz import UTC
|
||||
|
||||
from lms.djangoapps.certificates.models import CertificateStatuses, CertificateWhitelist
|
||||
|
||||
@@ -7,14 +7,14 @@ waffle switches for the Certificates app.
|
||||
from edx_toggles.toggles import LegacyWaffleSwitchNamespace
|
||||
|
||||
# Namespace
|
||||
WAFFLE_NAMESPACE = u'certificates'
|
||||
WAFFLE_NAMESPACE = 'certificates'
|
||||
|
||||
# Switches
|
||||
AUTO_CERTIFICATE_GENERATION = u'auto_certificate_generation'
|
||||
AUTO_CERTIFICATE_GENERATION = 'auto_certificate_generation'
|
||||
|
||||
|
||||
def waffle():
|
||||
"""
|
||||
Returns the namespaced, cached, audited Waffle class for Certificates.
|
||||
"""
|
||||
return LegacyWaffleSwitchNamespace(name=WAFFLE_NAMESPACE, log_prefix=u'Certificates: ')
|
||||
return LegacyWaffleSwitchNamespace(name=WAFFLE_NAMESPACE, log_prefix='Certificates: ')
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
import itertools
|
||||
from contextlib import contextmanager
|
||||
from datetime import datetime, timedelta
|
||||
@@ -20,7 +18,7 @@ from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, U
|
||||
|
||||
# TODO: Copied from lms.djangoapps.certificates.models,
|
||||
# to be resolved per https://openedx.atlassian.net/browse/EDUCATOR-1318
|
||||
class CertificateStatuses(object):
|
||||
class CertificateStatuses:
|
||||
"""
|
||||
Enum for certificate statuses
|
||||
"""
|
||||
@@ -45,7 +43,7 @@ class CertificateStatuses(object):
|
||||
)
|
||||
|
||||
|
||||
class MockGeneratedCertificate(object):
|
||||
class MockGeneratedCertificate:
|
||||
"""
|
||||
We can't import GeneratedCertificate from LMS here, so we roll
|
||||
our own minimal Certificate model for testing.
|
||||
@@ -76,7 +74,7 @@ def configure_waffle_namespace(feature_enabled):
|
||||
@ddt.ddt
|
||||
class CertificatesApiTestCase(TestCase):
|
||||
def setUp(self):
|
||||
super(CertificatesApiTestCase, self).setUp()
|
||||
super().setUp()
|
||||
self.course = CourseOverviewFactory.create(
|
||||
start=datetime(2017, 1, 1, tzinfo=pytz.UTC),
|
||||
end=datetime(2017, 1, 31, tzinfo=pytz.UTC),
|
||||
|
||||
Reference in New Issue
Block a user