Merge branch 'master' of github.com:edx/edx-platform into EDUCATOR-5080

This commit is contained in:
Justin Lapierre
2020-07-07 22:03:27 -04:00
13 changed files with 170 additions and 53 deletions

View File

@@ -0,0 +1,14 @@
"""
Progress Tab Serializers
"""
from rest_framework import serializers
from lms.djangoapps.course_home_api.outline.v1.serializers import CourseBlockSerializer
class ProgressTabSerializer(serializers.Serializer):
"""
Serializer for progress tab
"""
course_blocks = CourseBlockSerializer()
enrollment_mode = serializers.CharField()

View File

@@ -0,0 +1,102 @@
"""
Progress Tab Views
"""
from rest_framework.generics import RetrieveAPIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from edx_django_utils import monitoring as monitoring_utils
from opaque_keys.edx.keys import CourseKey
from lms.djangoapps.course_home_api.progress.v1.serializers import ProgressTabSerializer
from student.models import CourseEnrollment, UserTestGroup
from lms.djangoapps.course_api.blocks.transformers.blocks_api import BlocksAPITransformer
from lms.djangoapps.courseware.courses import get_course_with_access
from lms.djangoapps.courseware.masquerade import setup_masquerade
from lms.djangoapps.courseware.access import has_access
from xmodule.modulestore.django import modulestore
from lms.djangoapps.course_blocks.api import get_course_blocks
import lms.djangoapps.course_blocks.api as course_blocks_api
from openedx.core.djangoapps.content.block_structure.transformers import BlockStructureTransformers
class ProgressTabView(RetrieveAPIView):
"""
**Use Cases**
Request details for the Progress Tab
**Example Requests**
GET api/course_home/v1/progress/{course_key}
**Response Values**
Body consists of the following fields:
user: Serialized User object. The serialization has the following fields:
username: (str) The username of the user
email: (str) the email of the user
is_staff: (bool) boolean indicating whether the user has staff permisions or not
course_blocks:
blocks: List of serialized Course Block objects. Each serialization has the following fields:
id: (str) The usage ID of the block.
type: (str) The type of block. Possible values the names of any
XBlock type in the system, including custom blocks. Examples are
course, chapter, sequential, vertical, html, problem, video, and
discussion.
display_name: (str) The display name of the block.
lms_web_url: (str) The URL to the navigational container of the
xBlock on the web LMS.
children: (list) If the block has child blocks, a list of IDs of
the child blocks.
enrollment_mode: (str) a str representing the enrollment the user has ('audit', 'verified', ...)
**Returns**
* 200 on success with above fields.
* 403 if the user is not authenticated.
* 404 if the course is not available or cannot be seen.
"""
permission_classes = (IsAuthenticated,)
serializer_class = ProgressTabSerializer
def get(self, request, *args, **kwargs):
course_key_string = kwargs.get('course_key_string')
course_key = CourseKey.from_string(course_key_string)
course_usage_key = modulestore().make_course_usage_key(course_key)
# Enable NR tracing for this view based on course
monitoring_utils.set_custom_metric('course_id', course_key_string)
monitoring_utils.set_custom_metric('user_id', request.user.id)
monitoring_utils.set_custom_metric('is_staff', request.user.is_staff)
_, request.user = setup_masquerade(
request,
course_key,
staff_access=has_access(request.user, 'staff', course_key),
reset_masquerade_data=True
)
transformers = BlockStructureTransformers()
transformers += course_blocks_api.get_course_block_access_transformers(request.user)
transformers += [
BlocksAPITransformer(None, None, depth=3),
]
get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
course_blocks = get_course_blocks(request.user, course_usage_key, transformers, include_completion=True)
enrollment_mode, _ = CourseEnrollment.enrollment_mode_for_user(request.user, course_key)
data = {
'course_blocks': course_blocks,
'enrollment_mode': enrollment_mode,
}
serializer = self.get_serializer(data)
return Response(serializer.data)

View File

@@ -9,6 +9,7 @@ from django.urls import re_path
from lms.djangoapps.course_home_api.dates.v1.views import DatesTabView
from lms.djangoapps.course_home_api.course_metadata.v1.views import CourseHomeMetadataView
from lms.djangoapps.course_home_api.outline.v1.views import OutlineTabView
from lms.djangoapps.course_home_api.progress.v1.views import ProgressTabView
urlpatterns = []
@@ -38,3 +39,12 @@ urlpatterns += [
name='course-home-outline-tab'
),
]
# Progress Tab URLs
urlpatterns += [
re_path(
r'v1/progress/{}'.format(settings.COURSE_KEY_PATTERN),
ProgressTabView.as_view(),
name='course-home-progress-tab'
),
]

