Merge branch 'master' into kiran/remove-unused-task-code

This commit is contained in:
Kiran Chauhan
2025-11-08 16:26:59 +05:30
committed by GitHub
21 changed files with 493 additions and 122 deletions

View File

@@ -7,12 +7,12 @@ from itertools import chain
from config_models.models import ConfigurationModel
from django.db import models
from django.db.models import QuerySet, OuterRef, Case, When, Exists, Value, ExpressionWrapper
from django.db.models.fields import IntegerField, TextField, BooleanField
from django.db.models import Case, Exists, ExpressionWrapper, OuterRef, Q, QuerySet, Value, When
from django.db.models.fields import BooleanField, IntegerField, TextField
from django.db.models.functions import Coalesce
from django.db.models.lookups import GreaterThan
from django.utils.translation import gettext_lazy as _
from opaque_keys.edx.django.models import CourseKeyField, ContainerKeyField, UsageKeyField
from opaque_keys.edx.django.models import ContainerKeyField, CourseKeyField, UsageKeyField
from opaque_keys.edx.keys import CourseKey, UsageKey
from opaque_keys.edx.locator import LibraryContainerLocator
from openedx_learning.api.authoring import get_published_version
@@ -23,7 +23,6 @@ from openedx_learning.lib.fields import (
manual_date_time_field,
)
logger = logging.getLogger(__name__)
@@ -391,7 +390,7 @@ class ContainerLink(EntityLinkBase):
cls.objects.filter(**link_filter).select_related(*RELATED_FIELDS),
)
if ready_to_sync is not None:
result = result.filter(ready_to_sync=ready_to_sync)
result = result.filter(Q(ready_to_sync=ready_to_sync) | Q(ready_to_sync_from_children=ready_to_sync))
# Handle top-level parents logic
if use_top_level_parents:
@@ -436,6 +435,11 @@ class ContainerLink(EntityLinkBase):
),
then=1
),
# If upstream block was deleted, set ready_to_sync = True
When(
Q(upstream_container__publishable_entity__published__version__version_num__isnull=True),
then=1
),
default=0,
output_field=models.IntegerField()
)
@@ -457,6 +461,11 @@ class ContainerLink(EntityLinkBase):
),
then=1
),
# If upstream block was deleted, set ready_to_sync = True
When(
Q(upstream_block__publishable_entity__published__version__version_num__isnull=True),
then=1
),
default=0,
output_field=models.IntegerField()
)

View File

