refac: ran pyupgrade on lms/djangoapps/badges

This commit is contained in:
usamasadiq
2021-02-25 18:15:27 +05:00
parent b59b6b4147
commit fb92051dee
20 changed files with 154 additions and 167 deletions

View File

@@ -3,7 +3,7 @@ Admin registration for Badge Models
"""
from config_models.admin import ConfigurationModelAdmin # lint-amnesty, pylint: disable=import-error
from config_models.admin import ConfigurationModelAdmin
from django.contrib import admin
from lms.djangoapps.badges.models import BadgeClass, CourseCompleteImageConfiguration, CourseEventBadgesConfiguration

View File

@@ -3,7 +3,7 @@ Serializers for Badges
"""
from rest_framework import serializers # lint-amnesty, pylint: disable=import-error
from rest_framework import serializers
from lms.djangoapps.badges.models import BadgeAssertion, BadgeClass
@@ -14,7 +14,7 @@ class BadgeClassSerializer(serializers.ModelSerializer):
"""
image_url = serializers.ImageField(source='image')
class Meta(object):
class Meta:
model = BadgeClass
fields = ('slug', 'issuing_component', 'display_name', 'course_id', 'description', 'criteria', 'image_url')
@@ -25,6 +25,6 @@ class BadgeAssertionSerializer(serializers.ModelSerializer):
"""
badge_class = BadgeClassSerializer(read_only=True)
class Meta(object):
class Meta:
model = BadgeAssertion
fields = ('badge_class', 'image_url', 'assertion_url', 'created')

View File

@@ -3,18 +3,16 @@ Tests for the badges API views.
"""
import six
from ddt import data, ddt, unpack # lint-amnesty, pylint: disable=import-error
from ddt import data, ddt, unpack
from django.conf import settings
from django.test.utils import override_settings
from six.moves import range
from lms.djangoapps.badges.tests.factories import BadgeAssertionFactory, BadgeClassFactory, RandomBadgeClassFactory
from openedx.core.lib.api.test_utils import ApiTestCase
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.util.testing import UrlResetMixin
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=import-error, wrong-import-order
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=import-error, wrong-import-order
from lms.djangoapps.badges.tests.factories import BadgeAssertionFactory, BadgeClassFactory, RandomBadgeClassFactory
from openedx.core.lib.api.test_utils import ApiTestCase
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
FEATURES_WITH_BADGES_ENABLED = settings.FEATURES.copy()
FEATURES_WITH_BADGES_ENABLED['ENABLE_OPENBADGES'] = True
@@ -27,7 +25,7 @@ class UserAssertionTestCase(UrlResetMixin, ModuleStoreTestCase, ApiTestCase):
"""
def setUp(self):
super(UserAssertionTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = CourseFactory.create()
self.user = UserFactory.create()
# Password defined by factory.
@@ -37,7 +35,7 @@ class UserAssertionTestCase(UrlResetMixin, ModuleStoreTestCase, ApiTestCase):
"""
Return the URL to look up the current user's assertions.
"""
return '/api/badges/v1/assertions/user/{}/'.format(self.user.username)
return f'/api/badges/v1/assertions/user/{self.user.username}/'
def check_class_structure(self, badge_class, json_class):
"""
@@ -48,7 +46,7 @@ class UserAssertionTestCase(UrlResetMixin, ModuleStoreTestCase, ApiTestCase):
assert badge_class.image.url in json_class['image_url']
assert badge_class.description == json_class['description']
assert badge_class.criteria == json_class['criteria']
assert (badge_class.course_id and six.text_type(badge_class.course_id)) == json_class['course_id']
assert (badge_class.course_id and str(badge_class.course_id)) == json_class['course_id']
def check_assertion_structure(self, assertion, json_assertion):
"""
@@ -65,7 +63,7 @@ class UserAssertionTestCase(UrlResetMixin, ModuleStoreTestCase, ApiTestCase):
if wildcard:
return '*'
else:
return six.text_type(badge_class.course_id)
return str(badge_class.course_id)
def create_badge_class(self, check_course, **kwargs):
"""

View File

@@ -3,12 +3,13 @@ API views for badges
"""
from edx_rest_framework_extensions.auth.session.authentication import SessionAuthenticationAllowInactiveUser # lint-amnesty, pylint: disable=import-error
from opaque_keys import InvalidKeyError # lint-amnesty, pylint: disable=import-error
from opaque_keys.edx.django.models import CourseKeyField # lint-amnesty, pylint: disable=import-error
from opaque_keys.edx.keys import CourseKey # lint-amnesty, pylint: disable=import-error
from rest_framework import generics # lint-amnesty, pylint: disable=import-error
from rest_framework.exceptions import APIException # lint-amnesty, pylint: disable=import-error
from edx_rest_framework_extensions.auth.session.authentication import \
SessionAuthenticationAllowInactiveUser
from opaque_keys import InvalidKeyError
from opaque_keys.edx.django.models import CourseKeyField
from opaque_keys.edx.keys import CourseKey
from rest_framework import generics
from rest_framework.exceptions import APIException
from lms.djangoapps.badges.models import BadgeAssertion
from openedx.core.djangoapps.user_api.permissions import is_field_shared_factory

View File

@@ -12,7 +12,7 @@ class BadgesConfig(AppConfig):
"""
Application Configuration for Badges.
"""
name = u'lms.djangoapps.badges'
name = 'lms.djangoapps.badges'
def ready(self):
"""

View File

@@ -7,11 +7,10 @@ import hashlib
import logging
import mimetypes
import requests # lint-amnesty, pylint: disable=import-error
import six
import requests
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from eventtracking import tracker # lint-amnesty, pylint: disable=import-error
from eventtracking import tracker
from lazy import lazy # lint-amnesty, pylint: disable=no-name-in-module
from requests.packages.urllib3.exceptions import HTTPError # lint-amnesty, pylint: disable=import-error
@@ -29,7 +28,7 @@ class BadgrBackend(BadgeBackend):
badges = []
def __init__(self):
super(BadgrBackend, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments
super().__init__()
if not settings.BADGR_API_TOKEN:
raise ImproperlyConfigured("BADGR_API_TOKEN not set.")
@@ -38,20 +37,20 @@ class BadgrBackend(BadgeBackend):
"""
Base URL for all API requests.
"""
return "{}/v1/issuer/issuers/{}".format(settings.BADGR_BASE_URL, settings.BADGR_ISSUER_SLUG)
return f"{settings.BADGR_BASE_URL}/v1/issuer/issuers/{settings.BADGR_ISSUER_SLUG}"
@lazy
def _badge_create_url(self):
"""
URL for generating a new Badge specification
"""
return "{}/badges".format(self._base_url)
return f"{self._base_url}/badges"
def _badge_url(self, slug):
"""
Get the URL for a course's badge in a given mode.
"""
return "{}/{}".format(self._badge_create_url, slug)
return f"{self._badge_create_url}/{slug}"
def _assertion_url(self, slug):
"""
@@ -67,7 +66,7 @@ class BadgrBackend(BadgeBackend):
if badge_class.issuing_component and badge_class.course_id:
# Make this unique to the course, and down to 64 characters.
# We don't do this to badges without issuing_component set for backwards compatibility.
slug = hashlib.sha256((slug + six.text_type(badge_class.course_id)).encode('utf-8')).hexdigest()
slug = hashlib.sha256((slug + str(badge_class.course_id)).encode('utf-8')).hexdigest()
if len(slug) > MAX_SLUG_LENGTH:
# Will be 64 characters.
slug = hashlib.sha256(slug).hexdigest()
@@ -81,9 +80,9 @@ class BadgrBackend(BadgeBackend):
response.raise_for_status()
except HTTPError:
LOGGER.error(
u"Encountered an error when contacting the Badgr-Server. Request sent to %r with headers %r.\n"
u"and data values %r\n"
u"Response status was %s.\n%s",
"Encountered an error when contacting the Badgr-Server. Request sent to %r with headers %r.\n"
"and data values %r\n"
"Response status was %s.\n%s",
response.request.url, response.request.headers,
data,
response.status_code, response.content
@@ -100,8 +99,8 @@ class BadgrBackend(BadgeBackend):
content_type, __ = mimetypes.guess_type(image.name)
if not content_type:
raise ValueError(
u"Could not determine content-type of image! Make sure it is a properly named .png file. "
u"Filename was: {}".format(image.name)
"Could not determine content-type of image! Make sure it is a properly named .png file. "
"Filename was: {}".format(image.name)
)
files = {'image': (image.name, image, content_type)}
data = {
@@ -126,7 +125,7 @@ class BadgrBackend(BadgeBackend):
'badge_slug': assertion.badge_class.slug,
'badge_name': assertion.badge_class.display_name,
'issuing_component': assertion.badge_class.issuing_component,
'course_id': six.text_type(assertion.badge_class.course_id),
'course_id': str(assertion.badge_class.course_id),
'enrollment_mode': assertion.badge_class.mode,
'assertion_id': assertion.id,
'assertion_image_url': assertion.image_url,
@@ -162,7 +161,7 @@ class BadgrBackend(BadgeBackend):
"""
Headers to send along with the request-- used for authentication.
"""
return {'Authorization': u'Token {}'.format(settings.BADGR_API_TOKEN)}
return {'Authorization': f'Token {settings.BADGR_API_TOKEN}'}
def _ensure_badge_created(self, badge_class):
"""

View File

@@ -5,10 +5,8 @@ Base class for badge backends.
from abc import ABCMeta, abstractmethod
import six
class BadgeBackend(six.with_metaclass(ABCMeta, object)):
class BadgeBackend(metaclass=ABCMeta):
"""
Defines the interface for badging backends.
"""

View File

@@ -4,22 +4,21 @@ Tests for BadgrBackend
from datetime import datetime
from unittest.mock import Mock, call, patch
import ddt # lint-amnesty, pylint: disable=import-error
import six
import ddt
from django.db.models.fields.files import ImageFieldFile
from django.test.utils import override_settings
from lazy.lazy import lazy # lint-amnesty, pylint: disable=import-error, no-name-in-module
from mock import Mock, call, patch # lint-amnesty, pylint: disable=import-error
from lazy.lazy import lazy # lint-amnesty, pylint: disable=no-name-in-module
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from common.djangoapps.track.tests import EventTrackingTestCase
from lms.djangoapps.badges.backends.badgr import BadgrBackend
from lms.djangoapps.badges.models import BadgeAssertion
from lms.djangoapps.badges.tests.factories import BadgeClassFactory
from openedx.core.lib.tests.assertions.events import assert_event_matches
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from common.djangoapps.track.tests import EventTrackingTestCase
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=import-error, wrong-import-order
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=import-error, wrong-import-order
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
BADGR_SETTINGS = {
'BADGR_API_TOKEN': '12345',
@@ -43,7 +42,7 @@ class BadgrBackendTestCase(ModuleStoreTestCase, EventTrackingTestCase):
"""
Create a course and user to test with.
"""
super(BadgrBackendTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# Need key to be deterministic to test slugs.
self.course = CourseFactory.create(
org='edX', course='course_test', run='test_run', display_name='Badged',
@@ -182,7 +181,7 @@ class BadgrBackendTestCase(ModuleStoreTestCase, EventTrackingTestCase):
'name': 'edx.badge.assertion.created',
'data': {
'user_id': self.user.id,
'course_id': six.text_type(self.course.location.course_key),
'course_id': str(self.course.location.course_key),
'enrollment_mode': 'honor',
'assertion_id': assertion.id,
'badge_name': 'Test Badge',

View File

@@ -6,16 +6,13 @@ Helper functions for the course complete event that was originally included with
import hashlib
import logging
import six
from django.urls import reverse
from django.utils.text import slugify
from django.utils.translation import ugettext_lazy as _
from lms.djangoapps.badges.models import BadgeAssertion, BadgeClass, CourseCompleteImageConfiguration
from lms.djangoapps.badges.utils import requires_badges_enabled, site_prefix
from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=import-error, wrong-import-order
from xmodule.modulestore.django import modulestore
LOGGER = logging.getLogger(__name__)
@@ -35,9 +32,9 @@ def course_slug(course_key, mode):
"""
# Seven digits should be enough to realistically avoid collisions. That's what git services use.
digest = hashlib.sha256(
u"{}{}".format(six.text_type(course_key), six.text_type(mode)).encode('utf-8')
"{}{}".format(str(course_key), str(mode)).encode('utf-8')
).hexdigest()[:7]
base_slug = slugify(six.text_type(course_key) + u'_{}_'.format(mode))[:248]
base_slug = slugify(str(course_key) + f'_{mode}_')[:248]
return base_slug + digest
@@ -46,14 +43,14 @@ def badge_description(course, mode):
Returns a description for the earned badge.
"""
if course.end:
return _(u'Completed the course "{course_name}" ({course_mode}, {start_date} - {end_date})').format(
return _('Completed the course "{course_name}" ({course_mode}, {start_date} - {end_date})').format(
start_date=course.start.date(),
end_date=course.end.date(),
course_name=course.display_name,
course_mode=mode,
)
else:
return _(u'Completed the course "{course_name}" ({course_mode})').format(
return _('Completed the course "{course_name}" ({course_mode})').format(
course_name=course.display_name,
course_mode=mode,
)
@@ -64,7 +61,7 @@ def evidence_url(user_id, course_key):
Generates a URL to the user's Certificate HTML view, along with a GET variable that will signal the evidence visit
event.
"""
course_id = six.text_type(course_key)
course_id = str(course_key)
# avoid circular import problems
from lms.djangoapps.certificates.models import GeneratedCertificate
cert = GeneratedCertificate.eligible_certificates.get(user__id=int(user_id), course_id=course_id)
@@ -76,8 +73,8 @@ def criteria(course_key):
"""
Constructs the 'criteria' URL from the course about page.
"""
about_path = reverse('about_course', kwargs={'course_id': six.text_type(course_key)})
return u'{}{}'.format(site_prefix(), about_path)
about_path = reverse('about_course', kwargs={'course_id': str(course_key)})
return f'{site_prefix()}{about_path}'
def get_completion_badge(course_id, user):

View File

@@ -4,11 +4,11 @@ Tests for the course completion helper functions.
from datetime import datetime
from uuid import uuid4
from common.djangoapps.student.tests.factories import UserFactory
from lms.djangoapps.badges.events import course_complete
from lms.djangoapps.certificates.models import GeneratedCertificate
from common.djangoapps.student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=import-error, wrong-import-order
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=import-error, wrong-import-order
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
class CourseCompleteTestCase(ModuleStoreTestCase):
@@ -17,7 +17,7 @@ class CourseCompleteTestCase(ModuleStoreTestCase):
"""
def setUp(self):
super(CourseCompleteTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# Need key to be deterministic to test slugs.
self.course = CourseFactory.create(
org='edX', course='course_test', run='test_run', display_name='Badged',

View File

@@ -3,19 +3,18 @@ Tests the course meta badging events
"""
import six
from six.moves import range, zip
from ddt import data, ddt, unpack # lint-amnesty, pylint: disable=import-error
from unittest.mock import patch
from ddt import data, ddt, unpack
from django.conf import settings
from django.test.utils import override_settings
from mock import patch # lint-amnesty, pylint: disable=import-error
from lms.djangoapps.badges.tests.factories import CourseEventBadgesConfigurationFactory, RandomBadgeClassFactory
from lms.djangoapps.certificates.models import CertificateStatuses, GeneratedCertificate
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=import-error, wrong-import-order
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=import-error, wrong-import-order
from lms.djangoapps.badges.tests.factories import CourseEventBadgesConfigurationFactory, RandomBadgeClassFactory
from lms.djangoapps.certificates.models import CertificateStatuses, GeneratedCertificate
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@ddt
@@ -27,7 +26,7 @@ class CourseEnrollmentBadgeTest(ModuleStoreTestCase):
"""
def setUp(self):
super(CourseEnrollmentBadgeTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.badge_classes = [
RandomBadgeClassFactory(
issuing_component='openedx__course'
@@ -77,7 +76,7 @@ class CourseCompletionBadgeTest(ModuleStoreTestCase):
"""
def setUp(self):
super(CourseCompletionBadgeTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.badge_classes = [
RandomBadgeClassFactory(
issuing_component='openedx__course'
@@ -131,7 +130,7 @@ class CourseGroupBadgeTest(ModuleStoreTestCase):
"""
def setUp(self):
super(CourseGroupBadgeTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.badge_classes = [
RandomBadgeClassFactory(
issuing_component='openedx__course'
@@ -145,8 +144,8 @@ class CourseGroupBadgeTest(ModuleStoreTestCase):
]
self.courses = []
for _badge_class in self.badge_classes:
self.courses.append([CourseFactory().location.course_key for _i in range(3)])
lines = [badge_class.slug + ',' + ','.join([six.text_type(course_key) for course_key in keys])
self.courses.append([CourseFactory().location.course_key for _i in range(3)]) # lint-amnesty, pylint: disable=no-member
lines = [badge_class.slug + ',' + ','.join([str(course_key) for course_key in keys])
for badge_class, keys in zip(self.badge_classes, self.courses)]
config = '\r'.join(lines)
self.config = CourseEventBadgesConfigurationFactory(course_groups=config)

View File

@@ -5,10 +5,10 @@ Badges related signal handlers.
from django.dispatch import receiver
from lms.djangoapps.badges.events.course_meta import award_enrollment_badge
from lms.djangoapps.badges.utils import badges_enabled
from common.djangoapps.student.models import EnrollStatusChange
from common.djangoapps.student.signals import ENROLL_STATUS_CHANGE
from lms.djangoapps.badges.events.course_meta import award_enrollment_badge
from lms.djangoapps.badges.utils import badges_enabled
@receiver(ENROLL_STATUS_CHANGE)

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
import django.utils.timezone
import jsonfield.fields
from django.conf import settings
@@ -35,13 +32,13 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('slug', models.SlugField(max_length=255, validators=[badges_models.validate_lowercase])),
('issuing_component', models.SlugField(default=u'', blank=True, validators=[badges_models.validate_lowercase])),
('issuing_component', models.SlugField(default='', blank=True, validators=[badges_models.validate_lowercase])),
('display_name', models.CharField(max_length=255)),
('course_id', CourseKeyField(default=None, max_length=255, blank=True)),
('description', models.TextField()),
('criteria', models.TextField()),
('mode', models.CharField(default=u'', max_length=100, blank=True)),
('image', models.ImageField(upload_to=u'badge_classes', validators=[badges_models.validate_badge_image])),
('mode', models.CharField(default='', max_length=100, blank=True)),
('image', models.ImageField(upload_to='badge_classes', validators=[badges_models.validate_badge_image])),
],
),
migrations.CreateModel(
@@ -49,13 +46,13 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('mode', models.CharField(help_text='The course mode for this badge image. For example, "verified" or "honor".', unique=True, max_length=125)),
('icon', models.ImageField(help_text='Badge images must be square PNG files. The file size should be under 250KB.', upload_to=u'course_complete_badges', validators=[badges_models.validate_badge_image])),
('icon', models.ImageField(help_text='Badge images must be square PNG files. The file size should be under 250KB.', upload_to='course_complete_badges', validators=[badges_models.validate_badge_image])),
('default', models.BooleanField(default=False, help_text='Set this value to True if you want this image to be the default image for any course modes that do not have a specified badge image. You can have only one default image.')),
],
),
migrations.AlterUniqueTogether(
name='badgeclass',
unique_together=set([('slug', 'issuing_component', 'course_id')]),
unique_together={('slug', 'issuing_component', 'course_id')},
),
migrations.AddField(
model_name='badgeassertion',

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
import json
import os
import time
@@ -45,7 +42,7 @@ def forwards(apps, schema_editor):
badge_class.image.name = icon.name
badge_class.save()
classes[(badge.course_id, badge.mode)] = badge_class
if isinstance(badge.data, six.string_types):
if isinstance(badge.data, str):
data = badge.data
else:
data = json.dumps(badge.data)
@@ -83,7 +80,7 @@ def backwards(apps, schema_editor):
if not badge.badge_class.mode:
# Can't preserve old badges without modes.
continue
if isinstance(badge.data, six.string_types):
if isinstance(badge.data, str):
data = badge.data
else:
data = json.dumps(badge.data)

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
@@ -20,9 +17,9 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
('courses_completed', models.TextField(default=u'', help_text="On each line, put the number of completed courses to award a badge for, a comma, and the slug of a badge class you have created that has the issuing component 'openedx__course'. For example: 3,enrolled_3_courses", blank=True)),
('courses_enrolled', models.TextField(default=u'', help_text="On each line, put the number of enrolled courses to award a badge for, a comma, and the slug of a badge class you have created that has the issuing component 'openedx__course'. For example: 3,enrolled_3_courses", blank=True)),
('course_groups', models.TextField(default=u'', help_text="Each line is a comma-separated list. The first item in each line is the slug of a badge class you have created that has an issuing component of 'openedx__course'. The remaining items in each line are the course keys the learner needs to complete to be awarded the badge. For example: slug_for_compsci_courses_group_badge,course-v1:CompSci+Course+First,course-v1:CompsSci+Course+Second", blank=True)),
('courses_completed', models.TextField(default='', help_text="On each line, put the number of completed courses to award a badge for, a comma, and the slug of a badge class you have created that has the issuing component 'openedx__course'. For example: 3,enrolled_3_courses", blank=True)),
('courses_enrolled', models.TextField(default='', help_text="On each line, put the number of enrolled courses to award a badge for, a comma, and the slug of a badge class you have created that has the issuing component 'openedx__course'. For example: 3,enrolled_3_courses", blank=True)),
('course_groups', models.TextField(default='', help_text="Each line is a comma-separated list. The first item in each line is the slug of a badge class you have created that has an issuing component of 'openedx__course'. The remaining items in each line are the course keys the learner needs to complete to be awarded the badge. For example: slug_for_compsci_courses_group_badge,course-v1:CompSci+Course+First,course-v1:CompsSci+Course+Second", blank=True)),
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
],
),

View File

@@ -5,24 +5,23 @@ Database models for the badges app
from importlib import import_module
import six
from config_models.models import ConfigurationModel # lint-amnesty, pylint: disable=import-error
from config_models.models import ConfigurationModel
from django.conf import settings
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.encoding import python_2_unicode_compatible # lint-amnesty, pylint: disable=no-name-in-module
from django.utils.translation import ugettext_lazy as _
from jsonfield import JSONField # lint-amnesty, pylint: disable=import-error
from jsonfield import JSONField
from lazy import lazy # lint-amnesty, pylint: disable=no-name-in-module
from model_utils.models import TimeStampedModel # lint-amnesty, pylint: disable=import-error
from opaque_keys import InvalidKeyError # lint-amnesty, pylint: disable=import-error
from opaque_keys.edx.django.models import CourseKeyField # lint-amnesty, pylint: disable=import-error
from opaque_keys.edx.keys import CourseKey # lint-amnesty, pylint: disable=import-error
from model_utils.models import TimeStampedModel
from opaque_keys import InvalidKeyError
from opaque_keys.edx.django.models import CourseKeyField
from opaque_keys.edx.keys import CourseKey
from lms.djangoapps.badges.utils import deserialize_count_specs
from openedx.core.djangolib.markup import HTML, Text
from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=import-error, wrong-import-order
from xmodule.modulestore.django import modulestore
def validate_badge_image(image):
@@ -30,9 +29,9 @@ def validate_badge_image(image):
Validates that a particular image is small enough to be a badge and square.
"""
if image.width != image.height:
raise ValidationError(_(u"The badge image must be square."))
raise ValidationError(_("The badge image must be square."))
if not image.size < (250 * 1024):
raise ValidationError(_(u"The badge image file size must be less than 250KB."))
raise ValidationError(_("The badge image file size must be less than 250KB."))
def validate_lowercase(string):
@@ -40,7 +39,7 @@ def validate_lowercase(string):
Validates that a string is lowercase.
"""
if not string.islower():
raise ValidationError(_(u"This value must be all lowercase."))
raise ValidationError(_("This value must be all lowercase."))
class CourseBadgesDisabledError(Exception):
@@ -57,17 +56,17 @@ class BadgeClass(models.Model):
.. no_pii:
"""
slug = models.SlugField(max_length=255, validators=[validate_lowercase])
issuing_component = models.SlugField(max_length=50, default=u'', blank=True, validators=[validate_lowercase])
issuing_component = models.SlugField(max_length=50, default='', blank=True, validators=[validate_lowercase])
display_name = models.CharField(max_length=255)
course_id = CourseKeyField(max_length=255, blank=True, default=None)
description = models.TextField()
criteria = models.TextField()
# Mode a badge was awarded for. Included for legacy/migration purposes.
mode = models.CharField(max_length=100, default=u'', blank=True)
image = models.ImageField(upload_to=u'badge_classes', validators=[validate_badge_image])
mode = models.CharField(max_length=100, default='', blank=True)
image = models.ImageField(upload_to='badge_classes', validators=[validate_badge_image])
def __str__(self): # lint-amnesty, pylint: disable=invalid-str-returned
return HTML(u"<Badge '{slug}' for '{issuing_component}'>").format(
return HTML("<Badge '{slug}' for '{issuing_component}'>").format(
slug=HTML(self.slug), issuing_component=HTML(self.issuing_component)
)
@@ -138,9 +137,9 @@ class BadgeClass(models.Model):
"""
self.slug = self.slug and self.slug.lower()
self.issuing_component = self.issuing_component and self.issuing_component.lower()
super(BadgeClass, self).save(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments
super().save(**kwargs)
class Meta(object):
class Meta:
app_label = "badges"
unique_together = (('slug', 'issuing_component', 'course_id'),)
verbose_name_plural = "Badge Classes"
@@ -161,7 +160,7 @@ class BadgeAssertion(TimeStampedModel):
assertion_url = models.URLField()
def __str__(self): # lint-amnesty, pylint: disable=invalid-str-returned
return HTML(u"<{username} Badge Assertion for {slug} for {issuing_component}").format(
return HTML("<{username} Badge Assertion for {slug} for {issuing_component}").format(
username=HTML(self.user.username),
slug=HTML(self.badge_class.slug),
issuing_component=HTML(self.badge_class.issuing_component),
@@ -176,7 +175,7 @@ class BadgeAssertion(TimeStampedModel):
return cls.objects.filter(user=user, badge_class__course_id=course_id)
return cls.objects.filter(user=user)
class Meta(object):
class Meta:
app_label = "badges"
@@ -193,29 +192,29 @@ class CourseCompleteImageConfiguration(models.Model):
"""
mode = models.CharField(
max_length=125,
help_text=_(u'The course mode for this badge image. For example, "verified" or "honor".'),
help_text=_('The course mode for this badge image. For example, "verified" or "honor".'),
unique=True,
)
icon = models.ImageField(
# Actual max is 256KB, but need overhead for badge baking. This should be more than enough.
help_text=_(
u"Badge images must be square PNG files. The file size should be under 250KB."
"Badge images must be square PNG files. The file size should be under 250KB."
),
upload_to=u'course_complete_badges',
upload_to='course_complete_badges',
validators=[validate_badge_image]
)
default = models.BooleanField(
help_text=_(
u"Set this value to True if you want this image to be the default image for any course modes "
u"that do not have a specified badge image. You can have only one default image."
"Set this value to True if you want this image to be the default image for any course modes "
"that do not have a specified badge image. You can have only one default image."
),
default=False,
)
def __str__(self): # lint-amnesty, pylint: disable=invalid-str-returned
return HTML(u"<CourseCompleteImageConfiguration for '{mode}'{default}>").format(
return HTML("<CourseCompleteImageConfiguration for '{mode}'{default}>").format(
mode=HTML(self.mode),
default=HTML(u" (default)") if self.default else HTML(u'')
default=HTML(" (default)") if self.default else HTML('')
)
def clean(self):
@@ -223,7 +222,7 @@ class CourseCompleteImageConfiguration(models.Model):
Make sure there's not more than one default.
"""
if self.default and CourseCompleteImageConfiguration.objects.filter(default=True).exclude(id=self.id):
raise ValidationError(_(u"There can be only one default image."))
raise ValidationError(_("There can be only one default image."))
@classmethod
def image_for_mode(cls, mode):
@@ -236,7 +235,7 @@ class CourseCompleteImageConfiguration(models.Model):
# Fall back to default, if there is one.
return cls.objects.get(default=True).icon
class Meta(object):
class Meta:
app_label = "badges"
@@ -249,34 +248,34 @@ class CourseEventBadgesConfiguration(ConfigurationModel):
.. no_pii:
"""
courses_completed = models.TextField(
blank=True, default=u'',
blank=True, default='',
help_text=_(
u"On each line, put the number of completed courses to award a badge for, a comma, and the slug of a "
u"badge class you have created that has the issuing component 'openedx__course'. "
u"For example: 3,enrolled_3_courses"
"On each line, put the number of completed courses to award a badge for, a comma, and the slug of a "
"badge class you have created that has the issuing component 'openedx__course'. "
"For example: 3,enrolled_3_courses"
)
)
courses_enrolled = models.TextField(
blank=True, default=u'',
blank=True, default='',
help_text=_(
u"On each line, put the number of enrolled courses to award a badge for, a comma, and the slug of a "
u"badge class you have created that has the issuing component 'openedx__course'. "
u"For example: 3,enrolled_3_courses"
"On each line, put the number of enrolled courses to award a badge for, a comma, and the slug of a "
"badge class you have created that has the issuing component 'openedx__course'. "
"For example: 3,enrolled_3_courses"
)
)
course_groups = models.TextField(
blank=True, default=u'',
blank=True, default='',
help_text=_(
u"Each line is a comma-separated list. The first item in each line is the slug of a badge class you "
u"have created that has an issuing component of 'openedx__course'. The remaining items in each line are "
u"the course keys the learner needs to complete to be awarded the badge. For example: "
u"slug_for_compsci_courses_group_badge,course-v1:CompSci+Course+First,course-v1:CompsSci+Course+Second"
"Each line is a comma-separated list. The first item in each line is the slug of a badge class you "
"have created that has an issuing component of 'openedx__course'. The remaining items in each line are "
"the course keys the learner needs to complete to be awarded the badge. For example: "
"slug_for_compsci_courses_group_badge,course-v1:CompSci+Course+First,course-v1:CompsSci+Course+Second"
)
)
def __str__(self): # lint-amnesty, pylint: disable=invalid-str-returned
return HTML(u"<CourseEventBadgesConfiguration ({})>").format(
Text(u"Enabled") if self.enabled else Text(u"Disabled")
return HTML("<CourseEventBadgesConfiguration ({})>").format(
Text("Enabled") if self.enabled else Text("Disabled")
)
@property
@@ -314,28 +313,28 @@ class CourseEventBadgesConfiguration(ConfigurationModel):
Verify the settings are parseable.
"""
errors = {}
error_message = _(u"Please check the syntax of your entry.")
error_message = _("Please check the syntax of your entry.")
if 'courses_completed' not in exclude:
try:
self.completed_settings
except (ValueError, InvalidKeyError):
errors['courses_completed'] = [six.text_type(error_message)]
errors['courses_completed'] = [str(error_message)]
if 'courses_enrolled' not in exclude:
try:
self.enrolled_settings
except (ValueError, InvalidKeyError):
errors['courses_enrolled'] = [six.text_type(error_message)]
errors['courses_enrolled'] = [str(error_message)]
if 'course_groups' not in exclude:
store = modulestore()
try:
for key_list in self.course_group_settings.values():
for course_key in key_list:
if not store.get_course(course_key):
ValueError(u"The course {course_key} does not exist.".format(course_key=course_key))
ValueError(f"The course {course_key} does not exist.")
except (ValueError, InvalidKeyError):
errors['course_groups'] = [six.text_type(error_message)]
errors['course_groups'] = [str(error_message)]
if errors:
raise ValidationError(errors)
class Meta(object):
class Meta:
app_label = "badges"

View File

@@ -6,7 +6,7 @@ Badging service for XBlocks
from lms.djangoapps.badges.models import BadgeClass
class BadgingService(object):
class BadgingService:
"""
A class that provides functions for managing badges which XBlocks can use.

View File

@@ -5,13 +5,18 @@ Factories for Badge tests
from random import random
import factory # lint-amnesty, pylint: disable=import-error
import factory
from django.core.files.base import ContentFile
from factory import DjangoModelFactory # lint-amnesty, pylint: disable=import-error
from factory.django import ImageField # lint-amnesty, pylint: disable=import-error
from factory import DjangoModelFactory
from factory.django import ImageField
from lms.djangoapps.badges.models import BadgeAssertion, BadgeClass, CourseCompleteImageConfiguration, CourseEventBadgesConfiguration # lint-amnesty, pylint: disable=line-too-long
from common.djangoapps.student.tests.factories import UserFactory
from lms.djangoapps.badges.models import ( # lint-amnesty, pylint: disable=line-too-long
BadgeAssertion,
BadgeClass,
CourseCompleteImageConfiguration,
CourseEventBadgesConfiguration
)
def generate_dummy_image(_unused):
@@ -29,7 +34,7 @@ class CourseCompleteImageConfigurationFactory(DjangoModelFactory):
"""
Factory for BadgeImageConfigurations
"""
class Meta(object):
class Meta:
model = CourseCompleteImageConfiguration
mode = 'honor'
@@ -40,7 +45,7 @@ class BadgeClassFactory(DjangoModelFactory):
"""
Factory for BadgeClass
"""
class Meta(object):
class Meta:
model = BadgeClass
slug = 'test_slug'
@@ -63,7 +68,7 @@ class BadgeAssertionFactory(DjangoModelFactory):
"""
Factory for BadgeAssertions
"""
class Meta(object):
class Meta:
model = BadgeAssertion
user = factory.SubFactory(UserFactory)
@@ -77,7 +82,7 @@ class CourseEventBadgesConfigurationFactory(DjangoModelFactory):
"""
Factory for CourseEventsBadgesConfiguration
"""
class Meta(object):
class Meta:
model = CourseEventBadgesConfiguration
enabled = True

View File

@@ -2,6 +2,9 @@
Tests for the Badges app models.
"""
from unittest.mock import Mock, patch
import pytest
from django.core.exceptions import ValidationError
from django.core.files.images import ImageFile
@@ -9,10 +12,9 @@ from django.core.files.storage import default_storage
from django.db.utils import IntegrityError
from django.test import TestCase
from django.test.utils import override_settings
from mock import Mock, patch # lint-amnesty, pylint: disable=import-error
from path import Path # lint-amnesty, pylint: disable=import-error
from six.moves import range
from path import Path
from common.djangoapps.student.tests.factories import UserFactory
from lms.djangoapps.badges.models import (
BadgeAssertion,
BadgeClass,
@@ -22,9 +24,8 @@ from lms.djangoapps.badges.models import (
)
from lms.djangoapps.badges.tests.factories import BadgeAssertionFactory, BadgeClassFactory, RandomBadgeClassFactory
from lms.djangoapps.certificates.tests.test_models import TEST_DATA_ROOT
from common.djangoapps.student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=import-error, wrong-import-order
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=import-error, wrong-import-order
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
def get_image(name):
@@ -60,7 +61,7 @@ class BadgeImageConfigurationTest(TestCase):
.full_clean)
class DummyBackend(object):
class DummyBackend:
"""
Dummy badge backend, used for testing.
"""
@@ -74,7 +75,7 @@ class BadgeClassTest(ModuleStoreTestCase):
"""
def setUp(self):
super(BadgeClassTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.addCleanup(self.cleanup_uploads)
def cleanup_uploads(self):

View File

@@ -10,8 +10,8 @@ def site_prefix():
"""
Get the prefix for the site URL-- protocol and server name.
"""
scheme = u"https" if settings.HTTPS == "on" else u"http"
return u'{}://{}'.format(scheme, settings.SITE_NAME)
scheme = "https" if settings.HTTPS == "on" else "http"
return f'{scheme}://{settings.SITE_NAME}'
def requires_badges_enabled(function):