View File

@@ -323,7 +323,12 @@ FEATURES.update({
'AUTOMATIC_AUTH_FOR_TESTING': True,
'ENABLE_DISCUSSION_SERVICE': True,
'SHOW_HEADER_LANGUAGE_SELECTOR': True,
'ENABLE_ENTERPRISE_INTEGRATION': False,
# Enable enterprise integration by default.
# See https://github.com/edx/edx-enterprise/blob/master/docs/development.rst for
# more background on edx-enterprise.
# Toggle this off if you don't want anything to do with enterprise in devstack.
'ENABLE_ENTERPRISE_INTEGRATION': True,
})
ENABLE_MKTG_SITE = os.environ.get('ENABLE_MARKETING_SITE', False)
@@ -362,14 +367,15 @@ CREDENTIALS_SERVICE_USERNAME = 'credentials_worker'
COURSE_CATALOG_URL_ROOT = 'http://edx.devstack.discovery:18381'
COURSE_CATALOG_API_URL = '{}/api/v1'.format(COURSE_CATALOG_URL_ROOT)
# Enable enterprise integration so that we can include enterprise System-wide
# role logic without having to manipulate private settings.
FEATURES['ENABLE_ENTERPRISE_INTEGRATION'] = True
SYSTEM_WIDE_ROLE_CLASSES = os.environ.get("SYSTEM_WIDE_ROLE_CLASSES", SYSTEM_WIDE_ROLE_CLASSES)
SYSTEM_WIDE_ROLE_CLASSES.extend([
SYSTEM_WIDE_ROLE_CLASSES.append(
'system_wide_roles.SystemWideRoleAssignment',
'enterprise.SystemWideEnterpriseUserRoleAssignment',
])
)
if FEATURES.get('ENABLE_ENTERPRISE_INTEGRATION'):
SYSTEM_WIDE_ROLE_CLASSES.append(
'enterprise.SystemWideEnterpriseUserRoleAssignment',
)
# List of enterprise customer uuids to exclude from transition to use of enterprise-catalog
ENTERPRISE_CUSTOMERS_EXCLUDED_FROM_CATALOG = ()

View File

@@ -619,6 +619,7 @@ if FEATURES.get('ENABLE_THIRD_PARTY_AUTH'):
'social_core.backends.linkedin.LinkedinOAuth2',
'social_core.backends.facebook.FacebookOAuth2',
'social_core.backends.azuread.AzureADOAuth2',
'social_core.backends.apple.AppleIdAuth',
'third_party_auth.identityserver3.IdentityServer3',
'third_party_auth.saml.SAMLAuthBackend',
'third_party_auth.lti.LTIAuthBackend',

View File

@@ -271,11 +271,12 @@ class WaffleFlagNamespace(six.with_metaclass(ABCMeta, WaffleNamespace)):
# The callback needs to handle its own caching if it wants it.
value = self._cached_flags.get(namespaced_flag_name)
if value is None:
is_flag_active_for_everyone = False
if flag_undefined_default is not None:
# determine if the flag is undefined in waffle
try:
Flag.objects.get(name=namespaced_flag_name)
waffle_flag = Flag.objects.get(name=namespaced_flag_name)
is_flag_active_for_everyone = (waffle_flag.everyone is True)
except Flag.DoesNotExist:
if flag_undefined_default:
# This metric will go away once this has been fully retired with ARCHBOM-132.
@@ -289,7 +290,6 @@ class WaffleFlagNamespace(six.with_metaclass(ABCMeta, WaffleNamespace)):
value = flag_is_active(request, namespaced_flag_name)
else:
log.warning(u"%sFlag '%s' accessed without a request", self.log_prefix, namespaced_flag_name)
set_custom_metric('warn_flag_no_request', True)
# Return the default value if not in a request context.
# Note: this skips the cache as the value might be different
# in a normal request context. This case seems to occur when
@@ -297,6 +297,9 @@ class WaffleFlagNamespace(six.with_metaclass(ABCMeta, WaffleNamespace)):
# the default value.
value = bool(flag_undefined_default)
self._set_waffle_flag_metric(namespaced_flag_name, value)
no_request_default_match = is_flag_active_for_everyone == value
set_custom_metric('temp_flag_no_request_default_match_2', no_request_default_match)
set_custom_metric('warn_flag_no_request_return_value', value)
return value
self._cached_flags[namespaced_flag_name] = value