@@ -23,7 +23,7 @@ from common.djangoapps.student.roles import CourseStaffRole
from common.djangoapps.student.tests.factories import UserFactory
from openedx.core.djangoapps.content_libraries import api as lib_api
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase, ImmediateOnCommitMixin
from xmodule.modulestore.tests.django_utils import ImmediateOnCommitMixin, SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import BlockFactory, CourseFactory
from .. import downstreams as downstreams_views
@@ -32,6 +32,7 @@ MOCK_UPSTREAM_ERROR = "your LibraryGPT subscription has expired"
URL_PREFIX = '/api/libraries/v2/'
URL_LIB_CREATE = URL_PREFIX
URL_LIB_BLOCKS = URL_PREFIX + '{lib_key}/blocks/'
URL_LIB_BLOCK = URL_PREFIX + 'blocks/{block_key}/'
URL_LIB_BLOCK_PUBLISH = URL_PREFIX + 'blocks/{block_key}/publish/'
URL_LIB_BLOCK_OLX = URL_PREFIX + 'blocks/{block_key}/olx/'
URL_LIB_CONTAINER = URL_PREFIX + 'containers/{container_key}/' # Get a container in this library
@@ -277,6 +278,10 @@ class _BaseDownstreamViewTestMixin:
data["slug"] = slug
return self._api('post', URL_LIB_CONTAINERS.format(lib_key=lib_key), data, expect_response)
def _delete_component(self, block_key, expect_response=200):
""" Publish all changes in the specified container + children """
return self._api('delete', URL_LIB_BLOCK.format(block_key=block_key), None, expect_response)
class SharedErrorTestCases(_BaseDownstreamViewTestMixin):
"""
@@ -1503,3 +1508,109 @@ class GetDownstreamSummaryViewTest(
'last_published_at': self.now.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
}]
self.assertListEqual(data, expected)
class GetDownstreamDeletedUpstream(
_BaseDownstreamViewTestMixin,
ImmediateOnCommitMixin,
SharedModuleStoreTestCase,
):
"""
Test that parent container is marked ready_to_sync when even when the only change is a deleted component under it
"""
def call_api(
self,
course_id: str | None = None,
ready_to_sync: bool | None = None,
upstream_key: str | None = None,
item_type: str | None = None,
use_top_level_parents: bool | None = None,
):
data = {}
if course_id is not None:
data["course_id"] = str(course_id)
if ready_to_sync is not None:
data["ready_to_sync"] = str(ready_to_sync)
if upstream_key is not None:
data["upstream_key"] = str(upstream_key)
if item_type is not None:
data["item_type"] = str(item_type)
if use_top_level_parents is not None:
data["use_top_level_parents"] = str(use_top_level_parents)
return self.client.get("/api/contentstore/v2/downstreams/", data=data)
def test_delete_component_should_be_ready_to_sync(self):
"""
Test deleting a component from library should mark the entire section container ready to sync
"""
# Create blocks
section_id = self._create_container(self.library_id, "section", "section-12", "Section 12")["id"]
subsection_id = self._create_container(self.library_id, "subsection", "subsection-12", "Subsection 12")["id"]
unit_id = self._create_container(self.library_id, "unit", "unit-12", "Unit 12")["id"]
video_id = self._add_block_to_library(self.library_id, "video", "video-bar-13")["id"]
section_key = ContainerKey.from_string(section_id)
subsection_key = ContainerKey.from_string(subsection_id)
unit_key = ContainerKey.from_string(unit_id)
video_key = LibraryUsageLocatorV2.from_string(video_id)
# Set children
lib_api.update_container_children(section_key, [subsection_key], None)
lib_api.update_container_children(subsection_key, [unit_key], None)
lib_api.update_container_children(unit_key, [video_key], None)
self._publish_container(unit_id)
self._publish_container(subsection_id)
self._publish_container(section_id)
self._publish_library_block(video_id)
course = CourseFactory.create(display_name="Course New")
add_users(self.superuser, CourseStaffRole(course.id), self.course_user)
chapter = BlockFactory.create(
category='chapter', parent=course, upstream=section_id, upstream_version=2,
)
sequential = BlockFactory.create(
category='sequential',
parent=chapter,
upstream=subsection_id,
upstream_version=2,
top_level_downstream_parent_key=get_block_key_string(chapter.usage_key),
)
vertical = BlockFactory.create(
category='vertical',
parent=sequential,
upstream=unit_id,
upstream_version=2,
top_level_downstream_parent_key=get_block_key_string(chapter.usage_key),
)
BlockFactory.create(
category='video',
parent=vertical,
upstream=video_id,
upstream_version=1,
top_level_downstream_parent_key=get_block_key_string(chapter.usage_key),
)
self._delete_component(video_id)
self._publish_container(unit_id)
response = self.call_api(course_id=course.id, ready_to_sync=True, use_top_level_parents=True)
assert response.status_code == 200
data = response.json()['results']
assert len(data) == 1
date_format = self.now.isoformat().split("+")[0] + 'Z'
expected_results = {
'created': date_format,
'downstream_context_key': str(course.id),
'downstream_usage_key': str(chapter.usage_key),
'downstream_customized': [],
'id': 8,
'ready_to_sync': False,
'ready_to_sync_from_children': True,
'top_level_parent_usage_key': None,
'updated': date_format,
'upstream_context_key': self.library_id,
'upstream_context_title': self.library_title,
'upstream_key': section_id,
'upstream_type': 'container',
'upstream_version': 2,
'version_declined': None,
'version_synced': 2,
}
self.assertDictEqual(data[0], expected_results)

View File

@@ -5,11 +5,13 @@ Serializers for the Course to Library Import API.
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import LearningContextKey
from opaque_keys.edx.locator import LibraryLocatorV2
from openedx_learning.api.authoring_models import Collection
from rest_framework import serializers
from user_tasks.models import UserTaskStatus
from user_tasks.serializers import StatusSerializer
from cms.djangoapps.modulestore_migrator.data import CompositionLevel, RepeatHandlingStrategy
from cms.djangoapps.modulestore_migrator.models import ModulestoreMigration
from cms.djangoapps.modulestore_migrator.models import ModulestoreMigration, ModulestoreSource
class ModulestoreMigrationSerializer(serializers.Serializer):
@@ -173,3 +175,65 @@ class StatusWithModulestoreMigrationsSerializer(StatusSerializer):
fields = super().get_fields()
fields.pop('name', None)
return fields
class LibraryMigrationCourseSourceSerializer(serializers.ModelSerializer):
"""
Serializer for the source course of a library migration.
"""
display_name = serializers.SerializerMethodField()
class Meta:
model = ModulestoreSource
fields = ['key', 'display_name']
def get_display_name(self, obj):
"""
Return the display name of the source course
"""
return self.context["course_names"].get(str(obj.key), None)
class LibraryMigrationCollectionSerializer(serializers.ModelSerializer):
"""
Serializer for the target collection of a library migration.
"""
class Meta:
model = Collection
fields = ["key", "title"]
class LibraryMigrationCourseSerializer(serializers.ModelSerializer):
"""
Serializer for the course or legacylibrary migrations to V2 library.
"""
source = LibraryMigrationCourseSourceSerializer() # type: ignore[assignment]
target_collection = LibraryMigrationCollectionSerializer(required=False)
state = serializers.SerializerMethodField()
progress = serializers.SerializerMethodField()
class Meta:
model = ModulestoreMigration
fields = [
'source',
'target_collection',
'state',
'progress',
]
def get_state(self, obj: ModulestoreMigration):
"""
Return the state of the migration.
"""
if obj.is_failed or obj.task_status.state in [UserTaskStatus.FAILED, UserTaskStatus.CANCELED]:
return UserTaskStatus.FAILED
elif obj.task_status.state == UserTaskStatus.SUCCEEDED:
return UserTaskStatus.SUCCEEDED
return UserTaskStatus.IN_PROGRESS
def get_progress(self, obj: ModulestoreMigration):
"""
Return the progress of the migration.
"""
return obj.task_status.completed_steps / obj.task_status.total_steps

View File

@@ -3,10 +3,17 @@ Course to Library Import API v1 URLs.
"""
from rest_framework.routers import SimpleRouter
from .views import MigrationViewSet, BulkMigrationViewSet
from .views import BulkMigrationViewSet, LibraryCourseMigrationViewSet, MigrationViewSet
ROUTER = SimpleRouter()
ROUTER.register(r'migrations', MigrationViewSet, basename='migrations')
ROUTER.register(r'bulk_migration', BulkMigrationViewSet, basename='bulk-migration')
ROUTER.register(
r'library/(?P<lib_key_str>[^/.]+)/migrations/courses',
LibraryCourseMigrationViewSet,
basename='library-migrations',
)
urlpatterns = ROUTER.urls

View File

