refactor: pyupgrade in openedx ccxcon and certificates (#26852)

This commit is contained in:
M. Zulqarnain
2021-03-15 18:30:06 +05:00
committed by GitHub
parent 94f14c6b3d
commit 8ec2699269
12 changed files with 40 additions and 52 deletions

View File

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

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models

View File

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

View File

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

View File

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

View File

@@ -12,7 +12,7 @@ class CcxConFactory(DjangoModelFactory):
"""
Model factory for the CCXCon model
"""
class Meta(object):
class Meta:
model = CCXCon
oauth_client_id = 'asdfjasdljfasdkjffsdfjksd98fsd8y24fdsiuhsfdsf'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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