View File

@@ -2,7 +2,6 @@
Unified course experience settings and helper methods.
"""
import crum
from django.conf import settings
from django.utils.translation import ugettext as _
from edx_django_utils.monitoring import set_custom_metric
from waffle import flag_is_active
@@ -14,19 +13,6 @@ from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag, WaffleFlag, W
# Namespace for course experience waffle flags.
WAFFLE_FLAG_NAMESPACE = WaffleFlagNamespace(name='course_experience')
# .. toggle_name: USE_DEFAULT_TRUE_NAMESPACE
# .. toggle_implementation: DjangoSetting
# .. toggle_default: False
# .. toggle_description: When True, uses the new default_true namespace to help deprecate flag_undefined_default.
# .. toggle_category: course_experience
# .. toggle_use_cases: monitored_rollout
# .. toggle_creation_date: 2020-06-30
# .. toggle_expiration_date: 2020-07
# .. toggle_warnings: n/a
# .. toggle_tickets: n/a
# .. toggle_status: supported
_USE_DEFAULT_TRUE_NAMESPACE = 'USE_DEFAULT_TRUE_NAMESPACE'
class DefaultTrueWaffleFlagNamespace(WaffleFlagNamespace):
"""
@@ -36,9 +22,9 @@ class DefaultTrueWaffleFlagNamespace(WaffleFlagNamespace):
and refactor/fix any tests that shouldn't be removed.
"""
def _is_flag_active(self, flag_name):
def is_flag_active(self, flag_name, check_before_waffle_callback=None, flag_undefined_default=None):
"""
Returns and caches whether the provided flag is active.
Overrides is_flag_active, and returns and caches whether the provided flag is active.
If the flag value is already cached in the request, it is returned.
If the flag doesn't exist, always returns default of True.
@@ -80,17 +66,6 @@ class DefaultTrueWaffleFlagNamespace(WaffleFlagNamespace):
self._set_waffle_flag_metric(namespaced_flag_name, value)
return value
def is_flag_active(self, flag_name, check_before_waffle_callback=None, flag_undefined_default=None):
"""
Overrides is_flag_active if setting USE_DEFAULT_TRUE_NAMESPACE is True.
"""
use_default_true_namespace = getattr(settings, _USE_DEFAULT_TRUE_NAMESPACE, False)
set_custom_metric('temp_use_default_true_namespace', use_default_true_namespace)
if use_default_true_namespace:
return self._is_flag_active(flag_name)
else:
return super().is_flag_active(flag_name, check_before_waffle_callback, flag_undefined_default=True)
DEFAULT_TRUE_WAFFLE_FLAG_NAMESPACE = DefaultTrueWaffleFlagNamespace(name='course_experience')

View File