@@ -6,22 +6,30 @@ import logging
import edx_api_doc_tools as apidocs
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
from edx_rest_framework_extensions.auth.session.authentication import SessionAuthenticationAllowInactiveUser
from opaque_keys import InvalidKeyError
from opaque_keys.edx.locator import LibraryLocatorV2
from rest_framework import status
from rest_framework.exceptions import ParseError
from rest_framework.mixins import ListModelMixin
from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response
from rest_framework import status
from rest_framework.viewsets import GenericViewSet
from user_tasks.models import UserTaskStatus
from user_tasks.views import StatusViewSet
from cms.djangoapps.modulestore_migrator.api import start_migration_to_library, start_bulk_migration_to_library
from cms.djangoapps.modulestore_migrator.api import start_bulk_migration_to_library, start_migration_to_library
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.content_libraries import api as lib_api
from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser
from ...models import ModulestoreMigration
from .serializers import (
StatusWithModulestoreMigrationsSerializer,
ModulestoreMigrationSerializer,
BulkModulestoreMigrationSerializer,
LibraryMigrationCourseSerializer,
ModulestoreMigrationSerializer,
StatusWithModulestoreMigrationsSerializer,
)
log = logging.getLogger(__name__)
@@ -328,3 +336,55 @@ class BulkMigrationViewSet(StatusViewSet):
We disable this endpoint to avoid confusion.
"""
raise NotImplementedError
@apidocs.schema_for(
"list",
"List all course migrations to a library.",
responses={
201: LibraryMigrationCourseSerializer,
401: "The requester is not authenticated.",
403: "The requester does not have permission to access the library.",
},
)
class LibraryCourseMigrationViewSet(GenericViewSet, ListModelMixin):
"""
Show infomation about migrations related to a destination library.
"""
serializer_class = LibraryMigrationCourseSerializer
pagination_class = None
queryset = ModulestoreMigration.objects.all().select_related('target_collection', 'target', 'task_status')
def get_serializer_context(self):
"""
Add course name list to the serializer context.
We need to display the course names in the migration view, and we get all of
them here to avoid futher queries.
"""
context = super().get_serializer_context()
queryset = self.get_queryset()
course_keys = queryset.values_list('source__key', flat=True)
courses = CourseOverview.get_all_courses(course_keys=course_keys)
context['course_names'] = dict((str(course.id), course.display_name) for course in courses)
return context
def get_queryset(self):
"""
Override the default queryset to filter by the library key and check permissions.
"""
queryset = super().get_queryset()
lib_key_str = self.kwargs['lib_key_str']
try:
library_key = LibraryLocatorV2.from_string(lib_key_str)
except InvalidKeyError as exc:
raise ParseError(detail=f"Malformed library key: {lib_key_str}") from exc
lib_api.require_permission_for_library_key(
library_key,
self.request.user,
lib_api.permissions.CAN_VIEW_THIS_CONTENT_LIBRARY
)
queryset = queryset.filter(target__key=library_key, source__key__startswith='course-v1')
return queryset

View File

@@ -87,6 +87,13 @@ class UpstreamLink:
downstream_customized: list[str] | None # List of fields modified in downstream
has_top_level_parent: bool # True if this Upstream link has a top-level parent
@property
def is_upstream_deleted(self) -> bool:
return bool(
self.upstream_ref and
self.version_available is None
)
@property
def is_ready_to_sync_individually(self) -> bool:
return bool(
@@ -94,7 +101,7 @@ class UpstreamLink:
self.version_available and
self.version_available > (self.version_synced or 0) and
self.version_available > (self.version_declined or 0)
)
) or self.is_upstream_deleted
def _check_children_ready_to_sync(self, xblock_downstream: XBlock, return_fast: bool) -> list[dict[str, str]]:
"""

View File

