Integrate "olxcleaner" with course import (#27164)
* Integrating olxcleaner in course import * Adding toggle removal date and addressing pylint issues.
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
"""
|
||||
Receivers of signals sent from django-user-tasks
|
||||
"""
|
||||
|
||||
|
||||
import logging
|
||||
from urllib.parse import urljoin
|
||||
|
||||
@@ -33,7 +31,6 @@ def user_task_stopped_handler(sender, **kwargs): # pylint: disable=unused-argum
|
||||
None
|
||||
"""
|
||||
status = kwargs['status']
|
||||
|
||||
# Only send email when the entire task is complete, should only send when
|
||||
# a chain / chord / etc completes, not on sub-tasks.
|
||||
if status.parent is None:
|
||||
@@ -47,8 +44,10 @@ def user_task_stopped_handler(sender, **kwargs): # pylint: disable=unused-argum
|
||||
reverse('usertaskstatus-detail', args=[status.uuid])
|
||||
)
|
||||
|
||||
user_email = status.user.email
|
||||
task_name = status.name.lower()
|
||||
try:
|
||||
# Need to str state_text here because it is a proxy object and won't serialize correctly
|
||||
send_task_complete_email.delay(status.name.lower(), str(status.state_text), status.user.email, detail_url)
|
||||
send_task_complete_email.delay(task_name, str(status.state_text), user_email, detail_url)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
LOGGER.exception("Unable to queue send_task_complete_email")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
Celery tasks used by cms_user_tasks
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from boto.exception import NoAuthHandlerFound
|
||||
from celery import shared_task
|
||||
@@ -21,7 +21,7 @@ TASK_COMPLETE_EMAIL_TIMEOUT = 60
|
||||
|
||||
@shared_task(bind=True)
|
||||
@set_code_owner_attribute
|
||||
def send_task_complete_email(self, task_name, task_state_text, dest_addr, detail_url):
|
||||
def send_task_complete_email(self, task_name, task_state_text, dest_addr, detail_url, olx_validation_text=None):
|
||||
"""
|
||||
Sending an email to the users when an async task completes.
|
||||
"""
|
||||
@@ -32,6 +32,14 @@ def send_task_complete_email(self, task_name, task_state_text, dest_addr, detail
|
||||
'task_status': task_state_text,
|
||||
'detail_url': detail_url
|
||||
}
|
||||
if olx_validation_text:
|
||||
try:
|
||||
olx_validations = json.loads(olx_validation_text)
|
||||
context['olx_validation_errors'] = True
|
||||
context['error_summary'] = olx_validations.get('error_summary')
|
||||
context['error_report'] = olx_validations.get('error_report')
|
||||
except ValueError: # includes simplejson.decoder.JSONDecodeError
|
||||
LOGGER.error(f'Unable to parse CourseOlx validation text: {olx_validation_text}')
|
||||
|
||||
subject = render_to_string('emails/user_task_complete_email_subject.txt', context)
|
||||
# Eliminate any newlines
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
Unit tests for integration of the django-user-tasks app and its REST API.
|
||||
"""
|
||||
|
||||
|
||||
import logging
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
@@ -10,6 +10,7 @@ import tarfile
|
||||
from datetime import datetime
|
||||
from tempfile import NamedTemporaryFile, mkdtemp
|
||||
|
||||
import olxcleaner
|
||||
from ccx_keys.locator import CCXLocator
|
||||
from celery import shared_task
|
||||
from celery.utils.log import get_task_logger
|
||||
@@ -25,9 +26,10 @@ from edx_django_utils.monitoring import (
|
||||
set_code_owner_attribute,
|
||||
set_code_owner_attribute_from_module,
|
||||
set_custom_attribute,
|
||||
set_custom_attributes_for_course_key,
|
||||
set_custom_attributes_for_course_key
|
||||
)
|
||||
from common.djangoapps.util.monitoring import monitor_import_failure
|
||||
from olxcleaner.exceptions import ErrorLevel
|
||||
from olxcleaner.reporting import report_error_summary, report_errors
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from opaque_keys.edx.locator import LibraryLocator
|
||||
from organizations.api import add_organization_course, ensure_organization
|
||||
@@ -47,6 +49,7 @@ from cms.djangoapps.contentstore.utils import initialize_permissions, reverse_us
|
||||
from cms.djangoapps.models.settings.course_metadata import CourseMetadata
|
||||
from common.djangoapps.course_action_state.models import CourseRerunState
|
||||
from common.djangoapps.student.auth import has_course_author_access
|
||||
from common.djangoapps.util.monitoring import monitor_import_failure
|
||||
from openedx.core.djangoapps.content.learning_sequences.api import key_supports_outlines
|
||||
from openedx.core.djangoapps.embargo.models import CountryAccessRule, RestrictedCourse
|
||||
from openedx.core.lib.extract_tar import safetar_extractall
|
||||
@@ -60,6 +63,7 @@ from xmodule.modulestore.xml_exporter import export_course_to_xml, export_librar
|
||||
from xmodule.modulestore.xml_importer import import_course_from_xml, import_library_from_xml
|
||||
|
||||
from .outlines import update_outline_from_modulestore
|
||||
from .toggles import course_import_olx_validation_is_enabled
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
@@ -442,6 +446,7 @@ def import_olx(self, user_id, course_key_string, archive_path, archive_name, lan
|
||||
return file_is_valid
|
||||
|
||||
def file_exists_in_storage():
|
||||
"""Verify archive path exists in storage."""
|
||||
archive_path_exists = course_import_export_storage.exists(archive_path)
|
||||
|
||||
if not archive_path_exists:
|
||||
@@ -452,6 +457,39 @@ def import_olx(self, user_id, course_key_string, archive_path, archive_name, lan
|
||||
monitor_import_failure(courselike_key, current_step, message=message)
|
||||
return archive_path_exists
|
||||
|
||||
def verify_root_name_exists(course_dir, root_name):
|
||||
"""Verify root xml file exists."""
|
||||
|
||||
def get_all_files(directory):
|
||||
"""
|
||||
For each file in the directory, yield a 2-tuple of (file-name,
|
||||
directory-path)
|
||||
"""
|
||||
for directory_path, _dirnames, filenames in os.walk(directory):
|
||||
for filename in filenames:
|
||||
yield (filename, directory_path)
|
||||
|
||||
def get_dir_for_filename(directory, filename):
|
||||
"""
|
||||
Returns the directory path for the first file found in the directory
|
||||
with the given name. If there is no file in the directory with
|
||||
the specified name, return None.
|
||||
"""
|
||||
for name, directory_path in get_all_files(directory):
|
||||
if name == filename:
|
||||
return directory_path
|
||||
return None
|
||||
|
||||
dirpath = get_dir_for_filename(course_dir, root_name)
|
||||
if not dirpath:
|
||||
message = f'Could not find the {root_name} file in the package.'
|
||||
with translation_language(language):
|
||||
self.status.fail(_('Could not find the {0} file in the package.').format(root_name))
|
||||
LOGGER.error(f'{log_prefix}: {message}')
|
||||
monitor_import_failure(courselike_key, current_step, message=message)
|
||||
return
|
||||
return dirpath
|
||||
|
||||
user = validate_user()
|
||||
if not user:
|
||||
return
|
||||
@@ -543,34 +581,11 @@ def import_olx(self, user_id, course_key_string, archive_path, archive_name, lan
|
||||
self.status.increment_completed_steps()
|
||||
LOGGER.info(f'{log_prefix}: Uploaded file extracted. Verification step started')
|
||||
|
||||
# find the 'course.xml' file
|
||||
def get_all_files(directory):
|
||||
"""
|
||||
For each file in the directory, yield a 2-tuple of (file-name,
|
||||
directory-path)
|
||||
"""
|
||||
for directory_path, _dirnames, filenames in os.walk(directory):
|
||||
for filename in filenames:
|
||||
yield (filename, directory_path)
|
||||
|
||||
def get_dir_for_filename(directory, filename):
|
||||
"""
|
||||
Returns the directory path for the first file found in the directory
|
||||
with the given name. If there is no file in the directory with
|
||||
the specified name, return None.
|
||||
"""
|
||||
for name, directory_path in get_all_files(directory):
|
||||
if name == filename:
|
||||
return directory_path
|
||||
return None
|
||||
|
||||
dirpath = get_dir_for_filename(course_dir, root_name)
|
||||
dirpath = verify_root_name_exists(course_dir, root_name)
|
||||
if not dirpath:
|
||||
message = f'Could not find the {root_name} file in the package.'
|
||||
with translation_language(language):
|
||||
self.status.fail(_('Could not find the {0} file in the package.').format(root_name))
|
||||
LOGGER.error(f'{log_prefix}: {message}')
|
||||
monitor_import_failure(courselike_key, current_step, message=message)
|
||||
return
|
||||
|
||||
if not validate_course_olx(courselike_key, dirpath, self.status):
|
||||
return
|
||||
|
||||
dirpath = os.path.relpath(dirpath, data_root)
|
||||
@@ -643,3 +658,41 @@ def update_outline_from_modulestore_task(course_key_str):
|
||||
except Exception: # pylint disable=broad-except
|
||||
LOGGER.exception("Could not create course outline for course %s", course_key_str)
|
||||
raise # Re-raise so that errors are noted in reporting.
|
||||
|
||||
|
||||
def validate_course_olx(courselike_key, course_dir, status):
|
||||
"""
|
||||
Validates course olx and records the errors as artifact.
|
||||
Arguments:
|
||||
course_dir: complete path to the course olx
|
||||
status: UserTaskStatus object.
|
||||
"""
|
||||
olx_is_valid = True
|
||||
log_prefix = f'Course import {courselike_key}'
|
||||
|
||||
if not course_import_olx_validation_is_enabled():
|
||||
return olx_is_valid
|
||||
try:
|
||||
__, errorstore, __ = olxcleaner.validate(course_dir, steps=8)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
LOGGER.exception(f'{log_prefix}: CourseOlx Could not be validated')
|
||||
return olx_is_valid
|
||||
|
||||
has_errors = errorstore.return_error(ErrorLevel.ERROR.value)
|
||||
if not has_errors:
|
||||
return olx_is_valid
|
||||
|
||||
errorstore.errors = [error for error in errorstore.errors if error.level_val == ErrorLevel.ERROR.value]
|
||||
error_summary = report_error_summary(errorstore)
|
||||
error_report = report_errors(errorstore)
|
||||
message = json.dumps({
|
||||
'error_summary': error_summary,
|
||||
'error_report': error_report,
|
||||
})
|
||||
UserTaskArtifact.objects.create(status=status, name='OLX_VALIDATION_ERROR', text=message)
|
||||
LOGGER.error(f'{log_prefix}: CourseOlx validation failed')
|
||||
|
||||
# TODO: Do not fail the task until we have some data about kinds of
|
||||
# olx validation failures. TNL-8151
|
||||
|
||||
return olx_is_valid
|
||||
|
||||
@@ -38,9 +38,32 @@ SPLIT_LIBRARY_ON_DASHBOARD = LegacyWaffleFlag(
|
||||
module_name=__name__
|
||||
)
|
||||
|
||||
# Waffle flag to enable olx validation during course import.
|
||||
# .. toggle_name: course_import_olx_validation
|
||||
# .. toggle_implementation: WaffleFlag
|
||||
# .. toggle_default: False
|
||||
# .. toggle_description: Studio Import
|
||||
# .. toggle_use_cases: open_edx
|
||||
# .. toggle_creation_date: 2021-04-01
|
||||
# .. toggle_target_removal_date: 2021-05-01
|
||||
# .. toggle_warnings: ??
|
||||
# .. toggle_tickets: TNL-8151
|
||||
COURSE_IMPORT_OLX_VALIDATION = LegacyWaffleFlag(
|
||||
waffle_namespace=LegacyWaffleFlagNamespace(name=WAFFLE_NAMESPACE),
|
||||
flag_name='course_import_olx_validation',
|
||||
module_name=__name__
|
||||
)
|
||||
|
||||
|
||||
def split_library_view_on_dashboard():
|
||||
"""
|
||||
check if data new view for library is enabled on studio dashboard.
|
||||
"""
|
||||
return SPLIT_LIBRARY_ON_DASHBOARD.is_enabled()
|
||||
|
||||
|
||||
def course_import_olx_validation_is_enabled():
|
||||
"""
|
||||
Check if course olx validation is enabled on course import.
|
||||
"""
|
||||
return COURSE_IMPORT_OLX_VALIDATION.is_enabled()
|
||||
|
||||
@@ -9,3 +9,16 @@ ${_("Your {task_name} task has completed with the status '{task_status}'. Use th
|
||||
${_("Your {task_name} task has completed with the status '{task_status}'. Sign in to view the details of your task or download any files created.").format(task_name=task_name, task_status=task_status)}
|
||||
|
||||
% endif
|
||||
|
||||
% if olx_validation_errors:
|
||||
|
||||
${_("Here are some validation errors we found with your course content.")}
|
||||
|
||||
|
||||
% for error in error_report:
|
||||
${error}
|
||||
% endfor
|
||||
|
||||
% else:
|
||||
|
||||
% endif
|
||||
|
||||
@@ -20,7 +20,7 @@ matplotlib==2.2.4 # via -c requirements/edx-sandbox/../constraints.txt,
|
||||
mpmath==1.2.1 # via sympy
|
||||
networkx==2.2 # via -r requirements/edx-sandbox/py35.in
|
||||
nltk==3.5 # via -r requirements/edx-sandbox/shared.txt, chem
|
||||
numpy==1.16.5 # via -r requirements/edx-sandbox/py35.in, chem, matplotlib, openedx-calc, scipy
|
||||
numpy==1.16.5 # via -r requirements/edx-sandbox/py35.in, chem, matplotlib, openedx-calc
|
||||
openedx-calc==1.0.9 # via -r requirements/edx-sandbox/py35.in
|
||||
pycparser==2.20 # via -r requirements/edx-sandbox/shared.txt, cffi
|
||||
pyparsing==2.2.0 # via -r requirements/edx-sandbox/py35.in, chem, matplotlib, openedx-calc
|
||||
|
||||
@@ -115,6 +115,7 @@ mysqlclient # Driver for the default production relation
|
||||
newrelic # New Relic agent for performance monitoring
|
||||
nodeenv # Utility for managing Node.js environments; we use this for deployments and testing
|
||||
oauthlib # OAuth specification support for authenticating via LTI or other Open edX services
|
||||
olxcleaner # Library to support syntex validation during course import
|
||||
openedx-calc # Library supporting mathematical calculations for Open edX
|
||||
ora2
|
||||
piexif # Exif image metadata manipulation, used in the profile_images app
|
||||
|
||||
@@ -147,7 +147,7 @@ lazy==1.4 # via -r requirements/edx/paver.txt, acid-xblock, lti-
|
||||
libsass==0.10.0 # via -r requirements/edx/paver.txt, ora2
|
||||
loremipsum==1.0.5 # via ora2
|
||||
lti-consumer-xblock==2.7.1 # 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
|
||||
lxml==4.5.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/../edx-sandbox/shared.txt, capa, edxval, lti-consumer-xblock, olxcleaner, ora2, safe-lxml, xblock, xmlsec
|
||||
mailsnake==1.6.4 # via -r requirements/edx/base.in
|
||||
mako==1.1.4 # via -r requirements/edx/base.in, acid-xblock, lti-consumer-xblock, xblock-google-drive, xblock-utils
|
||||
markdown==3.3.4 # via -r requirements/edx/base.in, django-wiki, staff-graded-xblock, xblock-poll
|
||||
@@ -164,6 +164,7 @@ nltk==3.5 # via -r requirements/edx/../edx-sandbox/shared.txt, c
|
||||
nodeenv==1.5.0 # via -r requirements/edx/base.in
|
||||
numpy==1.20.2 # via chem, openedx-calc, scipy
|
||||
oauthlib==3.0.1 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.in, django-oauth-toolkit, lti-consumer-xblock, requests-oauthlib, social-auth-core
|
||||
olxcleaner==0.1.4 # via -r requirements/edx/base.in
|
||||
openedx-calc==2.0.1 # via -r requirements/edx/base.in
|
||||
ora2==3.4.0 # via -r requirements/edx/base.in
|
||||
packaging==20.9 # via bleach, drf-yasg
|
||||
@@ -182,18 +183,19 @@ pycryptodomex==3.10.1 # via -r requirements/edx/base.in, edx-proctoring, lti
|
||||
pygments==2.8.1 # via -r requirements/edx/base.in
|
||||
pyjwkest==1.4.2 # via -r requirements/edx/base.in, edx-drf-extensions, lti-consumer-xblock
|
||||
pyjwt[crypto]==1.7.1 # via -r requirements/edx/base.in, drf-jwt, edx-rest-api-client, social-auth-core
|
||||
pylatexenc==2.9 # via olxcleaner
|
||||
pymongo==3.10.1 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.in, -r requirements/edx/paver.txt, edx-opaque-keys, event-tracking, mongodbproxy, mongoengine
|
||||
pynliner==0.8.0 # via -r requirements/edx/base.in
|
||||
pyparsing==2.4.7 # via chem, openedx-calc, packaging, pycontracts
|
||||
pysrt==1.1.2 # via -r requirements/edx/base.in, edxval
|
||||
python-dateutil==2.4.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.in, analytics-python, botocore, edx-ace, edx-drf-extensions, edx-enterprise, edx-event-routing-backends, edx-proctoring, icalendar, ora2, xblock
|
||||
python-dateutil==2.4.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.in, analytics-python, botocore, edx-ace, edx-drf-extensions, edx-enterprise, edx-event-routing-backends, edx-proctoring, icalendar, olxcleaner, ora2, xblock
|
||||
python-levenshtein==0.12.2 # via -r requirements/edx/base.in
|
||||
python-memcached==1.59 # via -r requirements/edx/paver.txt
|
||||
python-slugify==4.0.1 # via code-annotations
|
||||
python-swiftclient==3.11.1 # via ora2
|
||||
python3-openid==3.2.0 ; python_version >= "3" # via -r requirements/edx/base.in, social-auth-core
|
||||
python3-saml==1.9.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.in
|
||||
pytz==2021.1 # via -r requirements/edx/base.in, babel, capa, celery, django, django-ses, edx-completion, edx-enterprise, edx-event-routing-backends, edx-proctoring, edx-submissions, edx-tincan-py35, event-tracking, fs, icalendar, ora2, tincan, xblock
|
||||
pytz==2021.1 # via -r requirements/edx/base.in, babel, capa, celery, django, django-ses, edx-completion, edx-enterprise, edx-event-routing-backends, edx-proctoring, edx-submissions, edx-tincan-py35, event-tracking, fs, icalendar, olxcleaner, ora2, tincan, xblock
|
||||
pyuca==1.2 # via -r requirements/edx/base.in
|
||||
pyyaml==5.4.1 # via -r requirements/edx/base.in, code-annotations, edx-django-release-util, edx-i18n-tools, xblock
|
||||
random2==1.0.1 # via -r requirements/edx/base.in
|
||||
|
||||
@@ -176,7 +176,7 @@ lazy==1.4 # via -r requirements/edx/testing.txt, acid-xblock, bo
|
||||
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.7.1 # 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
|
||||
lxml==4.5.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/testing.txt, capa, edxval, lti-consumer-xblock, olxcleaner, ora2, pyquery, safe-lxml, xblock, xmlsec
|
||||
m2r==0.2.1 # via sphinxcontrib-openapi
|
||||
mailsnake==1.6.4 # via -r requirements/edx/testing.txt
|
||||
mako==1.1.4 # via -r requirements/edx/testing.txt, acid-xblock, lti-consumer-xblock, xblock-google-drive, xblock-utils
|
||||
@@ -197,6 +197,7 @@ nltk==3.5 # via -r requirements/edx/testing.txt, chem
|
||||
nodeenv==1.5.0 # via -r requirements/edx/testing.txt
|
||||
numpy==1.20.2 # via -r requirements/edx/testing.txt, chem, openedx-calc, scipy
|
||||
oauthlib==3.0.1 # via -c requirements/edx/../constraints.txt, -r requirements/edx/testing.txt, django-oauth-toolkit, lti-consumer-xblock, requests-oauthlib, social-auth-core
|
||||
olxcleaner==0.1.4 # via -r requirements/edx/testing.txt
|
||||
openedx-calc==2.0.1 # via -r requirements/edx/testing.txt
|
||||
ora2==3.4.0 # via -r requirements/edx/testing.txt
|
||||
packaging==20.9 # via -r requirements/edx/testing.txt, bleach, drf-yasg, pytest, sphinx, tox
|
||||
@@ -219,6 +220,7 @@ pycryptodomex==3.10.1 # via -r requirements/edx/testing.txt, edx-proctoring,
|
||||
pygments==2.8.1 # via -r requirements/edx/testing.txt, diff-cover, sphinx
|
||||
pyjwkest==1.4.2 # via -r requirements/edx/testing.txt, edx-drf-extensions, lti-consumer-xblock
|
||||
pyjwt[crypto]==1.7.1 # via -r requirements/edx/testing.txt, drf-jwt, edx-rest-api-client, social-auth-core
|
||||
pylatexenc==2.9 # via -r requirements/edx/testing.txt, olxcleaner
|
||||
pylint-celery==0.3 # via -r requirements/edx/testing.txt, edx-lint
|
||||
pylint-django==2.4.2 # via -r requirements/edx/testing.txt, edx-lint
|
||||
pylint-plugin-utils==0.6 # via -r requirements/edx/testing.txt, pylint-celery, pylint-django
|
||||
@@ -238,14 +240,14 @@ pytest-metadata==1.8.0 # via -r requirements/edx/testing.txt, pytest-json-rep
|
||||
pytest-randomly==3.5.0 # via -r requirements/edx/testing.txt
|
||||
pytest-xdist[psutil]==2.2.1 # via -r requirements/edx/testing.txt
|
||||
pytest==6.2.2 # via -r requirements/edx/testing.txt, pytest-attrib, pytest-cov, pytest-django, pytest-forked, pytest-json-report, pytest-metadata, pytest-randomly, pytest-xdist
|
||||
python-dateutil==2.4.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/testing.txt, analytics-python, botocore, edx-ace, edx-drf-extensions, edx-enterprise, edx-event-routing-backends, edx-proctoring, faker, freezegun, icalendar, ora2, xblock
|
||||
python-dateutil==2.4.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/testing.txt, analytics-python, botocore, edx-ace, edx-drf-extensions, edx-enterprise, edx-event-routing-backends, edx-proctoring, faker, freezegun, icalendar, olxcleaner, ora2, xblock
|
||||
python-levenshtein==0.12.2 # via -r requirements/edx/testing.txt
|
||||
python-memcached==1.59 # via -r requirements/edx/testing.txt
|
||||
python-slugify==4.0.1 # via -r requirements/edx/testing.txt, code-annotations, transifex-client
|
||||
python-swiftclient==3.11.1 # via -r requirements/edx/testing.txt, ora2
|
||||
python3-openid==3.2.0 ; python_version >= "3" # via -r requirements/edx/testing.txt, social-auth-core
|
||||
python3-saml==1.9.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/testing.txt
|
||||
pytz==2021.1 # via -r requirements/edx/testing.txt, babel, capa, celery, django, django-ses, edx-completion, edx-enterprise, edx-event-routing-backends, edx-proctoring, edx-submissions, edx-tincan-py35, event-tracking, fs, icalendar, ora2, tincan, xblock
|
||||
pytz==2021.1 # via -r requirements/edx/testing.txt, babel, capa, celery, django, django-ses, edx-completion, edx-enterprise, edx-event-routing-backends, edx-proctoring, edx-submissions, edx-tincan-py35, event-tracking, fs, icalendar, olxcleaner, ora2, tincan, xblock
|
||||
pyuca==1.2 # via -r requirements/edx/testing.txt
|
||||
pywatchman==1.4.1 # via -r requirements/edx/development.in
|
||||
pyyaml==5.4.1 # via -r requirements/edx/testing.txt, code-annotations, edx-django-release-util, edx-i18n-tools, sphinxcontrib-openapi, xblock
|
||||
|
||||
@@ -170,7 +170,7 @@ lazy==1.4 # via -r requirements/edx/base.txt, acid-xblock, bok-c
|
||||
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.7.1 # 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
|
||||
lxml==4.5.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.txt, capa, edxval, lti-consumer-xblock, olxcleaner, ora2, pyquery, safe-lxml, xblock, xmlsec
|
||||
mailsnake==1.6.4 # via -r requirements/edx/base.txt
|
||||
mako==1.1.4 # via -r requirements/edx/base.txt, acid-xblock, lti-consumer-xblock, xblock-google-drive, xblock-utils
|
||||
markdown==3.3.4 # via -r requirements/edx/base.txt, django-wiki, staff-graded-xblock, xblock-poll
|
||||
@@ -189,6 +189,7 @@ nltk==3.5 # via -r requirements/edx/base.txt, chem
|
||||
nodeenv==1.5.0 # via -r requirements/edx/base.txt
|
||||
numpy==1.20.2 # via -r requirements/edx/base.txt, chem, openedx-calc, scipy
|
||||
oauthlib==3.0.1 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.txt, django-oauth-toolkit, lti-consumer-xblock, requests-oauthlib, social-auth-core
|
||||
olxcleaner==0.1.4 # via -r requirements/edx/base.txt
|
||||
openedx-calc==2.0.1 # via -r requirements/edx/base.txt
|
||||
ora2==3.4.0 # via -r requirements/edx/base.txt
|
||||
packaging==20.9 # via -r requirements/edx/base.txt, bleach, drf-yasg, pytest, tox
|
||||
@@ -210,6 +211,7 @@ pycryptodomex==3.10.1 # via -r requirements/edx/base.txt, edx-proctoring, lt
|
||||
pygments==2.8.1 # via -r requirements/edx/base.txt, -r requirements/edx/coverage.txt, diff-cover
|
||||
pyjwkest==1.4.2 # via -r requirements/edx/base.txt, edx-drf-extensions, lti-consumer-xblock
|
||||
pyjwt[crypto]==1.7.1 # via -r requirements/edx/base.txt, drf-jwt, edx-rest-api-client, social-auth-core
|
||||
pylatexenc==2.9 # via -r requirements/edx/base.txt, olxcleaner
|
||||
pylint-celery==0.3 # via edx-lint
|
||||
pylint-django==2.4.2 # via edx-lint
|
||||
pylint-plugin-utils==0.6 # via pylint-celery, pylint-django
|
||||
@@ -228,14 +230,14 @@ pytest-metadata==1.8.0 # via -r requirements/edx/testing.in, pytest-json-repo
|
||||
pytest-randomly==3.5.0 # via -r requirements/edx/testing.in
|
||||
pytest-xdist[psutil]==2.2.1 # via -r requirements/edx/testing.in
|
||||
pytest==6.2.2 # via -r requirements/edx/testing.in, pytest-attrib, pytest-cov, pytest-django, pytest-forked, pytest-json-report, pytest-metadata, pytest-randomly, pytest-xdist
|
||||
python-dateutil==2.4.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.txt, analytics-python, botocore, edx-ace, edx-drf-extensions, edx-enterprise, edx-event-routing-backends, edx-proctoring, faker, freezegun, icalendar, ora2, xblock
|
||||
python-dateutil==2.4.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.txt, analytics-python, botocore, edx-ace, edx-drf-extensions, edx-enterprise, edx-event-routing-backends, edx-proctoring, faker, freezegun, icalendar, olxcleaner, ora2, xblock
|
||||
python-levenshtein==0.12.2 # via -r requirements/edx/base.txt
|
||||
python-memcached==1.59 # via -r requirements/edx/base.txt
|
||||
python-slugify==4.0.1 # via -r requirements/edx/base.txt, code-annotations, transifex-client
|
||||
python-swiftclient==3.11.1 # via -r requirements/edx/base.txt, ora2
|
||||
python3-openid==3.2.0 ; python_version >= "3" # via -r requirements/edx/base.txt, social-auth-core
|
||||
python3-saml==1.9.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.txt
|
||||
pytz==2021.1 # via -r requirements/edx/base.txt, babel, capa, celery, django, django-ses, edx-completion, edx-enterprise, edx-event-routing-backends, edx-proctoring, edx-submissions, edx-tincan-py35, event-tracking, fs, icalendar, ora2, tincan, xblock
|
||||
pytz==2021.1 # via -r requirements/edx/base.txt, babel, capa, celery, django, django-ses, edx-completion, edx-enterprise, edx-event-routing-backends, edx-proctoring, edx-submissions, edx-tincan-py35, event-tracking, fs, icalendar, olxcleaner, ora2, tincan, xblock
|
||||
pyuca==1.2 # via -r requirements/edx/base.txt
|
||||
pyyaml==5.4.1 # via -r requirements/edx/base.txt, code-annotations, edx-django-release-util, edx-i18n-tools, xblock
|
||||
random2==1.0.1 # via -r requirements/edx/base.txt
|
||||
|
||||
Reference in New Issue
Block a user