@@ -84,6 +84,7 @@ class CourseHomeFragmentView(EdxFragmentView):
"""
A fragment to render the home page for a course.
"""
_uses_pattern_library = False
def _get_resume_course_info(self, request, course_id):
"""
@@ -258,7 +259,7 @@ class CourseHomeFragmentView(EdxFragmentView):
'update_message_fragment': update_message_fragment,
'course_sock_fragment': course_sock_fragment,
'disable_courseware_js': True,
'uses_pattern_library': True,
'uses_bootstrap': True,
'upgrade_price': upgrade_price,
'upgrade_url': upgrade_url,
'has_discount': has_discount,

View File

@@ -40,6 +40,11 @@ drf-yasg<1.17.1
# drf-jwt 1.15.0 contains a migration that breaks on MySQL: https://github.com/Styria-Digital/django-rest-framework-jwt/issues/40
drf-jwt==1.14.0
# The team that owns this package will manually bump this package rather than having it pulled in automatically.
# This is to allow them to better control its deployment and to do it in a process that works better
# for them.
edx-enterprise==3.3.17
# Upgrading to 2.12.0 breaks several test classes due to API changes, need to update our code accordingly
factory-boy==2.8.1

View File

@@ -99,13 +99,13 @@ edx-django-release-util==0.4.4 # via -r requirements/edx/base.in
edx-django-sites-extensions==2.5.1 # via -r requirements/edx/base.in
edx-django-utils==3.2.3 # via -r requirements/edx/base.in, django-config-models, edx-drf-extensions, edx-enterprise, edx-rest-api-client, edx-when
edx-drf-extensions==6.1.0 # via -r requirements/edx/base.in, edx-completion, edx-enterprise, edx-organizations, edx-proctoring, edx-rbac, edx-when, edxval
edx-enterprise==3.3.15 # via -r requirements/edx/base.in
edx-enterprise==3.3.17 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.in
edx-i18n-tools==0.5.3 # via ora2
edx-milestones==0.3.0 # via -r requirements/edx/base.in
edx-opaque-keys[django]==2.1.0 # via -r requirements/edx/paver.txt, edx-bulk-grades, edx-ccx-keys, edx-completion, edx-drf-extensions, edx-enterprise, edx-milestones, edx-organizations, edx-proctoring, edx-user-state-client, edx-when, xmodule
edx-organizations==5.2.0 # via -r requirements/edx/base.in
edx-proctoring-proctortrack==1.0.5 # via -r requirements/edx/base.in
edx-proctoring==2.4.3 # via -r requirements/edx/base.in, edx-proctoring-proctortrack
edx-proctoring==2.4.4 # via -r requirements/edx/base.in, edx-proctoring-proctortrack
edx-rbac==1.3.1 # via edx-enterprise
edx-rest-api-client==5.2.1 # via -r requirements/edx/base.in, edx-enterprise, edx-proctoring
edx-search==1.4.1 # via -r requirements/edx/base.in
@@ -145,7 +145,7 @@ lazy==1.4 # via -r requirements/edx/paver.txt, acid-xblock, lti-
lepl==5.1.3 # via rfc6266-parser
libsass==0.10.0 # via -r requirements/edx/paver.txt, ora2
loremipsum==1.0.5 # via ora2
lti-consumer-xblock==2.0.1.1 # via -r requirements/edx/base.in
lti-consumer-xblock==2.0.2 # via -r requirements/edx/base.in
lxml==4.5.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/../edx-sandbox/shared.txt, capa, edxval, lti-consumer-xblock, ora2, safe-lxml, xblock, xmlsec
mailsnake==1.6.4 # via -r requirements/edx/base.in
mako==1.1.3 # via -r requirements/edx/base.in, acid-xblock, lti-consumer-xblock, xblock-google-drive, xblock-utils
@@ -179,7 +179,7 @@ polib==1.1.0 # via edx-i18n-tools
psutil==1.2.1 # via -r requirements/edx/paver.txt, edx-django-utils
py2neo==3.1.2 # via -r requirements/edx/base.in
pycontracts==1.8.12 # via -r requirements/edx/base.in, edx-user-state-client
pycountry==19.8.18 # via -r requirements/edx/base.in
pycountry==20.7.2 # via -r requirements/edx/base.in
pycparser==2.20 # via -r requirements/edx/../edx-sandbox/shared.txt, cffi
pycryptodome==3.9.8 # via lti-consumer-xblock, pdfminer.six
pycryptodomex==3.9.8 # via -r requirements/edx/base.in, edx-proctoring, pyjwkest
@@ -228,7 +228,7 @@ sqlparse==0.3.1 # via -r requirements/edx/base.in, django
staff-graded-xblock==0.8 # via -r requirements/edx/base.in
stevedore==1.32.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.in, -r requirements/edx/paver.txt, code-annotations, edx-ace, edx-enterprise, edx-opaque-keys
super-csv==0.9.9 # via -r requirements/edx/base.in, edx-bulk-grades
sympy==1.6 # via symmath
sympy==1.6.1 # via symmath
testfixtures==6.14.1 # via edx-enterprise
text-unidecode==1.3 # via python-slugify
tqdm==4.47.0 # via -r requirements/edx/../edx-sandbox/shared.txt, nltk

View File

@@ -111,14 +111,14 @@ edx-django-release-util==0.4.4 # via -r requirements/edx/testing.txt
edx-django-sites-extensions==2.5.1 # via -r requirements/edx/testing.txt
edx-django-utils==3.2.3 # via -r requirements/edx/testing.txt, django-config-models, edx-drf-extensions, edx-enterprise, edx-rest-api-client, edx-when
edx-drf-extensions==6.1.0 # via -r requirements/edx/testing.txt, edx-completion, edx-enterprise, edx-organizations, edx-proctoring, edx-rbac, edx-when, edxval
edx-enterprise==3.3.15 # via -r requirements/edx/testing.txt
edx-enterprise==3.3.17 # via -c requirements/edx/../constraints.txt, -r requirements/edx/testing.txt
edx-i18n-tools==0.5.3 # via -r requirements/edx/testing.txt, ora2
edx-lint==1.5.0 # via -r requirements/edx/testing.txt
edx-milestones==0.3.0 # via -r requirements/edx/testing.txt
edx-opaque-keys[django]==2.1.0 # via -r requirements/edx/testing.txt, edx-bulk-grades, edx-ccx-keys, edx-completion, edx-drf-extensions, edx-enterprise, edx-milestones, edx-organizations, edx-proctoring, edx-user-state-client, edx-when, xmodule
edx-organizations==5.2.0 # via -r requirements/edx/testing.txt
edx-proctoring-proctortrack==1.0.5 # via -r requirements/edx/testing.txt
edx-proctoring==2.4.3 # via -r requirements/edx/testing.txt, edx-proctoring-proctortrack
edx-proctoring==2.4.4 # via -r requirements/edx/testing.txt, edx-proctoring-proctortrack
edx-rbac==1.3.1 # via -r requirements/edx/testing.txt, edx-enterprise
edx-rest-api-client==5.2.1 # via -r requirements/edx/testing.txt, edx-enterprise, edx-proctoring
edx-search==1.4.1 # via -r requirements/edx/testing.txt
@@ -176,7 +176,7 @@ lazy==1.4 # via -r requirements/edx/testing.txt, acid-xblock, bo
lepl==5.1.3 # via -r requirements/edx/testing.txt, rfc6266-parser
libsass==0.10.0 # via -r requirements/edx/testing.txt, ora2
loremipsum==1.0.5 # via -r requirements/edx/testing.txt, ora2
lti-consumer-xblock==2.0.1.1 # via -r requirements/edx/testing.txt
lti-consumer-xblock==2.0.2 # via -r requirements/edx/testing.txt
lxml==4.5.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/testing.txt, capa, edxval, lti-consumer-xblock, ora2, pyquery, safe-lxml, xblock, xmlsec
m2r==0.2.1 # via sphinxcontrib-openapi
mailsnake==1.6.4 # via -r requirements/edx/testing.txt
@@ -219,7 +219,7 @@ py2neo==3.1.2 # via -r requirements/edx/testing.txt
py==1.9.0 # via -r requirements/edx/testing.txt, pytest, tox
pycodestyle==2.6.0 # via -r requirements/edx/testing.txt, flake8
pycontracts==1.8.12 # via -r requirements/edx/testing.txt, edx-user-state-client
pycountry==19.8.18 # via -r requirements/edx/testing.txt
pycountry==20.7.2 # via -r requirements/edx/testing.txt
pycparser==2.20 # via -r requirements/edx/testing.txt, cffi
pycryptodome==3.9.8 # via -r requirements/edx/testing.txt, lti-consumer-xblock, pdfminer.six
pycryptodomex==3.9.8 # via -r requirements/edx/testing.txt, edx-proctoring, pyjwkest
@@ -299,7 +299,7 @@ sqlparse==0.3.1 # via -r requirements/edx/testing.txt, django, django-
staff-graded-xblock==0.8 # via -r requirements/edx/testing.txt
stevedore==1.32.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/testing.txt, code-annotations, edx-ace, edx-enterprise, edx-opaque-keys
super-csv==0.9.9 # via -r requirements/edx/testing.txt, edx-bulk-grades
sympy==1.6 # via -r requirements/edx/testing.txt, symmath
sympy==1.6.1 # via -r requirements/edx/testing.txt, symmath
testfixtures==6.14.1 # via -r requirements/edx/testing.txt, edx-enterprise
text-unidecode==1.3 # via -r requirements/edx/testing.txt, faker, python-slugify
toml==0.10.1 # via -r requirements/edx/testing.txt, tox

View File

@@ -108,14 +108,14 @@ edx-django-release-util==0.4.4 # via -r requirements/edx/base.txt
edx-django-sites-extensions==2.5.1 # via -r requirements/edx/base.txt
edx-django-utils==3.2.3 # via -r requirements/edx/base.txt, django-config-models, edx-drf-extensions, edx-enterprise, edx-rest-api-client, edx-when
edx-drf-extensions==6.1.0 # via -r requirements/edx/base.txt, edx-completion, edx-enterprise, edx-organizations, edx-proctoring, edx-rbac, edx-when, edxval
edx-enterprise==3.3.15 # via -r requirements/edx/base.txt
edx-enterprise==3.3.17 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.txt
edx-i18n-tools==0.5.3 # via -r requirements/edx/base.txt, -r requirements/edx/testing.in, ora2
edx-lint==1.5.0 # via -r requirements/edx/testing.in
edx-milestones==0.3.0 # via -r requirements/edx/base.txt
edx-opaque-keys[django]==2.1.0 # via -r requirements/edx/base.txt, edx-bulk-grades, edx-ccx-keys, edx-completion, edx-drf-extensions, edx-enterprise, edx-milestones, edx-organizations, edx-proctoring, edx-user-state-client, edx-when, xmodule
edx-organizations==5.2.0 # via -r requirements/edx/base.txt
edx-proctoring-proctortrack==1.0.5 # via -r requirements/edx/base.txt
edx-proctoring==2.4.3 # via -r requirements/edx/base.txt, edx-proctoring-proctortrack
edx-proctoring==2.4.4 # via -r requirements/edx/base.txt, edx-proctoring-proctortrack
edx-rbac==1.3.1 # via -r requirements/edx/base.txt, edx-enterprise
edx-rest-api-client==5.2.1 # via -r requirements/edx/base.txt, edx-enterprise, edx-proctoring
edx-search==1.4.1 # via -r requirements/edx/base.txt
@@ -170,7 +170,7 @@ lazy==1.4 # via -r requirements/edx/base.txt, acid-xblock, bok-c
lepl==5.1.3 # via -r requirements/edx/base.txt, rfc6266-parser
libsass==0.10.0 # via -r requirements/edx/base.txt, ora2
loremipsum==1.0.5 # via -r requirements/edx/base.txt, ora2
lti-consumer-xblock==2.0.1.1 # via -r requirements/edx/base.txt
lti-consumer-xblock==2.0.2 # via -r requirements/edx/base.txt
lxml==4.5.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.txt, capa, edxval, lti-consumer-xblock, ora2, pyquery, safe-lxml, xblock, xmlsec
mailsnake==1.6.4 # via -r requirements/edx/base.txt
mako==1.1.3 # via -r requirements/edx/base.txt, acid-xblock, lti-consumer-xblock, xblock-google-drive, xblock-utils
@@ -210,7 +210,7 @@ py2neo==3.1.2 # via -r requirements/edx/base.txt
py==1.9.0 # via pytest, tox
pycodestyle==2.6.0 # via -r requirements/edx/testing.in, flake8
pycontracts==1.8.12 # via -r requirements/edx/base.txt, edx-user-state-client
pycountry==19.8.18 # via -r requirements/edx/base.txt
pycountry==20.7.2 # via -r requirements/edx/base.txt
pycparser==2.20 # via -r requirements/edx/base.txt, cffi
pycryptodome==3.9.8 # via -r requirements/edx/base.txt, lti-consumer-xblock, pdfminer.six
pycryptodomex==3.9.8 # via -r requirements/edx/base.txt, edx-proctoring, pyjwkest
@@ -278,7 +278,7 @@ sqlparse==0.3.1 # via -r requirements/edx/base.txt, django
staff-graded-xblock==0.8 # via -r requirements/edx/base.txt
stevedore==1.32.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.txt, code-annotations, edx-ace, edx-enterprise, edx-opaque-keys
super-csv==0.9.9 # via -r requirements/edx/base.txt, edx-bulk-grades
sympy==1.6 # via -r requirements/edx/base.txt, symmath
sympy==1.6.1 # via -r requirements/edx/base.txt, symmath
testfixtures==6.14.1 # via -r requirements/edx/base.txt, -r requirements/edx/testing.in, edx-enterprise
text-unidecode==1.3 # via -r requirements/edx/base.txt, faker, python-slugify
toml==0.10.1 # via tox