@@ -630,7 +630,7 @@ class ContainersTestCase(ContentLibrariesRestApiTest):
]
def test_subsection_hierarchy(self):
with self.assertNumQueries(93):
with self.assertNumQueries(95):
hierarchy = self._get_container_hierarchy(self.subsection_with_units["id"])
assert hierarchy["object_key"] == self.subsection_with_units["id"]
assert hierarchy["components"] == [
@@ -653,7 +653,7 @@ class ContainersTestCase(ContentLibrariesRestApiTest):
]
def test_units_hierarchy(self):
with self.assertNumQueries(56):
with self.assertNumQueries(60):
hierarchy = self._get_container_hierarchy(self.unit_with_components["id"])
assert hierarchy["object_key"] == self.unit_with_components["id"]
assert hierarchy["components"] == [
@@ -679,7 +679,7 @@ class ContainersTestCase(ContentLibrariesRestApiTest):
)
def test_block_hierarchy(self):
with self.assertNumQueries(21):
with self.assertNumQueries(27):
hierarchy = self._get_block_hierarchy(self.problem_block["id"])
assert hierarchy["object_key"] == self.problem_block["id"]
assert hierarchy["components"] == [

View File

@@ -16,7 +16,7 @@ SORTED_COUNTRIES = [
("AU", "Australia"),
("AT", "Austria"),
("AZ", "Azerbaijan"),
("BS", "Bahamas"),
("BS", "Bahamas (The)"),
("BH", "Bahrain"),
("BD", "Bangladesh"),
("BB", "Barbados"),
@@ -51,7 +51,6 @@ SORTED_COUNTRIES = [
("CO", "Colombia"),
("KM", "Comoros"),
("CG", "Congo"),
("CD", "Congo (the Democratic Republic of the)"),
("CK", "Cook Islands"),
("CR", "Costa Rica"),
("CI", "C\xf4te d'Ivoire"),
@@ -60,6 +59,7 @@ SORTED_COUNTRIES = [
("CW", "Cura\xe7ao"),
("CY", "Cyprus"),
("CZ", "Czechia"),
("CD", "Democratic Republic of the Congo"),
("DK", "Denmark"),
("DJ", "Djibouti"),
("DM", "Dominica"),
@@ -98,7 +98,6 @@ SORTED_COUNTRIES = [
("GY", "Guyana"),
("HT", "Haiti"),
("HM", "Heard Island and McDonald Islands"),
("VA", "Holy See"),
("HN", "Honduras"),
("HK", "Hong Kong"),
("HU", "Hungary"),
@@ -170,7 +169,7 @@ SORTED_COUNTRIES = [
("OM", "Oman"),
("PK", "Pakistan"),
("PW", "Palau"),
("PS", "Palestine, State of"),
("PS", "Palestine"),
("PA", "Panama"),
("PG", "Papua New Guinea"),
("PY", "Paraguay"),
@@ -186,7 +185,7 @@ SORTED_COUNTRIES = [
("RU", "Russia"),
("RW", "Rwanda"),
("BL", "Saint Barth\xe9lemy"),
("SH", "Saint Helena, Ascension and Tristan da Cunha"),
("SH", "Saint Helena"),
("KN", "Saint Kitts and Nevis"),
("LC", "Saint Lucia"),
("MF", "Saint Martin (French part)"),
@@ -207,7 +206,7 @@ SORTED_COUNTRIES = [
("SB", "Solomon Islands"),
("SO", "Somalia"),
("ZA", "South Africa"),
("GS", "South Georgia and the South Sandwich Islands"),
("GS", "South Georgia"),
("KR", "South Korea"),
("SS", "South Sudan"),
("ES", "Spain"),
@@ -241,6 +240,7 @@ SORTED_COUNTRIES = [
("UY", "Uruguay"),
("UZ", "Uzbekistan"),
("VU", "Vanuatu"),
("VA", "Vatican City"),
("VE", "Venezuela"),
("VN", "Vietnam"),
("VG", "Virgin Islands (British)"),

View File

@@ -16,14 +16,14 @@
# this file from Github directly. It does not require packaging in edx-lint.
# using LTS django version
Django<6.0
# elasticsearch>=7.14.0 includes breaking changes in it which caused issues in discovery upgrade process.
# elastic search changelog: https://www.elastic.co/guide/en/enterprise-search/master/release-notes-7.14.0.html
# See https://github.com/openedx/edx-platform/issues/35126 for more info
elasticsearch<7.14.0
# pip 25.3 is incompatible with pip-tools hence causing failures during the build process
# pip 25.3 is incompatible with pip-tools hence causing failures during the build process
# Make upgrade command and all requirements upgrade jobs are broken due to this.
# See issue https://github.com/openedx/public-engineering/issues/440 for details regarding the ongoing fix.
# The constraint can be removed once a release (pip-tools > 7.5.1) is available with support for pip 25.3

View File

@@ -61,7 +61,7 @@ numpy<2.0.0
# Date: 2023-09-18
# pinning this version to avoid updates while the library is being developed
# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35269
openedx-learning==0.29.1
openedx-learning==0.30.0
# Date: 2023-11-29
# Open AI version 1.0.0 dropped support for openai.ChatCompletion which is currently in use in enterprise.

View File

@@ -74,3 +74,21 @@ releases/sumac.txt
.. _Python changelog: https://docs.python.org/3.11/whatsnew/changelog.html
.. _SciPy changelog: https://docs.scipy.org/doc/scipy/release.html
.. _NumPy changelog: https://numpy.org/doc/stable/release.html
releases/teak.txt
------------------
* Frozen at the time of the Teak release
* Supports Python 3.11 and Python 3.12
* SciPy is upgraded from 1.14.1 to 1.15.2
.. _SciPy changelog: https://docs.scipy.org/doc/scipy/release.html
releases/ulmo.txt
------------------
* Frozen at the time of the Ulmo release
* Supports Python 3.11 and Python 3.12
* SciPy is upgraded from 1.15.2 to 1.16.3
.. _SciPy changelog: https://docs.scipy.org/doc/scipy/release.html

View File

@@ -74,7 +74,7 @@ python-dateutil==2.9.0.post0
# via matplotlib
random2==1.0.2
# via -r requirements/edx-sandbox/base.in
regex==2025.10.23
regex==2025.11.3
# via nltk
scipy==1.16.3
# via

View File

@@ -0,0 +1,90 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# make upgrade
#
cffi==2.0.0
# via cryptography
chem==2.0.0
# via -r requirements/edx-sandbox/base.in
click==8.3.0
# via nltk
codejail-includes==2.0.0
# via -r requirements/edx-sandbox/base.in
contourpy==1.3.3
# via matplotlib
cryptography==45.0.7
# via
# -c requirements/constraints.txt
# -r requirements/edx-sandbox/base.in
cycler==0.12.1
# via matplotlib
fonttools==4.60.1
# via matplotlib
joblib==1.5.2
# via nltk
kiwisolver==1.4.9
# via matplotlib
lxml[html-clean]==5.3.2
# via
# -c requirements/constraints.txt
# -r requirements/edx-sandbox/base.in
# lxml-html-clean
# openedx-calc
lxml-html-clean==0.4.3
# via lxml
markupsafe==3.0.3
# via
# chem
# openedx-calc
matplotlib==3.10.7
# via -r requirements/edx-sandbox/base.in
mpmath==1.3.0
# via sympy
networkx==3.5
# via -r requirements/edx-sandbox/base.in
nltk==3.9.2
# via
# -r requirements/edx-sandbox/base.in
# chem
numpy==1.26.4
# via
# -c requirements/constraints.txt
# chem
# contourpy
# matplotlib
# openedx-calc
# scipy
openedx-calc==4.0.2
# via -r requirements/edx-sandbox/base.in
packaging==25.0
# via matplotlib
pillow==12.0.0
# via matplotlib
pycparser==2.23
# via cffi
pyparsing==3.2.5
# via
# -r requirements/edx-sandbox/base.in
# chem
# matplotlib
# openedx-calc
python-dateutil==2.9.0.post0
# via matplotlib
random2==1.0.2
# via -r requirements/edx-sandbox/base.in
regex==2025.10.23
# via nltk
scipy==1.16.3
# via
# -r requirements/edx-sandbox/base.in
# chem
six==1.17.0
# via python-dateutil
sympy==1.14.0
# via
# -r requirements/edx-sandbox/base.in
# openedx-calc
tqdm==4.67.1
# via nltk

View File

@@ -69,14 +69,14 @@ bleach[css]==6.3.0
# xblock-poll
boto==2.49.0
# via -r requirements/edx/kernel.in
boto3==1.40.62
boto3==1.40.66
# via
# -r requirements/edx/kernel.in
# django-ses
# fs-s3fs
# ora2
# snowflake-connector-python
botocore==1.40.62
botocore==1.40.66
# via
# -r requirements/edx/kernel.in
# boto3
@@ -170,8 +170,9 @@ defusedxml==0.7.1
# ora2
# python3-openid
# social-auth-core
django==5.2.7
django==5.2.8
# via
# -c requirements/common_constraints.txt
# -c requirements/constraints.txt
# -r requirements/edx/kernel.in
# casbin-django-orm-adapter
@@ -265,7 +266,7 @@ django-config-models==2.9.0
# lti-consumer-xblock
django-cors-headers==4.9.0
# via -r requirements/edx/kernel.in
django-countries==7.6.1
django-countries==8.0.0
# via
# -r requirements/edx/kernel.in
# edx-enterprise
@@ -375,7 +376,7 @@ django-waffle==5.0.0
# edx-enterprise
# edx-proctoring
# edx-toggles
django-webpack-loader==3.2.1
django-webpack-loader==3.2.2
# via
# -r requirements/edx/kernel.in
# edx-proctoring
@@ -408,7 +409,7 @@ done-xblock==2.5.0
# via -r requirements/edx/bundled.in
drf-jwt==1.19.2
# via edx-drf-extensions
drf-spectacular==0.28.0
drf-spectacular==0.29.0
# via -r requirements/edx/kernel.in
drf-yasg==1.21.11
# via
@@ -612,7 +613,7 @@ google-api-core[grpc]==2.28.1
# google-cloud-core
# google-cloud-firestore
# google-cloud-storage
google-auth==2.42.0
google-auth==2.42.1
# via
# google-api-core
# google-cloud-core
@@ -624,7 +625,7 @@ google-cloud-core==2.5.0
# google-cloud-storage
google-cloud-firestore==2.21.0
# via firebase-admin
google-cloud-storage==3.4.1
google-cloud-storage==3.5.0
# via firebase-admin
google-crc32c==1.7.1
# via
@@ -662,7 +663,7 @@ httpx[http2]==0.28.1
# via firebase-admin
hyperframe==6.1.0
# via h2
icalendar==6.3.1
icalendar==6.3.2
# via -r requirements/edx/kernel.in
idna==3.11
# via
@@ -753,7 +754,7 @@ mako==1.3.10
# lti-consumer-xblock
# xblock
# xblock-utils
markdown==3.9
markdown==3.10
# via
# -r requirements/edx/kernel.in
# openedx-django-wiki
@@ -790,7 +791,7 @@ mysqlclient==2.2.7
# via
# -r requirements/edx/kernel.in
# openedx-forum
nh3==0.3.1
nh3==0.3.2
# via
# -r requirements/edx/kernel.in
# xblocks-contrib
@@ -825,7 +826,7 @@ openedx-atlas==0.7.0
# enterprise-integrated-channels
# openedx-authz
# openedx-forum
openedx-authz==0.11.2
openedx-authz==0.13.0
# via -r requirements/edx/kernel.in
openedx-calc==4.0.2
# via -r requirements/edx/kernel.in
@@ -854,7 +855,7 @@ openedx-filters==2.1.0
# ora2
openedx-forum==0.3.8
# via -r requirements/edx/kernel.in
openedx-learning==0.29.1
openedx-learning==0.30.0
# via
# -c requirements/constraints.txt
# -r requirements/edx/kernel.in
@@ -912,7 +913,7 @@ protobuf==6.33.0
# googleapis-common-protos
# grpcio-status
# proto-plus
psutil==7.1.2
psutil==7.1.3
# via
# -r requirements/edx/kernel.in
# edx-django-utils
@@ -936,9 +937,9 @@ pycryptodomex==3.23.0
# -r requirements/edx/kernel.in
# edx-proctoring
# lti-consumer-xblock
pydantic==2.12.3
pydantic==2.12.4
# via camel-converter
pydantic-core==2.41.4
pydantic-core==2.41.5
# via pydantic
pyjwt[crypto]==2.10.1
# via
@@ -1049,7 +1050,7 @@ referencing==0.37.0
# via
# jsonschema
# jsonschema-specifications
regex==2025.10.23
regex==2025.11.3
# via nltk
requests==2.32.5
# via

View File

@@ -141,7 +141,7 @@ boto==2.49.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
boto3==1.40.62
boto3==1.40.66
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -149,7 +149,7 @@ boto3==1.40.62
# fs-s3fs
# ora2
# snowflake-connector-python
botocore==1.40.62
botocore==1.40.66
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -343,8 +343,9 @@ distlib==0.4.0
# via
# -r requirements/edx/testing.txt
# virtualenv
django==5.2.7
django==5.2.8
# via
# -c requirements/common_constraints.txt
# -c requirements/constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -458,7 +459,7 @@ django-cors-headers==4.9.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
django-countries==7.6.1
django-countries==8.0.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -616,7 +617,7 @@ django-waffle==5.0.0
# edx-enterprise
# edx-proctoring
# edx-toggles
django-webpack-loader==3.2.1
django-webpack-loader==3.2.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -670,7 +671,7 @@ drf-jwt==1.19.2
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# edx-drf-extensions
drf-spectacular==0.28.0
drf-spectacular==0.29.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -915,7 +916,7 @@ faker==37.12.0
# via
# -r requirements/edx/testing.txt
# factory-boy
fastapi==0.120.2
fastapi==0.121.0
# via
# -r requirements/edx/testing.txt
# pact-python
@@ -978,7 +979,7 @@ google-api-core[grpc]==2.28.1
# google-cloud-core
# google-cloud-firestore
# google-cloud-storage
google-auth==2.42.0
google-auth==2.42.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -997,7 +998,7 @@ google-cloud-firestore==2.21.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# firebase-admin
google-cloud-storage==3.4.1
google-cloud-storage==3.5.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1080,7 +1081,7 @@ hyperframe==6.1.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# h2
icalendar==6.3.1
icalendar==6.3.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1248,7 +1249,7 @@ mako==1.3.10
# lti-consumer-xblock
# xblock
# xblock-utils
markdown==3.9
markdown==3.10
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1325,7 +1326,7 @@ mysqlclient==2.2.7
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# openedx-forum
nh3==0.3.1
nh3==0.3.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1375,7 +1376,7 @@ openedx-atlas==0.7.0
# enterprise-integrated-channels
# openedx-authz
# openedx-forum
openedx-authz==0.11.2
openedx-authz==0.13.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1418,7 +1419,7 @@ openedx-forum==0.3.8
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
openedx-learning==0.29.1
openedx-learning==0.30.0
# via
# -c requirements/constraints.txt
# -r requirements/edx/doc.txt
@@ -1539,7 +1540,7 @@ protobuf==6.33.0
# googleapis-common-protos
# grpcio-status
# proto-plus
psutil==7.1.2
psutil==7.1.3
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1585,13 +1586,13 @@ pycryptodomex==3.23.0
# -r requirements/edx/testing.txt
# edx-proctoring
# lti-consumer-xblock
pydantic==2.12.3
pydantic==2.12.4
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# camel-converter
# fastapi
pydantic-core==2.41.4
pydantic-core==2.41.5
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1830,7 +1831,7 @@ referencing==0.37.0
# -r requirements/edx/testing.txt
# jsonschema
# jsonschema-specifications
regex==2025.10.23
regex==2025.11.3
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -2073,7 +2074,7 @@ staff-graded-xblock==3.1.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
starlette==0.49.1
starlette==0.49.3
# via
# -r requirements/edx/testing.txt
# fastapi

View File

@@ -104,14 +104,14 @@ bleach[css]==6.3.0
# xblock-poll
boto==2.49.0
# via -r requirements/edx/base.txt
boto3==1.40.62
boto3==1.40.66
# via
# -r requirements/edx/base.txt
# django-ses
# fs-s3fs
# ora2
# snowflake-connector-python
botocore==1.40.62
botocore==1.40.66
# via
# -r requirements/edx/base.txt
# boto3
@@ -232,8 +232,9 @@ defusedxml==0.7.1
# ora2
# python3-openid
# social-auth-core
django==5.2.7
django==5.2.8
# via
# -c requirements/common_constraints.txt
# -c requirements/constraints.txt
# -r requirements/edx/base.txt
# casbin-django-orm-adapter
@@ -333,7 +334,7 @@ django-config-models==2.9.0
# lti-consumer-xblock
django-cors-headers==4.9.0
# via -r requirements/edx/base.txt
django-countries==7.6.1
django-countries==8.0.0
# via
# -r requirements/edx/base.txt
# edx-enterprise
@@ -451,7 +452,7 @@ django-waffle==5.0.0
# edx-enterprise
# edx-proctoring
# edx-toggles
django-webpack-loader==3.2.1
django-webpack-loader==3.2.2
# via
# -r requirements/edx/base.txt
# edx-proctoring
@@ -495,7 +496,7 @@ drf-jwt==1.19.2
# via
# -r requirements/edx/base.txt
# edx-drf-extensions
drf-spectacular==0.28.0
drf-spectacular==0.29.0
# via -r requirements/edx/base.txt
drf-yasg==1.21.11
# via
@@ -717,7 +718,7 @@ google-api-core[grpc]==2.28.1
# google-cloud-core
# google-cloud-firestore
# google-cloud-storage
google-auth==2.42.0
google-auth==2.42.1
# via
# -r requirements/edx/base.txt
# google-api-core
@@ -733,7 +734,7 @@ google-cloud-firestore==2.21.0
# via
# -r requirements/edx/base.txt
# firebase-admin
google-cloud-storage==3.4.1
google-cloud-storage==3.5.0
# via
# -r requirements/edx/base.txt
# firebase-admin
@@ -792,7 +793,7 @@ hyperframe==6.1.0
# via
# -r requirements/edx/base.txt
# h2
icalendar==6.3.1
icalendar==6.3.2
# via -r requirements/edx/base.txt
idna==3.11
# via
@@ -912,7 +913,7 @@ mako==1.3.10
# lti-consumer-xblock
# xblock
# xblock-utils
markdown==3.9
markdown==3.10
# via
# -r requirements/edx/base.txt
# openedx-django-wiki
@@ -963,7 +964,7 @@ mysqlclient==2.2.7
# via
# -r requirements/edx/base.txt
# openedx-forum
nh3==0.3.1
nh3==0.3.2
# via
# -r requirements/edx/base.txt
# xblocks-contrib
@@ -1002,7 +1003,7 @@ openedx-atlas==0.7.0
# enterprise-integrated-channels
# openedx-authz
# openedx-forum
openedx-authz==0.11.2
openedx-authz==0.13.0
# via -r requirements/edx/base.txt
openedx-calc==4.0.2
# via -r requirements/edx/base.txt
@@ -1032,7 +1033,7 @@ openedx-filters==2.1.0
# ora2
openedx-forum==0.3.8
# via -r requirements/edx/base.txt
openedx-learning==0.29.1
openedx-learning==0.30.0
# via
# -c requirements/constraints.txt
# -r requirements/edx/base.txt
@@ -1109,7 +1110,7 @@ protobuf==6.33.0
# googleapis-common-protos
# grpcio-status
# proto-plus
psutil==7.1.2
psutil==7.1.3
# via
# -r requirements/edx/base.txt
# edx-django-utils
@@ -1139,11 +1140,11 @@ pycryptodomex==3.23.0
# -r requirements/edx/base.txt
# edx-proctoring
# lti-consumer-xblock
pydantic==2.12.3
pydantic==2.12.4
# via
# -r requirements/edx/base.txt
# camel-converter
pydantic-core==2.41.4
pydantic-core==2.41.5
# via
# -r requirements/edx/base.txt
# pydantic
@@ -1281,7 +1282,7 @@ referencing==0.37.0
# -r requirements/edx/base.txt
# jsonschema
# jsonschema-specifications
regex==2025.10.23
regex==2025.11.3
# via
# -r requirements/edx/base.txt
# nltk

View File

@@ -113,17 +113,17 @@ packaging==25.0
# via
# opentelemetry-instrumentation
# semgrep
peewee==3.18.2
peewee==3.18.3
# via semgrep
protobuf==6.33.0
# via
# googleapis-common-protos
# opentelemetry-proto
pydantic==2.12.3
pydantic==2.12.4
# via
# mcp
# pydantic-settings
pydantic-core==2.41.4
pydantic-core==2.41.5
# via pydantic
pydantic-settings==2.11.0
# via mcp
@@ -153,13 +153,13 @@ ruamel-yaml-clib==0.2.14
# via
# ruamel-yaml
# semgrep
semgrep==1.141.1
semgrep==1.142.0
# via -r requirements/edx/semgrep.in
sniffio==1.3.1
# via anyio
sse-starlette==3.0.2
sse-starlette==3.0.3
# via mcp
starlette==0.49.1
starlette==0.50.0
# via mcp
tomli==2.0.2
# via semgrep

View File

@@ -103,14 +103,14 @@ bleach[css]==6.3.0
# xblock-poll
boto==2.49.0
# via -r requirements/edx/base.txt
boto3==1.40.62
boto3==1.40.66
# via
# -r requirements/edx/base.txt
# django-ses
# fs-s3fs
# ora2
# snowflake-connector-python
botocore==1.40.62
botocore==1.40.66
# via
# -r requirements/edx/base.txt
# boto3
@@ -259,8 +259,9 @@ dill==0.4.0
# via pylint
distlib==0.4.0
# via virtualenv
django==5.2.7
django==5.2.8
# via
# -c requirements/common_constraints.txt
# -c requirements/constraints.txt
# -r requirements/edx/base.txt
# casbin-django-orm-adapter
@@ -360,7 +361,7 @@ django-config-models==2.9.0
# lti-consumer-xblock
django-cors-headers==4.9.0
# via -r requirements/edx/base.txt
django-countries==7.6.1
django-countries==8.0.0
# via
# -r requirements/edx/base.txt
# edx-enterprise
@@ -478,7 +479,7 @@ django-waffle==5.0.0
# edx-enterprise
# edx-proctoring
# edx-toggles
django-webpack-loader==3.2.1
django-webpack-loader==3.2.2
# via
# -r requirements/edx/base.txt
# edx-proctoring
@@ -517,7 +518,7 @@ drf-jwt==1.19.2
# via
# -r requirements/edx/base.txt
# edx-drf-extensions
drf-spectacular==0.28.0
drf-spectacular==0.29.0
# via -r requirements/edx/base.txt
drf-yasg==1.21.11
# via
@@ -705,7 +706,7 @@ factory-boy==3.3.3
# via -r requirements/edx/testing.in
faker==37.12.0
# via factory-boy
fastapi==0.120.2
fastapi==0.121.0
# via pact-python
fastavro==1.12.1
# via
@@ -749,7 +750,7 @@ google-api-core[grpc]==2.28.1
# google-cloud-core
# google-cloud-firestore
# google-cloud-storage
google-auth==2.42.0
google-auth==2.42.1
# via
# -r requirements/edx/base.txt
# google-api-core
@@ -765,7 +766,7 @@ google-cloud-firestore==2.21.0
# via
# -r requirements/edx/base.txt
# firebase-admin
google-cloud-storage==3.4.1
google-cloud-storage==3.5.0
# via
# -r requirements/edx/base.txt
# firebase-admin
@@ -829,7 +830,7 @@ hyperframe==6.1.0
# via
# -r requirements/edx/base.txt
# h2
icalendar==6.3.1
icalendar==6.3.2
# via -r requirements/edx/base.txt
idna==3.11
# via
@@ -955,7 +956,7 @@ mako==1.3.10
# lti-consumer-xblock
# xblock
# xblock-utils
markdown==3.9
markdown==3.10
# via
# -r requirements/edx/base.txt
# openedx-django-wiki
@@ -1009,7 +1010,7 @@ mysqlclient==2.2.7
# via
# -r requirements/edx/base.txt
# openedx-forum
nh3==0.3.1
nh3==0.3.2
# via
# -r requirements/edx/base.txt
# xblocks-contrib
@@ -1048,7 +1049,7 @@ openedx-atlas==0.7.0
# enterprise-integrated-channels
# openedx-authz
# openedx-forum
openedx-authz==0.11.2
openedx-authz==0.13.0
# via -r requirements/edx/base.txt
openedx-calc==4.0.2
# via -r requirements/edx/base.txt
@@ -1078,7 +1079,7 @@ openedx-filters==2.1.0
# ora2
openedx-forum==0.3.8
# via -r requirements/edx/base.txt
openedx-learning==0.29.1
openedx-learning==0.30.0
# via
# -c requirements/constraints.txt
# -r requirements/edx/base.txt
@@ -1169,7 +1170,7 @@ protobuf==6.33.0
# googleapis-common-protos
# grpcio-status
# proto-plus
psutil==7.1.2
psutil==7.1.3
# via
# -r requirements/edx/base.txt
# edx-django-utils
@@ -1207,12 +1208,12 @@ pycryptodomex==3.23.0
# -r requirements/edx/base.txt
# edx-proctoring
# lti-consumer-xblock
pydantic==2.12.3
pydantic==2.12.4
# via
# -r requirements/edx/base.txt
# camel-converter
# fastapi
pydantic-core==2.41.4
pydantic-core==2.41.5
# via
# -r requirements/edx/base.txt
# pydantic
@@ -1393,7 +1394,7 @@ referencing==0.37.0
# -r requirements/edx/base.txt
# jsonschema
# jsonschema-specifications
regex==2025.10.23
regex==2025.11.3
# via
# -r requirements/edx/base.txt
# nltk
@@ -1536,7 +1537,7 @@ sqlparse==0.5.3
# django
staff-graded-xblock==3.1.0
# via -r requirements/edx/base.txt
starlette==0.49.1
starlette==0.49.3
# via fastapi
stevedore==5.5.0
# via

View File

@@ -10,9 +10,9 @@ attrs==25.4.0
# via zeep
backoff==2.2.1
# via -r scripts/user_retirement/requirements/base.in
boto3==1.40.62
boto3==1.40.66
# via -r scripts/user_retirement/requirements/base.in
botocore==1.40.62
botocore==1.40.66
# via
# boto3
# s3transfer
@@ -34,8 +34,9 @@ cryptography==45.0.7
# via
# -c requirements/constraints.txt
# pyjwt
django==5.2.7
django==5.2.8
# via
# -c requirements/common_constraints.txt
# -c requirements/constraints.txt
# django-crum
# django-waffle
@@ -50,14 +51,14 @@ edx-rest-api-client==6.2.0
# via -r scripts/user_retirement/requirements/base.in
google-api-core==2.28.1
# via google-api-python-client
google-api-python-client==2.185.0
google-api-python-client==2.186.0
# via -r scripts/user_retirement/requirements/base.in
google-auth==2.42.0
google-auth==2.42.1
# via
# google-api-core
# google-api-python-client
# google-auth-httplib2
google-auth-httplib2==0.2.0
google-auth-httplib2==0.2.1
# via google-api-python-client
googleapis-common-protos==1.71.0
# via google-api-core
@@ -69,7 +70,7 @@ idna==3.11
# via requests
isodate==0.7.2
# via zeep
jenkinsapi==0.3.16
jenkinsapi==0.3.17
# via -r scripts/user_retirement/requirements/base.in
jmespath==1.0.1
# via
@@ -90,7 +91,7 @@ protobuf==6.33.0
# google-api-core
# googleapis-common-protos
# proto-plus
psutil==7.1.2
psutil==7.1.3
# via edx-django-utils
pyasn1==0.6.1
# via

View File

@@ -14,11 +14,11 @@ attrs==25.4.0
# zeep
backoff==2.2.1
# via -r scripts/user_retirement/requirements/base.txt
boto3==1.40.62
boto3==1.40.66
# via
# -r scripts/user_retirement/requirements/base.txt
# moto
botocore==1.40.62
botocore==1.40.66
# via
# -r scripts/user_retirement/requirements/base.txt
# boto3
@@ -52,7 +52,7 @@ cryptography==45.0.7
# pyjwt
ddt==1.7.2
# via -r scripts/user_retirement/requirements/testing.in
django==5.2.7
django==5.2.8
# via
# -r scripts/user_retirement/requirements/base.txt
# django-crum
@@ -76,15 +76,15 @@ google-api-core==2.28.1
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-python-client
google-api-python-client==2.185.0
google-api-python-client==2.186.0
# via -r scripts/user_retirement/requirements/base.txt
google-auth==2.42.0
google-auth==2.42.1
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-core
# google-api-python-client
# google-auth-httplib2
google-auth-httplib2==0.2.0
google-auth-httplib2==0.2.1
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-python-client
@@ -107,7 +107,7 @@ isodate==0.7.2
# via
# -r scripts/user_retirement/requirements/base.txt
# zeep
jenkinsapi==0.3.16
jenkinsapi==0.3.17
# via -r scripts/user_retirement/requirements/base.txt
jinja2==3.1.6
# via moto
@@ -130,7 +130,7 @@ more-itertools==10.8.0
# via
# -r scripts/user_retirement/requirements/base.txt
# simple-salesforce
moto==5.1.15
moto==5.1.16
# via -r scripts/user_retirement/requirements/testing.in
packaging==25.0
# via pytest
@@ -150,7 +150,7 @@ protobuf==6.33.0
# google-api-core
# googleapis-common-protos
# proto-plus
psutil==7.1.2
psutil==7.1.3
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils