Addresses INCR-195 - runs modernise /student/tests (#20419)
This commit is contained in:
committed by
Michael Youngstrom
parent
3dcdf96faf
commit
6363090144
@@ -1,9 +1,12 @@
|
||||
"""Provides factories for student models."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import uuid4
|
||||
|
||||
import factory
|
||||
import six
|
||||
from django.contrib.auth.models import AnonymousUser, Group, Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from factory.django import DjangoModelFactory
|
||||
@@ -102,7 +105,7 @@ class UserFactory(DjangoModelFactory):
|
||||
if extracted is None:
|
||||
return
|
||||
|
||||
if isinstance(extracted, basestring):
|
||||
if isinstance(extracted, six.string_types):
|
||||
extracted = [extracted]
|
||||
|
||||
for group_name in extracted:
|
||||
@@ -140,7 +143,7 @@ class CourseEnrollmentFactory(DjangoModelFactory):
|
||||
course_id = kwargs.get('course_id')
|
||||
course_overview = None
|
||||
if course_id is not None:
|
||||
if isinstance(course_id, basestring):
|
||||
if isinstance(course_id, six.string_types):
|
||||
course_id = CourseKey.from_string(course_id)
|
||||
course_kwargs.setdefault('id', course_id)
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
"""Tests for account activation"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import unittest
|
||||
from uuid import uuid4
|
||||
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from django.test import TestCase, override_settings
|
||||
from django.urls import reverse
|
||||
from mock import patch
|
||||
|
||||
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
"""
|
||||
Tests student admin.py
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import datetime
|
||||
|
||||
import ddt
|
||||
import six
|
||||
from django.conf import settings
|
||||
from django.contrib.admin.sites import AdminSite
|
||||
from django.contrib.auth.models import User
|
||||
from django.conf import settings
|
||||
from django.forms import ValidationError
|
||||
from django.urls import reverse
|
||||
from django.test import TestCase, override_settings
|
||||
from django.urls import reverse
|
||||
from mock import Mock
|
||||
|
||||
from student.admin import COURSE_ENROLLMENT_ADMIN_SWITCH, UserAdmin
|
||||
@@ -34,7 +38,7 @@ class AdminCourseRolesPageTest(SharedModuleStoreTestCase):
|
||||
def test_save_valid_data(self):
|
||||
|
||||
data = {
|
||||
'course_id': unicode(self.course.id),
|
||||
'course_id': six.text_type(self.course.id),
|
||||
'role': 'finance_admin',
|
||||
'org': 'edx',
|
||||
'email': self.user.email
|
||||
@@ -50,7 +54,7 @@ class AdminCourseRolesPageTest(SharedModuleStoreTestCase):
|
||||
self.assertContains(response, 'Select course access role to change')
|
||||
self.assertContains(response, 'Add course access role')
|
||||
self.assertContains(response, 'finance_admin')
|
||||
self.assertContains(response, unicode(self.course.id))
|
||||
self.assertContains(response, six.text_type(self.course.id))
|
||||
self.assertContains(response, '1 course access role')
|
||||
|
||||
#try adding with same information raise error.
|
||||
@@ -62,7 +66,7 @@ class AdminCourseRolesPageTest(SharedModuleStoreTestCase):
|
||||
data = {
|
||||
'role': 'staff',
|
||||
'email': self.user.email,
|
||||
'course_id': unicode(self.course.id)
|
||||
'course_id': six.text_type(self.course.id)
|
||||
}
|
||||
|
||||
self.client.login(username=self.user.username, password='test')
|
||||
@@ -114,7 +118,7 @@ class AdminCourseRolesPageTest(SharedModuleStoreTestCase):
|
||||
|
||||
def test_save_with_invalid_course(self):
|
||||
|
||||
course = unicode('no/edx/course')
|
||||
course = six.text_type('no/edx/course')
|
||||
email = "invalid@email.com"
|
||||
data = {
|
||||
'course_id': course,
|
||||
@@ -144,7 +148,7 @@ class AdminCourseRolesPageTest(SharedModuleStoreTestCase):
|
||||
def test_save_valid_course_invalid_org(self):
|
||||
|
||||
data = {
|
||||
'course_id': unicode(self.course.id),
|
||||
'course_id': six.text_type(self.course.id),
|
||||
'role': 'finance_admin',
|
||||
'org': 'edxxx',
|
||||
'email': self.user.email
|
||||
@@ -268,8 +272,8 @@ class CourseEnrollmentAdminTest(SharedModuleStoreTestCase):
|
||||
# is_active will change from True to False
|
||||
self.assertTrue(self.course_enrollment.is_active)
|
||||
data = {
|
||||
'user': unicode(self.course_enrollment.user.id),
|
||||
'course': unicode(self.course_enrollment.course.id),
|
||||
'user': six.text_type(self.course_enrollment.user.id),
|
||||
'course': six.text_type(self.course_enrollment.course.id),
|
||||
'is_active': 'false',
|
||||
'mode': self.course_enrollment.mode,
|
||||
}
|
||||
@@ -289,7 +293,7 @@ class CourseEnrollmentAdminTest(SharedModuleStoreTestCase):
|
||||
Send an invalid course ID instead of "org.0/course_0/Run_0" when saving, and verify that it fails.
|
||||
"""
|
||||
data = {
|
||||
'user': unicode(self.course_enrollment.user.id),
|
||||
'user': six.text_type(self.course_enrollment.user.id),
|
||||
'course': 'invalid-course-id',
|
||||
'is_active': 'true',
|
||||
'mode': self.course_enrollment.mode,
|
||||
@@ -348,7 +352,7 @@ class LoginFailuresAdminTest(TestCase):
|
||||
url,
|
||||
data={
|
||||
'action': 'unlock_student_accounts',
|
||||
'_selected_action': [unicode(o.pk) for o in LoginFailures.objects.all()]
|
||||
'_selected_action': [six.text_type(o.pk) for o in LoginFailures.objects.all()]
|
||||
},
|
||||
follow=True
|
||||
)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"""
|
||||
Tests authz.py
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import mock
|
||||
from ccx_keys.locator import CCXLocator
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
|
||||
@@ -4,6 +4,8 @@ that bulk email is always disabled for non-Mongo backed courses, regardless
|
||||
of email feature flag, and that the view is conditionally available when
|
||||
Course Auth is turned on.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import unittest
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
"""Tests for display of certificates on the student dashboard. """
|
||||
|
||||
import unittest
|
||||
from __future__ import absolute_import
|
||||
|
||||
import datetime
|
||||
import unittest
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from django.test.utils import override_settings
|
||||
from django.urls import reverse
|
||||
from mock import patch
|
||||
from pytz import UTC
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from lms.djangoapps.certificates.api import get_certificate_url # pylint: disable=import-error
|
||||
from lms.djangoapps.certificates.models import CertificateStatuses # pylint: disable=import-error
|
||||
from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFactory # pylint: disable=import-error
|
||||
from course_modes.models import CourseMode
|
||||
from student.models import LinkedInAddToProfileConfiguration
|
||||
from student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
|
||||
# pylint: disable=no-member
|
||||
|
||||
PAST_DATE = datetime.datetime.now(UTC) - datetime.timedelta(days=2)
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
"""
|
||||
Test for user creation from sites with configuration overrides.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import json
|
||||
|
||||
import mock
|
||||
from django.contrib.auth.models import User
|
||||
from django.urls import reverse
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from student.models import UserSignupSource
|
||||
|
||||
@@ -62,14 +64,14 @@ class TestSite(TestCase):
|
||||
"honor_code": "true",
|
||||
"terms_of_service": "true",
|
||||
}
|
||||
self.extended_params = dict(self.params.items() + {
|
||||
self.extended_params = dict(list(self.params.items()) + list({
|
||||
"address1": "foo",
|
||||
"city": "foo",
|
||||
"state": "foo",
|
||||
"country": "foo",
|
||||
"company": "foo",
|
||||
"title": "foo"
|
||||
}.items())
|
||||
}.items()))
|
||||
|
||||
@mock.patch("openedx.core.djangoapps.site_configuration.helpers.get_value", fake_site_name)
|
||||
def test_user_signup_source(self):
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
Unit tests for getting the list of courses for a user through iterating all courses and
|
||||
by reversing group name formats.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
import six
|
||||
from django.conf import settings
|
||||
from django.test.client import Client
|
||||
from milestones.tests.utils import MilestonesTestCaseMixin
|
||||
@@ -133,8 +136,8 @@ class TestCourseListing(ModuleStoreTestCase, MilestonesTestCaseMixin):
|
||||
self._create_course_with_access_groups(pre_requisite_course_location2)
|
||||
# create a course with pre_requisite_courses
|
||||
pre_requisite_courses = [
|
||||
unicode(pre_requisite_course_location),
|
||||
unicode(pre_requisite_course_location2),
|
||||
six.text_type(pre_requisite_course_location),
|
||||
six.text_type(pre_requisite_course_location2),
|
||||
]
|
||||
course_location = self.store.make_course_key('Org1', 'Course1', 'Run1')
|
||||
self._create_course_with_access_groups(course_location, {
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
"""
|
||||
Tests for credit courses on the student dashboard.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import datetime
|
||||
import unittest
|
||||
|
||||
import ddt
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from django.test.utils import override_settings
|
||||
from django.urls import reverse
|
||||
from mock import patch
|
||||
|
||||
from openedx.core.djangoapps.credit import api as credit_api
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
# coding=utf-8
|
||||
import ddt
|
||||
from __future__ import absolute_import
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
import ddt
|
||||
import six
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core import mail
|
||||
from django.urls import reverse
|
||||
from django.db import transaction
|
||||
from django.http import HttpResponse
|
||||
from django.test import override_settings, TransactionTestCase
|
||||
from django.test import TransactionTestCase, override_settings
|
||||
from django.test.client import RequestFactory
|
||||
from django.urls import reverse
|
||||
from mock import Mock, patch
|
||||
from six import text_type
|
||||
|
||||
from edxmako.shortcuts import render_to_string, marketing_link
|
||||
from edxmako.shortcuts import marketing_link, render_to_string
|
||||
from openedx.core.djangoapps.ace_common.tests.mixins import EmailTemplateTagMixin
|
||||
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
|
||||
from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme
|
||||
from openedx.core.djangoapps.user_api.config.waffle import PREVENT_AUTH_USER_WRITES, SYSTEM_MAINTENANCE_MSG, waffle
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, CacheIsolationMixin
|
||||
from student.models import (
|
||||
PendingEmailChange,
|
||||
Registration,
|
||||
UserProfile,
|
||||
)
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationMixin, CacheIsolationTestCase
|
||||
from openedx.core.lib.request_utils import safe_get_host
|
||||
from student.models import PendingEmailChange, Registration, UserProfile
|
||||
from student.tests.factories import PendingEmailChangeFactory, RegistrationFactory, UserFactory
|
||||
from student.views import (
|
||||
SETTING_CHANGE_INITIATED,
|
||||
confirm_email_change,
|
||||
do_email_change_request,
|
||||
generate_activation_email_context,
|
||||
send_reactivation_email_for_user,
|
||||
validate_new_email
|
||||
)
|
||||
from student.views import generate_activation_email_context, send_reactivation_email_for_user
|
||||
from third_party_auth.views import inactive_user_view
|
||||
from util.testing import EventTestMixin
|
||||
|
||||
@@ -49,7 +49,7 @@ def mock_render_to_string(template_name, context):
|
||||
"""
|
||||
Return a string that encodes template_name and context
|
||||
"""
|
||||
return str((template_name, sorted(context.iteritems())))
|
||||
return str((template_name, sorted(six.iteritems(context))))
|
||||
|
||||
|
||||
def mock_render_to_response(template_name, context):
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
"""
|
||||
Tests for student enrollment.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import unittest
|
||||
|
||||
import ddt
|
||||
import six
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from mock import patch
|
||||
@@ -12,10 +15,10 @@ from course_modes.models import CourseMode
|
||||
from course_modes.tests.factories import CourseModeFactory
|
||||
from openedx.core.djangoapps.embargo.test_utils import restrict_course
|
||||
from student.models import (
|
||||
SCORE_RECALCULATION_DELAY_ON_ENROLLMENT_UPDATE,
|
||||
CourseEnrollment,
|
||||
CourseFullError,
|
||||
EnrollmentClosedError,
|
||||
SCORE_RECALCULATION_DELAY_ON_ENROLLMENT_UPDATE,
|
||||
EnrollmentClosedError
|
||||
)
|
||||
from student.roles import CourseInstructorRole, CourseStaffRole
|
||||
from student.tests.factories import CourseEnrollmentAllowedFactory, UserFactory
|
||||
@@ -51,7 +54,7 @@ class EnrollmentTest(UrlResetMixin, SharedModuleStoreTestCase):
|
||||
self.course_limited.max_student_enrollments_allowed = 1
|
||||
self.store.update_item(self.course_limited, self.user.id)
|
||||
self.urls = [
|
||||
reverse('course_modes_choose', kwargs={'course_id': unicode(self.course.id)})
|
||||
reverse('course_modes_choose', kwargs={'course_id': six.text_type(self.course.id)})
|
||||
]
|
||||
|
||||
@ddt.data(
|
||||
@@ -94,7 +97,7 @@ class EnrollmentTest(UrlResetMixin, SharedModuleStoreTestCase):
|
||||
# (otherwise, use an empty string, which the JavaScript client
|
||||
# interprets as a redirect to the dashboard)
|
||||
full_url = (
|
||||
reverse(next_url, kwargs={'course_id': unicode(self.course.id)})
|
||||
reverse(next_url, kwargs={'course_id': six.text_type(self.course.id)})
|
||||
if next_url else next_url
|
||||
)
|
||||
|
||||
@@ -272,7 +275,7 @@ class EnrollmentTest(UrlResetMixin, SharedModuleStoreTestCase):
|
||||
|
||||
"""
|
||||
if course_id is None:
|
||||
course_id = unicode(self.course.id)
|
||||
course_id = six.text_type(self.course.id)
|
||||
|
||||
params = {
|
||||
'enrollment_action': action,
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
"""
|
||||
Test that various events are fired for models in the student app.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import mock
|
||||
from django.db.utils import IntegrityError
|
||||
from django.test import TestCase
|
||||
from django_countries.fields import Country
|
||||
|
||||
from student.models import CourseEnrollmentAllowed
|
||||
from student.tests.factories import UserFactory, CourseEnrollmentAllowedFactory
|
||||
from student.tests.factories import CourseEnrollmentAllowedFactory, UserFactory
|
||||
from student.tests.tests import UserSettingsEventTestMixin
|
||||
|
||||
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
""" Test Student helpers """
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import logging
|
||||
|
||||
import ddt
|
||||
from django.conf import settings
|
||||
from django.contrib.sessions.middleware import SessionMiddleware
|
||||
from django.urls import reverse
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
from django.test.utils import override_settings
|
||||
from django.urls import reverse
|
||||
from mock import patch
|
||||
from testfixtures import LogCapture
|
||||
|
||||
from student.helpers import get_next_url_for_login_page
|
||||
from openedx.core.djangoapps.site_configuration.tests.test_util import with_site_configuration_context
|
||||
from student.helpers import get_next_url_for_login_page
|
||||
|
||||
LOGGER_NAME = "student.helpers"
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Tests for LinkedIn Add to Profile configuration. """
|
||||
|
||||
from urllib import quote, urlencode
|
||||
from __future__ import absolute_import
|
||||
|
||||
import ddt
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from opaque_keys.edx.locator import CourseLocator
|
||||
from six.moves.urllib.parse import quote, urlencode
|
||||
|
||||
from openedx.core.djangoapps.site_configuration.tests.test_util import with_site_configuration_context
|
||||
from student.models import LinkedInAddToProfileConfiguration
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import json
|
||||
|
||||
from django.urls import reverse
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from openedx.core.djangoapps.user_api.accounts import USERNAME_BAD_LENGTH_MSG
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import datetime
|
||||
import hashlib
|
||||
|
||||
@@ -10,28 +12,28 @@ from django.contrib.auth.models import AnonymousUser
|
||||
from django.core.cache import cache
|
||||
from django.db.models import signals
|
||||
from django.db.models.functions import Lower
|
||||
from django.test import TestCase
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from course_modes.tests.factories import CourseModeFactory
|
||||
from courseware.models import DynamicUpgradeDeadlineConfiguration
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.djangoapps.schedules.models import Schedule
|
||||
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from student.models import (
|
||||
ALLOWEDTOENROLL_TO_ENROLLED,
|
||||
AccountRecovery,
|
||||
CourseEnrollment,
|
||||
CourseEnrollmentAllowed,
|
||||
PendingEmailChange,
|
||||
ManualEnrollmentAudit,
|
||||
ALLOWEDTOENROLL_TO_ENROLLED,
|
||||
PendingNameChange,
|
||||
AccountRecovery
|
||||
PendingEmailChange,
|
||||
PendingNameChange
|
||||
)
|
||||
from student.tests.factories import CourseEnrollmentFactory, UserFactory, AccountRecoveryFactory
|
||||
from student.tests.factories import AccountRecoveryFactory, CourseEnrollmentFactory, UserFactory
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Unit tests for parental controls."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import datetime
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
"""
|
||||
This test file will verify proper password policy enforcement, which is an option feature
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import json
|
||||
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.urls import reverse
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
from django.test.utils import override_settings
|
||||
from django.urls import reverse
|
||||
from mock import patch
|
||||
|
||||
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"""
|
||||
Tests for the recently enrolled messaging within the Dashboard.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import datetime
|
||||
import unittest
|
||||
|
||||
@@ -10,6 +12,7 @@ from django.urls import reverse
|
||||
from django.utils.timezone import now
|
||||
from opaque_keys.edx import locator
|
||||
from pytz import UTC
|
||||
from six.moves import range, zip
|
||||
|
||||
from common.test.utils import XssTestMixin
|
||||
from course_modes.tests.factories import CourseModeFactory
|
||||
@@ -100,7 +103,7 @@ class TestRecentEnrollments(ModuleStoreTestCase, XssTestMixin):
|
||||
# Create a number of new enrollments and courses, and force their creation behind
|
||||
# the first enrollment
|
||||
courses = []
|
||||
for idx, seconds_past in zip(range(2, 6), [5, 10, 15, 20]):
|
||||
for idx, seconds_past in zip(list(range(2, 6)), [5, 10, 15, 20]):
|
||||
course_location = locator.CourseLocator(
|
||||
'Org{num}'.format(num=idx),
|
||||
'Course{num}'.format(num=idx),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"""
|
||||
Tests for enrollment refund capabilities.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import logging
|
||||
import unittest
|
||||
from datetime import datetime, timedelta
|
||||
@@ -11,16 +13,17 @@ import pytz
|
||||
# Explicitly import the cache from ConfigurationModel so we can reset it after each test
|
||||
from config_models.models import cache
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from django.test.client import Client
|
||||
from django.test.utils import override_settings
|
||||
from django.urls import reverse
|
||||
from mock import patch
|
||||
from six.moves import range
|
||||
|
||||
from lms.djangoapps.certificates.models import CertificateStatuses, GeneratedCertificate
|
||||
from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFactory
|
||||
# These imports refer to lms djangoapps.
|
||||
# Their testcases are only run under lms.
|
||||
from course_modes.tests.factories import CourseModeFactory
|
||||
from lms.djangoapps.certificates.models import CertificateStatuses, GeneratedCertificate
|
||||
from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFactory
|
||||
from openedx.core.djangoapps.commerce.utils import ECOMMERCE_DATE_FORMAT
|
||||
from student.models import CourseEnrollment
|
||||
from student.tests.factories import UserFactory
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"""
|
||||
Test the various password reset flows
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import json
|
||||
import re
|
||||
import unicodedata
|
||||
@@ -11,21 +13,22 @@ from django.conf import settings
|
||||
from django.contrib.auth.hashers import UNUSABLE_PASSWORD_PREFIX, make_password
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.tokens import default_token_generator
|
||||
from django.core.cache import cache
|
||||
from django.core import mail
|
||||
from django.urls import reverse
|
||||
from django.core.cache import cache
|
||||
from django.test.client import RequestFactory
|
||||
from django.test.utils import override_settings
|
||||
from django.urls import reverse
|
||||
from django.utils.http import int_to_base36
|
||||
from edx_oauth2_provider.tests.factories import AccessTokenFactory, ClientFactory, RefreshTokenFactory
|
||||
from mock import Mock, patch
|
||||
from oauth2_provider import models as dot_models
|
||||
from provider.oauth2 import models as dop_models
|
||||
from six.moves import range
|
||||
|
||||
from openedx.core.djangoapps.oauth_dispatch.tests import factories as dot_factories
|
||||
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
|
||||
from openedx.core.djangoapps.user_api.models import UserRetirementRequest
|
||||
from openedx.core.djangoapps.user_api.config.waffle import PREVENT_AUTH_USER_WRITES, SYSTEM_MAINTENANCE_MSG, waffle
|
||||
from openedx.core.djangoapps.user_api.models import UserRetirementRequest
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
|
||||
from student.tests.factories import UserFactory
|
||||
from student.tests.test_email import mock_render_to_string
|
||||
@@ -105,7 +108,7 @@ class ResetPasswordTests(EventTestMixin, CacheIsolationTestCase):
|
||||
"""
|
||||
cache.clear()
|
||||
|
||||
for i in xrange(30):
|
||||
for i in range(30):
|
||||
good_req = self.request_factory.post('/password_reset/', {
|
||||
'email': 'thisdoesnotexist{0}@foo.com'.format(i)
|
||||
})
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
"""
|
||||
Test user retirement methods
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import json
|
||||
|
||||
import ddt
|
||||
import pytest
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.urls import reverse
|
||||
from django.test import TestCase
|
||||
import pytest
|
||||
from django.urls import reverse
|
||||
|
||||
from student.models import (
|
||||
_get_all_retired_emails_by_email,
|
||||
@@ -18,12 +20,11 @@ from student.models import (
|
||||
get_potentially_retired_user_by_username_and_hash,
|
||||
get_retired_email_by_email,
|
||||
get_retired_username_by_username,
|
||||
is_username_retired,
|
||||
is_email_retired
|
||||
is_email_retired,
|
||||
is_username_retired
|
||||
)
|
||||
from student.tests.factories import UserFactory
|
||||
|
||||
|
||||
# Tell pytest it's ok to user the Django db
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
"""
|
||||
Tests of student.roles
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import ddt
|
||||
import six
|
||||
from django.test import TestCase
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
@@ -69,7 +72,7 @@ class RolesTestCase(TestCase):
|
||||
CourseStaffRole(self.course_key).add_users(self.student)
|
||||
self.assertTrue(
|
||||
CourseStaffRole(self.course_key).has_user(self.student),
|
||||
"Student doesn't have access to {}".format(unicode(self.course_key))
|
||||
"Student doesn't have access to {}".format(six.text_type(self.course_key))
|
||||
)
|
||||
|
||||
# remove access and confirm
|
||||
@@ -90,7 +93,7 @@ class RolesTestCase(TestCase):
|
||||
OrgStaffRole(self.course_key.org).add_users(self.student)
|
||||
self.assertTrue(
|
||||
OrgStaffRole(self.course_key.org).has_user(self.student),
|
||||
"Student doesn't have access to {}".format(unicode(self.course_key.org))
|
||||
"Student doesn't have access to {}".format(six.text_type(self.course_key.org))
|
||||
)
|
||||
|
||||
# remove access and confirm
|
||||
@@ -110,11 +113,11 @@ class RolesTestCase(TestCase):
|
||||
CourseInstructorRole(self.course_key).add_users(self.student)
|
||||
self.assertTrue(
|
||||
OrgInstructorRole(self.course_key.org).has_user(self.student),
|
||||
"Student doesn't have access to {}".format(unicode(self.course_key.org))
|
||||
"Student doesn't have access to {}".format(six.text_type(self.course_key.org))
|
||||
)
|
||||
self.assertTrue(
|
||||
CourseInstructorRole(self.course_key).has_user(self.student),
|
||||
"Student doesn't have access to {}".format(unicode(self.course_key))
|
||||
"Student doesn't have access to {}".format(six.text_type(self.course_key))
|
||||
)
|
||||
|
||||
# remove access and confirm
|
||||
@@ -125,7 +128,7 @@ class RolesTestCase(TestCase):
|
||||
)
|
||||
self.assertTrue(
|
||||
CourseInstructorRole(self.course_key).has_user(self.student),
|
||||
"Student doesn't have access to {}".format(unicode(self.course_key))
|
||||
"Student doesn't have access to {}".format(six.text_type(self.course_key))
|
||||
)
|
||||
|
||||
# ok now keep org role and get rid of course one
|
||||
@@ -137,7 +140,7 @@ class RolesTestCase(TestCase):
|
||||
)
|
||||
self.assertFalse(
|
||||
CourseInstructorRole(self.course_key).has_user(self.student),
|
||||
"Student doesn't have access to {}".format(unicode(self.course_key))
|
||||
"Student doesn't have access to {}".format(six.text_type(self.course_key))
|
||||
)
|
||||
|
||||
def test_get_user_for_role(self):
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
Tests for the Sending activation email celery tasks
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import mock
|
||||
from boto.exception import NoAuthHandlerFound
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from six.moves import range
|
||||
|
||||
from lms.djangoapps.courseware.tests.factories import UserFactory
|
||||
from student.tasks import send_activation_email
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Unit tests for custom UserProfile properties."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import datetime
|
||||
|
||||
import ddt
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
These are tests for disabling and enabling student accounts, and for making sure
|
||||
that students with disabled accounts are unable to access the courseware.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import unittest
|
||||
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from django.test import Client, TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from student.models import UserStanding
|
||||
from student.tests.factories import UserFactory, UserStandingFactory
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
"""Tests for per-course verification status on the dashboard. """
|
||||
from __future__ import absolute_import
|
||||
|
||||
import unittest
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import ddt
|
||||
import six
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from django.test import override_settings
|
||||
from django.urls import reverse
|
||||
from mock import patch
|
||||
from pytz import UTC
|
||||
|
||||
@@ -359,7 +362,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
|
||||
response = self.client.get(self.dashboard_url)
|
||||
|
||||
# Sanity check: verify that the course is on the page
|
||||
self.assertContains(response, unicode(self.course.id))
|
||||
self.assertContains(response, six.text_type(self.course.id))
|
||||
|
||||
# Verify that the correct banner is rendered on the dashboard
|
||||
alt_text = self.BANNER_ALT_MESSAGES.get(status)
|
||||
|
||||
@@ -1,34 +1,38 @@
|
||||
"""
|
||||
Test the student dashboard view.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import itertools
|
||||
import json
|
||||
import re
|
||||
import unittest
|
||||
from datetime import timedelta, datetime
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import ddt
|
||||
from completion.test_utils import submit_completions_for_testing, CompletionWaffleTestMixin
|
||||
import six
|
||||
from completion.test_utils import CompletionWaffleTestMixin, submit_completions_for_testing
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from django.test import RequestFactory, TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.urls import reverse
|
||||
from django.utils.timezone import now
|
||||
from milestones.tests.utils import MilestonesTestCaseMixin
|
||||
from mock import patch
|
||||
from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from pyquery import PyQuery as pq
|
||||
from six.moves import range
|
||||
|
||||
from bulk_email.models import BulkEmailFlag
|
||||
from course_modes.models import CourseMode
|
||||
from entitlements.tests.factories import CourseEntitlementFactory
|
||||
from milestones.tests.utils import MilestonesTestCaseMixin
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from openedx.core.djangoapps.catalog.tests.factories import ProgramFactory
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
|
||||
from openedx.core.djangoapps.site_configuration.tests.test_util import with_site_configuration_context
|
||||
from pyquery import PyQuery as pq
|
||||
from openedx.core.djangoapps.schedules.config import COURSE_UPDATE_WAFFLE_FLAG
|
||||
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
|
||||
from openedx.core.djangoapps.site_configuration.tests.test_util import with_site_configuration_context
|
||||
from openedx.core.djangoapps.user_authn.cookies import _get_user_info_cookie_data
|
||||
from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag
|
||||
from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
|
||||
@@ -37,9 +41,7 @@ from student.helpers import DISABLE_UNENROLL_CERT_STATES
|
||||
from student.models import CourseEnrollment, UserProfile
|
||||
from student.signals import REFUND_ORDER
|
||||
from student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
from util.milestones_helpers import (get_course_milestones,
|
||||
remove_prerequisite_course,
|
||||
set_prerequisite_courses)
|
||||
from util.milestones_helpers import get_course_milestones, remove_prerequisite_course, set_prerequisite_courses
|
||||
from util.testing import UrlResetMixin
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
@@ -269,11 +271,11 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin,
|
||||
org='edx',
|
||||
number='998',
|
||||
display_name='Test Course',
|
||||
pre_requisite_courses=[unicode(self.pre_requisite_course.id)]
|
||||
pre_requisite_courses=[six.text_type(self.pre_requisite_course.id)]
|
||||
)
|
||||
self.course_enrollment = CourseEnrollmentFactory(course_id=self.course.id, user=self.user)
|
||||
|
||||
set_prerequisite_courses(self.course.id, [unicode(self.pre_requisite_course.id)])
|
||||
set_prerequisite_courses(self.course.id, [six.text_type(self.pre_requisite_course.id)])
|
||||
response = self.client.get(reverse('dashboard'))
|
||||
self.assertIn('<div class="prerequisites">', response.content)
|
||||
|
||||
@@ -301,7 +303,7 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin,
|
||||
mock_course_overview.return_value = CourseOverviewFactory.create(start=self.TOMORROW, id=course_key)
|
||||
mock_course_runs.return_value = [
|
||||
{
|
||||
'key': unicode(course_key),
|
||||
'key': six.text_type(course_key),
|
||||
'enrollment_end': str(self.TOMORROW),
|
||||
'pacing_type': 'instructor_paced',
|
||||
'type': 'verified',
|
||||
@@ -309,7 +311,7 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin,
|
||||
}
|
||||
]
|
||||
mock_pseudo_session.return_value = {
|
||||
'key': unicode(course_key),
|
||||
'key': six.text_type(course_key),
|
||||
'type': 'verified'
|
||||
}
|
||||
response = self.client.get(self.path)
|
||||
@@ -320,7 +322,7 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin,
|
||||
|
||||
# If an entitlement has already been redeemed by the user for a course run, do not let the run be selectable
|
||||
enrollment = CourseEnrollmentFactory(
|
||||
user=self.user, course_id=unicode(mock_course_overview.return_value.id), mode=CourseMode.VERIFIED
|
||||
user=self.user, course_id=six.text_type(mock_course_overview.return_value.id), mode=CourseMode.VERIFIED
|
||||
)
|
||||
CourseEntitlementFactory.create(
|
||||
user=self.user, course_uuid=program['courses'][0]['uuid'], enrollment_course_run=enrollment
|
||||
@@ -386,7 +388,7 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin,
|
||||
start=self.TOMORROW, end=self.THREE_YEARS_FROM_NOW, self_paced=True, enrollment_end=self.THREE_YEARS_AGO
|
||||
)
|
||||
mock_course_overview.return_value = mocked_course_overview
|
||||
course_enrollment = CourseEnrollmentFactory(user=self.user, course_id=unicode(mocked_course_overview.id))
|
||||
course_enrollment = CourseEnrollmentFactory(user=self.user, course_id=six.text_type(mocked_course_overview.id))
|
||||
mock_course_runs.return_value = [
|
||||
{
|
||||
'key': str(mocked_course_overview.id),
|
||||
@@ -450,7 +452,7 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin,
|
||||
start=self.TOMORROW, self_paced=True, enrollment_end=self.TOMORROW
|
||||
)
|
||||
mock_course_overview.return_value = mocked_course_overview
|
||||
course_enrollment = CourseEnrollmentFactory(user=self.user, course_id=unicode(mocked_course_overview.id))
|
||||
course_enrollment = CourseEnrollmentFactory(user=self.user, course_id=six.text_type(mocked_course_overview.id))
|
||||
mock_course_runs.return_value = [
|
||||
{
|
||||
'key': str(mocked_course_overview.id),
|
||||
@@ -462,7 +464,7 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin,
|
||||
]
|
||||
entitlement = CourseEntitlementFactory(user=self.user, enrollment_course_run=course_enrollment)
|
||||
program = ProgramFactory()
|
||||
program['courses'][0]['course_runs'] = [{'key': unicode(mocked_course_overview.id)}]
|
||||
program['courses'][0]['course_runs'] = [{'key': six.text_type(mocked_course_overview.id)}]
|
||||
program['courses'][0]['uuid'] = entitlement.course_uuid
|
||||
mock_get_programs.return_value = [program]
|
||||
response = self.client.get(self.path)
|
||||
@@ -485,7 +487,7 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin,
|
||||
start=self.TOMORROW, self_paced=True, enrollment_end=self.TOMORROW
|
||||
)
|
||||
mock_course_overview.return_value = mocked_course_overview
|
||||
course_enrollment = CourseEnrollmentFactory(user=self.user, course_id=unicode(mocked_course_overview.id), created=self.THREE_YEARS_AGO)
|
||||
course_enrollment = CourseEnrollmentFactory(user=self.user, course_id=six.text_type(mocked_course_overview.id), created=self.THREE_YEARS_AGO)
|
||||
mock_course_runs.return_value = [
|
||||
{
|
||||
'key': str(mocked_course_overview.id),
|
||||
@@ -497,7 +499,7 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin,
|
||||
]
|
||||
entitlement = CourseEntitlementFactory(user=self.user, enrollment_course_run=course_enrollment, created=self.THREE_YEARS_AGO)
|
||||
program = ProgramFactory()
|
||||
program['courses'][0]['course_runs'] = [{'key': unicode(mocked_course_overview.id)}]
|
||||
program['courses'][0]['course_runs'] = [{'key': six.text_type(mocked_course_overview.id)}]
|
||||
program['courses'][0]['uuid'] = entitlement.course_uuid
|
||||
mock_get_programs.return_value = [program]
|
||||
response = self.client.get(self.path)
|
||||
@@ -518,7 +520,7 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin,
|
||||
course_enrollment = CourseEnrollmentFactory(user=self.user)
|
||||
entitlement = CourseEntitlementFactory(user=self.user, enrollment_course_run=course_enrollment)
|
||||
course_runs = [{
|
||||
'key': unicode(course_overview.id),
|
||||
'key': six.text_type(course_overview.id),
|
||||
'uuid': entitlement.course_uuid
|
||||
}]
|
||||
mock_get_course_runs.return_value = course_runs
|
||||
@@ -689,9 +691,9 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin,
|
||||
ItemFactory.create(
|
||||
category='video',
|
||||
parent_location=course.location,
|
||||
display_name='Video {0}'.format(unicode(number))
|
||||
display_name='Video {0}'.format(six.text_type(number))
|
||||
).location
|
||||
for number in xrange(5)
|
||||
for number in range(5)
|
||||
]
|
||||
|
||||
submit_completions_for_testing(self.user, course_key, block_keys)
|
||||
@@ -801,9 +803,9 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin,
|
||||
ItemFactory.create(
|
||||
category='video',
|
||||
parent_location=course.location,
|
||||
display_name='Video {0}'.format(unicode(number))
|
||||
display_name='Video {0}'.format(six.text_type(number))
|
||||
).location
|
||||
for number in xrange(5)
|
||||
for number in range(5)
|
||||
]
|
||||
last_completed_block_string = str(block_keys[-1])
|
||||
|
||||
|
||||
@@ -2,32 +2,36 @@
|
||||
"""
|
||||
Miscellaneous tests for the student app.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import logging
|
||||
import unittest
|
||||
from datetime import datetime, timedelta
|
||||
from urllib import quote
|
||||
|
||||
import ddt
|
||||
import pytz
|
||||
import six
|
||||
from config_models.models import cache
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.urls import reverse
|
||||
from django.test import TestCase, override_settings
|
||||
from django.test.client import Client
|
||||
from django.urls import reverse
|
||||
from markupsafe import escape
|
||||
from mock import Mock, patch
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from opaque_keys.edx.locations import CourseLocator
|
||||
from pyquery import PyQuery as pq
|
||||
from six import text_type
|
||||
from six.moves import range
|
||||
from six.moves.urllib.parse import quote
|
||||
|
||||
import shoppingcart # pylint: disable=import-error
|
||||
from bulk_email.models import Optout # pylint: disable=import-error
|
||||
from lms.djangoapps.certificates.models import CertificateStatuses # pylint: disable=import-error
|
||||
from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFactory # pylint: disable=import-error
|
||||
from course_modes.models import CourseMode
|
||||
from course_modes.tests.factories import CourseModeFactory
|
||||
from lms.djangoapps.certificates.models import CertificateStatuses # pylint: disable=import-error
|
||||
from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFactory # pylint: disable=import-error
|
||||
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
|
||||
from openedx.core.djangoapps.catalog.tests.factories import CourseFactory as CatalogCourseFactory
|
||||
from openedx.core.djangoapps.catalog.tests.factories import CourseRunFactory, ProgramFactory, generate_course_run_key
|
||||
@@ -240,7 +244,7 @@ class CourseEndingTest(TestCase):
|
||||
)
|
||||
|
||||
if cert_grade is not None:
|
||||
cert_status = {'status': 'generating', 'grade': unicode(cert_grade), 'mode': 'honor'}
|
||||
cert_status = {'status': 'generating', 'grade': six.text_type(cert_grade), 'mode': 'honor'}
|
||||
else:
|
||||
cert_status = {'status': 'generating', 'mode': 'honor'}
|
||||
|
||||
@@ -252,7 +256,7 @@ class CourseEndingTest(TestCase):
|
||||
'status': 'generating',
|
||||
'show_survey_button': True,
|
||||
'survey_url': survey_url,
|
||||
'grade': unicode(expected_grade),
|
||||
'grade': six.text_type(expected_grade),
|
||||
'mode': 'honor',
|
||||
'linked_in_url': None,
|
||||
'can_unenroll': False,
|
||||
@@ -1086,7 +1090,7 @@ class RelatedProgramsTests(ProgramsApiConfigMixin, SharedModuleStoreTestCase):
|
||||
self.create_programs_config()
|
||||
self.client.login(username=self.user.username, password=self.password)
|
||||
|
||||
course_run = CourseRunFactory(key=unicode(self.course.id)) # pylint: disable=no-member
|
||||
course_run = CourseRunFactory(key=six.text_type(self.course.id)) # pylint: disable=no-member
|
||||
course = CatalogCourseFactory(course_runs=[course_run])
|
||||
self.programs = [ProgramFactory(courses=[course]) for __ in range(2)]
|
||||
|
||||
@@ -1160,4 +1164,4 @@ class UserAttributeTests(TestCase):
|
||||
def test_unicode(self):
|
||||
UserAttribute.set_user_attribute(self.user, self.name, self.value)
|
||||
for field in (self.name, self.value, self.user.username):
|
||||
self.assertIn(field, unicode(UserAttribute.objects.get(user=self.user)))
|
||||
self.assertIn(field, six.text_type(UserAttribute.objects.get(user=self.user)))
|
||||
|
||||
Reference in New Issue
Block a user