Ran pyupgrade on lms/djangoapps
Ran pyupgrade on lms/djangoapps/instructor_analytics Ran pyugprade on lms/djangoapps/instructor_task Ran pyupgrade on lms/djangoapps/learner_dashboard
This commit is contained in:
@@ -9,7 +9,6 @@ import datetime
|
||||
import json
|
||||
import logging
|
||||
|
||||
import six
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
@@ -18,16 +17,15 @@ from django.db.models import Count, Q # lint-amnesty, pylint: disable=unused-im
|
||||
from django.urls import reverse
|
||||
from edx_proctoring.api import get_exam_violation_report
|
||||
from opaque_keys.edx.keys import CourseKey, UsageKey
|
||||
from six import text_type
|
||||
|
||||
import xmodule.graders as xmgraders
|
||||
from lms.djangoapps.courseware.models import StudentModule
|
||||
from common.djangoapps.student.models import CourseEnrollment, CourseEnrollmentAllowed
|
||||
from lms.djangoapps.certificates.models import CertificateStatuses, GeneratedCertificate
|
||||
from lms.djangoapps.courseware.models import StudentModule
|
||||
from lms.djangoapps.grades.api import context as grades_context
|
||||
from lms.djangoapps.verify_student.services import IDVerificationService
|
||||
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
|
||||
from openedx.core.djangolib.markup import HTML, Text
|
||||
from common.djangoapps.student.models import CourseEnrollment, CourseEnrollmentAllowed
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -68,7 +66,7 @@ def issued_certificates(course_key, features):
|
||||
]
|
||||
"""
|
||||
|
||||
report_run_date = datetime.date.today().strftime(u"%B %d, %Y")
|
||||
report_run_date = datetime.date.today().strftime("%B %d, %Y")
|
||||
certificate_features = [x for x in CERTIFICATE_FEATURES if x in features]
|
||||
generated_certificates = list(GeneratedCertificate.eligible_certificates.filter(
|
||||
course_id=course_key,
|
||||
@@ -117,7 +115,7 @@ def enrolled_students_features(course_key, features):
|
||||
DjangoJSONEncoder().default(attr)
|
||||
return attr
|
||||
except TypeError:
|
||||
return six.text_type(attr)
|
||||
return str(attr)
|
||||
|
||||
def extract_student(student, features):
|
||||
""" convert student to dictionary """
|
||||
@@ -133,12 +131,10 @@ def enrolled_students_features(course_key, features):
|
||||
meta_key = feature.split('.')[1]
|
||||
meta_features.append((feature, meta_key))
|
||||
|
||||
student_dict = dict((feature, extract_attr(student, feature))
|
||||
for feature in student_features)
|
||||
student_dict = {feature: extract_attr(student, feature) for feature in student_features}
|
||||
profile = student.profile
|
||||
if profile is not None:
|
||||
profile_dict = dict((feature, extract_attr(profile, feature))
|
||||
for feature in profile_features)
|
||||
profile_dict = {feature: extract_attr(profile, feature) for feature in profile_features}
|
||||
student_dict.update(profile_dict)
|
||||
|
||||
# now fetch the requested meta fields
|
||||
@@ -196,7 +192,7 @@ def list_may_enroll(course_key, features):
|
||||
"""
|
||||
Build dict containing information about a single student.
|
||||
"""
|
||||
return dict((feature, getattr(student, feature)) for feature in features)
|
||||
return {feature: getattr(student, feature) for feature in features}
|
||||
|
||||
return [extract_student(student, features) for student in may_enroll_and_unenrolled]
|
||||
|
||||
@@ -211,18 +207,18 @@ def get_proctored_exam_results(course_key, features):
|
||||
"""
|
||||
Build dict containing information about a single student exam_attempt.
|
||||
"""
|
||||
proctored_exam = dict(
|
||||
(feature, exam_attempt.get(feature)) for feature in features if feature in exam_attempt
|
||||
)
|
||||
proctored_exam = {
|
||||
feature: exam_attempt.get(feature) for feature in features if feature in exam_attempt
|
||||
}
|
||||
|
||||
for status in comment_statuses:
|
||||
comment_list = exam_attempt.get(
|
||||
u'{status} Comments'.format(status=status),
|
||||
f'{status} Comments',
|
||||
[]
|
||||
)
|
||||
proctored_exam.update({
|
||||
u'{status} Count'.format(status=status): len(comment_list),
|
||||
u'{status} Comments'.format(status=status): '; '.join(comment_list),
|
||||
f'{status} Count': len(comment_list),
|
||||
f'{status} Comments': '; '.join(comment_list),
|
||||
})
|
||||
try:
|
||||
proctored_exam['track'] = course_enrollments[exam_attempt['user_id']]
|
||||
@@ -267,7 +263,7 @@ def coupon_codes_features(features, coupons_list, course_id):
|
||||
"""
|
||||
coupon_features = [x for x in COUPON_FEATURES if x in features]
|
||||
|
||||
coupon_dict = dict((feature, getattr(coupon, feature)) for feature in coupon_features)
|
||||
coupon_dict = {feature: getattr(coupon, feature) for feature in coupon_features}
|
||||
coupon_redemptions = coupon.couponredemption_set.filter(
|
||||
order__status="purchased"
|
||||
)
|
||||
@@ -297,7 +293,7 @@ def coupon_codes_features(features, coupons_list, course_id):
|
||||
# They have not been redeemed yet
|
||||
|
||||
coupon_dict['expiration_date'] = coupon.display_expiry_date
|
||||
coupon_dict['course_id'] = text_type(coupon_dict['course_id'])
|
||||
coupon_dict['course_id'] = str(coupon_dict['course_id'])
|
||||
return coupon_dict
|
||||
return [extract_coupon(coupon, features) for coupon in coupons_list]
|
||||
|
||||
@@ -371,8 +367,8 @@ def get_response_state(response):
|
||||
except TypeError:
|
||||
username = response.student.username
|
||||
err_msg = (
|
||||
u'Error occurred while attempting to load learner state '
|
||||
u'{username} for state {state}.'.format(
|
||||
'Error occurred while attempting to load learner state '
|
||||
'{username} for state {state}.'.format(
|
||||
username=username,
|
||||
state=problem_state
|
||||
)
|
||||
@@ -426,7 +422,7 @@ def course_registration_features(features, registration_codes, csv_type):
|
||||
site_name = configuration_helpers.get_value('SITE_NAME', settings.SITE_NAME)
|
||||
registration_features = [x for x in COURSE_REGISTRATION_FEATURES if x in features]
|
||||
|
||||
course_registration_dict = dict((feature, getattr(registration_code, feature)) for feature in registration_features) # lint-amnesty, pylint: disable=line-too-long
|
||||
course_registration_dict = {feature: getattr(registration_code, feature) for feature in registration_features} # lint-amnesty, pylint: disable=line-too-long
|
||||
course_registration_dict['company_name'] = None
|
||||
if registration_code.invoice_item:
|
||||
course_registration_dict['company_name'] = registration_code.invoice_item.invoice.company_name
|
||||
@@ -454,7 +450,7 @@ def course_registration_features(features, registration_codes, csv_type):
|
||||
except ObjectDoesNotExist:
|
||||
pass
|
||||
|
||||
course_registration_dict['course_id'] = text_type(course_registration_dict['course_id'])
|
||||
course_registration_dict['course_id'] = str(course_registration_dict['course_id'])
|
||||
return course_registration_dict
|
||||
return [extract_course_registration(code, features, csv_type) for code in registration_codes]
|
||||
|
||||
@@ -477,26 +473,26 @@ def dump_grading_context(course):
|
||||
msg += '\n'
|
||||
msg += "Graded sections:\n"
|
||||
for subgrader, category, weight in course.grader.subgraders:
|
||||
msg += u" subgrader=%s, type=%s, category=%s, weight=%s\n"\
|
||||
msg += " subgrader=%s, type=%s, category=%s, weight=%s\n"\
|
||||
% (subgrader.__class__, subgrader.type, category, weight)
|
||||
subgrader.index = 1
|
||||
graders[subgrader.type] = subgrader
|
||||
msg += hbar
|
||||
msg += u"Listing grading context for course %s\n" % text_type(course.id)
|
||||
msg += "Listing grading context for course %s\n" % str(course.id)
|
||||
|
||||
gcontext = grades_context.grading_context_for_course(course)
|
||||
msg += "graded sections:\n"
|
||||
|
||||
msg += '%s\n' % list(gcontext['all_graded_subsections_by_type'].keys())
|
||||
for (gsomething, gsvals) in gcontext['all_graded_subsections_by_type'].items():
|
||||
msg += u"--> Section %s:\n" % (gsomething)
|
||||
msg += "--> Section %s:\n" % (gsomething)
|
||||
for sec in gsvals:
|
||||
sdesc = sec['subsection_block']
|
||||
frmat = getattr(sdesc, 'format', None)
|
||||
aname = ''
|
||||
if frmat in graders:
|
||||
gform = graders[frmat]
|
||||
aname = u'%s %02d' % (gform.short_label, gform.index)
|
||||
aname = '%s %02d' % (gform.short_label, gform.index)
|
||||
gform.index += 1
|
||||
elif sdesc.display_name in graders:
|
||||
gform = graders[sdesc.display_name]
|
||||
@@ -504,7 +500,7 @@ def dump_grading_context(course):
|
||||
notes = ''
|
||||
if getattr(sdesc, 'score_by_attempt', False):
|
||||
notes = ', score by attempt!'
|
||||
msg += u" %s (format=%s, Assignment=%s%s)\n"\
|
||||
msg += " %s (format=%s, Assignment=%s%s)\n"\
|
||||
% (sdesc.display_name, frmat, aname, notes)
|
||||
msg += "all graded blocks:\n"
|
||||
msg += "length=%d\n" % gcontext['count_all_graded_blocks']
|
||||
|
||||
@@ -7,8 +7,6 @@ Format and create csv responses
|
||||
|
||||
import csv
|
||||
|
||||
import six
|
||||
from six.moves import map
|
||||
from django.http import HttpResponse
|
||||
|
||||
|
||||
@@ -24,18 +22,18 @@ def create_csv_response(filename, header, datarows):
|
||||
|
||||
"""
|
||||
response = HttpResponse(content_type='text/csv')
|
||||
response['Content-Disposition'] = u'attachment; filename={0}'.format(filename)
|
||||
response['Content-Disposition'] = f'attachment; filename={filename}'
|
||||
csvwriter = csv.writer(
|
||||
response,
|
||||
dialect='excel',
|
||||
quotechar='"',
|
||||
quoting=csv.QUOTE_ALL)
|
||||
|
||||
encoded_header = [six.text_type(s) for s in header]
|
||||
encoded_header = [str(s) for s in header]
|
||||
csvwriter.writerow(encoded_header)
|
||||
|
||||
for datarow in datarows:
|
||||
encoded_row = [six.text_type(s) for s in datarow]
|
||||
encoded_row = [str(s) for s in datarow]
|
||||
csvwriter.writerow(encoded_row)
|
||||
|
||||
return response
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
u"""
|
||||
"""
|
||||
Profile Distributions
|
||||
|
||||
Aggregate sums for values of fields in students profiles.
|
||||
@@ -39,7 +39,7 @@ DISPLAY_NAMES = {
|
||||
}
|
||||
|
||||
|
||||
class ProfileDistribution(object):
|
||||
class ProfileDistribution:
|
||||
"""
|
||||
Container for profile distribution data
|
||||
|
||||
@@ -96,7 +96,7 @@ def profile_distribution(course_id, feature):
|
||||
|
||||
if feature not in AVAILABLE_PROFILE_FEATURES:
|
||||
raise ValueError(
|
||||
u"unsupported feature requested for distribution u'{}'".format(
|
||||
"unsupported feature requested for distribution u'{}'".format(
|
||||
feature)
|
||||
)
|
||||
|
||||
@@ -152,8 +152,7 @@ def profile_distribution(course_id, feature):
|
||||
# query_distribution is of the form [{'featureval': 'value1', 'featureval__count': 4},
|
||||
# {'featureval': 'value2', 'featureval__count': 2}, ...]
|
||||
|
||||
distribution = dict((vald[feature], vald[feature + '__count'])
|
||||
for vald in query_distribution)
|
||||
distribution = {vald[feature]: vald[feature + '__count'] for vald in query_distribution}
|
||||
# distribution is of the form {'value1': 4, 'value2': 2, ...}
|
||||
|
||||
# change none to no_data for valid json key
|
||||
|
||||
@@ -4,22 +4,24 @@ Tests for instructor.basic
|
||||
"""
|
||||
|
||||
|
||||
import ddt
|
||||
import datetime # lint-amnesty, pylint: disable=unused-import, wrong-import-order
|
||||
import json # lint-amnesty, pylint: disable=wrong-import-order
|
||||
import json
|
||||
from unittest.mock import MagicMock, Mock, patch
|
||||
|
||||
import ddt
|
||||
import pytz # lint-amnesty, pylint: disable=unused-import
|
||||
from django.db.models import Q # lint-amnesty, pylint: disable=unused-import
|
||||
from django.urls import reverse # lint-amnesty, pylint: disable=unused-import
|
||||
from edx_proctoring.api import create_exam
|
||||
from edx_proctoring.models import ProctoredExamStudentAttempt
|
||||
from mock import MagicMock, Mock, patch
|
||||
from opaque_keys.edx.locator import UsageKey
|
||||
from six import text_type # lint-amnesty, pylint: disable=unused-import
|
||||
from six.moves import range, zip
|
||||
|
||||
from common.djangoapps.course_modes.models import CourseMode # lint-amnesty, pylint: disable=unused-import
|
||||
from common.djangoapps.course_modes.tests.factories import CourseModeFactory # lint-amnesty, pylint: disable=unused-import
|
||||
from common.djangoapps.course_modes.tests.factories import \
|
||||
CourseModeFactory # lint-amnesty, pylint: disable=unused-import
|
||||
from common.djangoapps.student.models import CourseEnrollment, CourseEnrollmentAllowed
|
||||
from common.djangoapps.student.roles import CourseSalesAdminRole # lint-amnesty, pylint: disable=unused-import
|
||||
from common.djangoapps.student.tests.factories import UserFactory
|
||||
from lms.djangoapps.courseware.tests.factories import InstructorFactory
|
||||
from lms.djangoapps.instructor_analytics.basic import ( # lint-amnesty, pylint: disable=unused-import
|
||||
AVAILABLE_FEATURES,
|
||||
@@ -32,12 +34,9 @@ from lms.djangoapps.instructor_analytics.basic import ( # lint-amnesty, pylint:
|
||||
get_proctored_exam_results,
|
||||
get_response_state,
|
||||
list_may_enroll,
|
||||
list_problem_responses,
|
||||
list_problem_responses
|
||||
)
|
||||
from openedx.core.djangoapps.course_groups.tests.helpers import CohortFactory
|
||||
from common.djangoapps.student.models import CourseEnrollment, CourseEnrollmentAllowed
|
||||
from common.djangoapps.student.roles import CourseSalesAdminRole # lint-amnesty, pylint: disable=unused-import
|
||||
from common.djangoapps.student.tests.factories import UserFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
@@ -47,7 +46,7 @@ class TestAnalyticsBasic(ModuleStoreTestCase):
|
||||
""" Test basic analytics functions. """
|
||||
|
||||
def setUp(self):
|
||||
super(TestAnalyticsBasic, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.course_key = self.store.make_course_key('robot', 'course', 'id')
|
||||
self.users = tuple(UserFactory() for _ in range(30))
|
||||
self.ces = tuple(CourseEnrollment.enroll(user, self.course_key)
|
||||
@@ -55,8 +54,8 @@ class TestAnalyticsBasic(ModuleStoreTestCase):
|
||||
self.instructor = InstructorFactory(course_key=self.course_key)
|
||||
for user in self.users:
|
||||
user.profile.meta = json.dumps({
|
||||
"position": u"edX expert {}".format(user.id),
|
||||
"company": u"Open edX Inc {}".format(user.id),
|
||||
"position": f"edX expert {user.id}",
|
||||
"company": f"Open edX Inc {user.id}",
|
||||
})
|
||||
user.profile.save()
|
||||
self.students_who_may_enroll = list(self.users) + [UserFactory() for _ in range(5)]
|
||||
@@ -66,8 +65,8 @@ class TestAnalyticsBasic(ModuleStoreTestCase):
|
||||
)
|
||||
|
||||
@ddt.data(
|
||||
(u'あなた', u'スの中'),
|
||||
(u"ГЂіи lіиэ ъэтшээи", u"Ђэаvэи аиↁ Ђэѓэ")
|
||||
('あなた', 'スの中'),
|
||||
("ГЂіи lіиэ ъэтшээи", "Ђэаvэи аиↁ Ђэѓэ")
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_get_response_state_with_ora(self, files_descriptions, saved_response):
|
||||
@@ -92,13 +91,13 @@ class TestAnalyticsBasic(ModuleStoreTestCase):
|
||||
relevant info (student.username and state).
|
||||
"""
|
||||
result = Mock(spec=['student', 'state'])
|
||||
result.student.username.return_value = u'user{}'.format(result_id)
|
||||
result.state.return_value = u'state{}'.format(result_id)
|
||||
result.student.username.return_value = f'user{result_id}'
|
||||
result.state.return_value = f'state{result_id}'
|
||||
return result
|
||||
|
||||
# Ensure that UsageKey.from_string returns a problem key that list_problem_responses can work with
|
||||
# (even when called with a dummy location):
|
||||
mock_problem_key = Mock(return_value=u'')
|
||||
mock_problem_key = Mock(return_value='')
|
||||
mock_problem_key.course_key = self.course_key
|
||||
with patch.object(UsageKey, 'from_string') as patched_from_string:
|
||||
patched_from_string.return_value = mock_problem_key
|
||||
@@ -135,8 +134,8 @@ class TestAnalyticsBasic(ModuleStoreTestCase):
|
||||
def test_enrolled_students_features_keys(self):
|
||||
query_features = ('username', 'name', 'email', 'city', 'country',)
|
||||
for user in self.users:
|
||||
user.profile.city = u"Mos Eisley {}".format(user.id)
|
||||
user.profile.country = u"Tatooine {}".format(user.id)
|
||||
user.profile.city = f"Mos Eisley {user.id}"
|
||||
user.profile.country = f"Tatooine {user.id}"
|
||||
user.profile.save()
|
||||
for feature in query_features:
|
||||
assert feature in AVAILABLE_FEATURES
|
||||
@@ -173,8 +172,8 @@ class TestAnalyticsBasic(ModuleStoreTestCase):
|
||||
assert len(userreports) == len(self.users)
|
||||
for userreport in userreports:
|
||||
assert set(userreport.keys()) == set(query_features)
|
||||
assert userreport['meta.position'] in [u'edX expert {}'.format(user.id) for user in self.users]
|
||||
assert userreport['meta.company'] in [u'Open edX Inc {}'.format(user.id) for user in self.users]
|
||||
assert userreport['meta.position'] in [f"edX expert {user.id}" for user in self.users]
|
||||
assert userreport['meta.company'] in [f"Open edX Inc {user.id}" for user in self.users]
|
||||
|
||||
def test_enrolled_students_enrollment_verification(self):
|
||||
"""
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
import pytest
|
||||
from django.test import TestCase
|
||||
from six.moves import range
|
||||
|
||||
from lms.djangoapps.instructor_analytics.csvs import create_csv_response, format_dictlist, format_instances
|
||||
|
||||
@@ -17,7 +16,7 @@ class TestAnalyticsCSVS(TestCase):
|
||||
|
||||
res = create_csv_response('robot.csv', header, datarows)
|
||||
assert res['Content-Type'] == 'text/csv'
|
||||
assert res['Content-Disposition'] == u'attachment; filename={0}'.format('robot.csv')
|
||||
assert res['Content-Disposition'] == 'attachment; filename={}'.format('robot.csv')
|
||||
assert res.content.strip().decode('utf-8') == '"Name","Email"'
|
||||
|
||||
def test_create_csv_response(self):
|
||||
@@ -26,7 +25,7 @@ class TestAnalyticsCSVS(TestCase):
|
||||
|
||||
res = create_csv_response('robot.csv', header, datarows)
|
||||
assert res['Content-Type'] == 'text/csv'
|
||||
assert res['Content-Disposition'] == u'attachment; filename={0}'.format('robot.csv')
|
||||
assert res['Content-Disposition'] == 'attachment; filename={}'.format('robot.csv')
|
||||
assert res.content.strip().decode('utf-8') ==\
|
||||
'"Name","Email"\r\n"Jim","jim@edy.org"\r\n"Jake","jake@edy.org"\r\n"Jeeves","jeeves@edy.org"'
|
||||
|
||||
@@ -36,7 +35,7 @@ class TestAnalyticsCSVS(TestCase):
|
||||
|
||||
res = create_csv_response('robot.csv', header, datarows)
|
||||
assert res['Content-Type'] == 'text/csv'
|
||||
assert res['Content-Disposition'] == u'attachment; filename={0}'.format('robot.csv')
|
||||
assert res['Content-Disposition'] == 'attachment; filename={}'.format('robot.csv')
|
||||
assert res.content.strip().decode('utf-8') == ''
|
||||
|
||||
|
||||
@@ -80,7 +79,7 @@ class TestAnalyticsFormatDictlist(TestCase):
|
||||
|
||||
res = create_csv_response('robot.csv', header, datarows)
|
||||
assert res['Content-Type'] == 'text/csv'
|
||||
assert res['Content-Disposition'] == u'attachment; filename={0}'.format('robot.csv')
|
||||
assert res['Content-Disposition'] == 'attachment; filename={}'.format('robot.csv')
|
||||
assert res.content.strip().decode('utf-8') ==\
|
||||
'"Name","Email"\r\n"Jim","jim@edy.org"\r\n"Jake","jake@edy.org"\r\n"Jeeves","jeeves@edy.org"'
|
||||
|
||||
@@ -88,7 +87,7 @@ class TestAnalyticsFormatDictlist(TestCase):
|
||||
class TestAnalyticsFormatInstances(TestCase):
|
||||
""" test format_instances method """
|
||||
|
||||
class TestDataClass(object):
|
||||
class TestDataClass:
|
||||
""" Test class to generate objects for format_instances """
|
||||
def __init__(self):
|
||||
self.a_var = 'aval'
|
||||
@@ -101,7 +100,7 @@ class TestAnalyticsFormatInstances(TestCase):
|
||||
return 'dval'
|
||||
|
||||
def setUp(self):
|
||||
super(TestAnalyticsFormatInstances, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.instances = [self.TestDataClass() for _ in range(5)]
|
||||
|
||||
def test_format_instances_response(self):
|
||||
|
||||
@@ -4,18 +4,17 @@
|
||||
import pytest
|
||||
from django.test import TestCase
|
||||
from opaque_keys.edx.locator import CourseLocator
|
||||
from six.moves import range
|
||||
|
||||
from lms.djangoapps.instructor_analytics.distributions import AVAILABLE_PROFILE_FEATURES, profile_distribution
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from common.djangoapps.student.tests.factories import UserFactory
|
||||
from lms.djangoapps.instructor_analytics.distributions import AVAILABLE_PROFILE_FEATURES, profile_distribution
|
||||
|
||||
|
||||
class TestAnalyticsDistributions(TestCase):
|
||||
'''Test analytics distribution gathering.'''
|
||||
|
||||
def setUp(self):
|
||||
super(TestAnalyticsDistributions, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.course_id = CourseLocator('robot', 'course', 'id')
|
||||
|
||||
self.users = [UserFactory(
|
||||
@@ -78,7 +77,7 @@ class TestAnalyticsDistributionsNoData(TestCase):
|
||||
'''Test analytics distribution gathering.'''
|
||||
|
||||
def setUp(self):
|
||||
super(TestAnalyticsDistributionsNoData, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.course_id = CourseLocator('robot', 'course', 'id')
|
||||
|
||||
self.users = [UserFactory(
|
||||
|
||||
Reference in New Issue
Block a user