From 8fb033a502fc65bcb3afedd438826aaf9ad6b58c Mon Sep 17 00:00:00 2001
From: hamzawaleed01
Date: Mon, 5 Aug 2024 15:41:48 +0500
Subject: [PATCH 001/242] feat: add PA role
---
lms/envs/common.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 9f9004976e..0dc679f763 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -56,7 +56,8 @@ from enterprise.constants import (
ENTERPRISE_FULFILLMENT_OPERATOR_ROLE,
ENTERPRISE_REPORTING_CONFIG_ADMIN_ROLE,
ENTERPRISE_SSO_ORCHESTRATOR_OPERATOR_ROLE,
- ENTERPRISE_OPERATOR_ROLE
+ ENTERPRISE_OPERATOR_ROLE,
+ SYSTEM_ENTERPRISE_PROVISIONING_ADMIN_ROLE
)
from openedx.core.constants import COURSE_KEY_REGEX, COURSE_KEY_PATTERN, COURSE_ID_PATTERN
@@ -4740,6 +4741,7 @@ SYSTEM_TO_FEATURE_ROLE_MAPPING = {
ENTERPRISE_FULFILLMENT_OPERATOR_ROLE,
ENTERPRISE_SSO_ORCHESTRATOR_OPERATOR_ROLE,
],
+ SYSTEM_ENTERPRISE_PROVISIONING_ADMIN_ROLE: [SYSTEM_ENTERPRISE_PROVISIONING_ADMIN_ROLE]
}
DATA_CONSENT_SHARE_CACHE_TIMEOUT = 8 * 60 * 60 # 8 hours
From 05bf82c6552defcc0904e35a1f1894e9a7597166 Mon Sep 17 00:00:00 2001
From: Prashant Makwana
Date: Mon, 5 Aug 2024 16:50:43 +0000
Subject: [PATCH 002/242] chore: bumping enteprise version to 4.22.4
---
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 1e2953c7d7..89f265931d 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -20,7 +20,7 @@ celery>=5.2.2,<6.0.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==4.22.2
+edx-enterprise==4.22.4
# Stay on LTS version, remove once this is added to common constraint
Django<5.0
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 45d33a16ce..5ce0de2aaf 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -456,7 +456,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.22.2
+edx-enterprise==4.22.4
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index 656895c6ab..cfadac2df6 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -727,7 +727,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.22.2
+edx-enterprise==4.22.4
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 0e6235d3bf..120986d6f3 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -528,7 +528,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.22.2
+edx-enterprise==4.22.4
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 0c7b2d33b8..3f948fa735 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -559,7 +559,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.22.2
+edx-enterprise==4.22.4
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
From 8aac664e6758e63122a1537d1d31f2f6fada167b Mon Sep 17 00:00:00 2001
From: Braden MacDonald
Date: Mon, 5 Aug 2024 12:00:14 -0700
Subject: [PATCH 003/242] fix: error when saving changes to a v2 library block
(#35217)
---
openedx/core/djangoapps/xblock/rest_api/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/openedx/core/djangoapps/xblock/rest_api/views.py b/openedx/core/djangoapps/xblock/rest_api/views.py
index 501386efba..8c2d16839a 100644
--- a/openedx/core/djangoapps/xblock/rest_api/views.py
+++ b/openedx/core/djangoapps/xblock/rest_api/views.py
@@ -257,7 +257,7 @@ class BlockFieldsView(APIView):
# Signal that we've modified this block
context_impl = get_learning_context_impl(usage_key)
- context_impl.send_updated_event(usage_key)
+ context_impl.send_block_updated_event(usage_key)
return Response({
"id": str(block.location),
From 48c5856a3f3bfbd25265b40761fa887b931bf53d Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 6 Aug 2024 07:39:42 +0000
Subject: [PATCH 004/242] fix(deps): update dependency requirejs to v2.3.7
[security]
---
package-lock.json | 9 +++++----
package.json | 2 +-
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 04728ccaab..02dde6f701 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -59,7 +59,7 @@
"react-slick": "0.29.0",
"redux": "3.7.2",
"redux-thunk": "2.2.0",
- "requirejs": "2.3.6",
+ "requirejs": "2.3.7",
"rtlcss": "2.6.2",
"sass": "^1.54.8",
"sass-loader": "^14.1.1",
@@ -20862,9 +20862,10 @@
}
},
"node_modules/requirejs": {
- "version": "2.3.6",
- "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz",
- "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==",
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.7.tgz",
+ "integrity": "sha512-DouTG8T1WanGok6Qjg2SXuCMzszOo0eHeH9hDZ5Y4x8Je+9JB38HdTLT4/VA8OaUhBa0JPVHJ0pyBkM1z+pDsw==",
+ "license": "MIT",
"bin": {
"r_js": "bin/r.js",
"r.js": "bin/r.js"
diff --git a/package.json b/package.json
index d182d83d31..49ab2820d1 100644
--- a/package.json
+++ b/package.json
@@ -65,7 +65,7 @@
"react-slick": "0.29.0",
"redux": "3.7.2",
"redux-thunk": "2.2.0",
- "requirejs": "2.3.6",
+ "requirejs": "2.3.7",
"rtlcss": "2.6.2",
"sass": "^1.54.8",
"sass-loader": "^14.1.1",
From 0a067ffa829c4fd89a67b59ece907c43ac46e280 Mon Sep 17 00:00:00 2001
From: Awais Qureshi
Date: Tue, 6 Aug 2024 15:25:24 +0500
Subject: [PATCH 005/242] feat: upgrading simple api to drf compatible (4th api
) (#35135)
* feat: upgrading simple api to drf compatible.
---
lms/djangoapps/instructor/views/api.py | 75 +++++++++++----------
lms/djangoapps/instructor/views/api_urls.py | 2 +-
2 files changed, 41 insertions(+), 36 deletions(-)
diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py
index e7a8249645..a58762d0f3 100644
--- a/lms/djangoapps/instructor/views/api.py
+++ b/lms/djangoapps/instructor/views/api.py
@@ -2366,46 +2366,51 @@ def _list_instructor_tasks(request, course_id):
return JsonResponse(response_payload)
-@require_POST
-@ensure_csrf_cookie
-@cache_control(no_cache=True, no_store=True, must_revalidate=True)
-@require_course_permission(permissions.SHOW_TASKS)
-def list_entrance_exam_instructor_tasks(request, course_id):
+@method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True), name='dispatch')
+class ListEntranceExamInstructorTasks(APIView):
"""
List entrance exam related instructor tasks.
-
- Takes either of the following query parameters
- - unique_student_identifier is an email or username
- - all_students is a boolean
"""
- course_id = CourseKey.from_string(course_id)
- course = get_course_by_id(course_id)
- student = request.POST.get('unique_student_identifier', None)
- if student is not None:
- student = get_student_from_identifier(student)
+ permission_classes = (IsAuthenticated, permissions.InstructorPermission)
+ permission_name = permissions.SHOW_TASKS
- try:
- entrance_exam_key = UsageKey.from_string(course.entrance_exam_id).map_into_course(course_id)
- except InvalidKeyError:
- return HttpResponseBadRequest(_("Course has no valid entrance exam section."))
- if student:
- # Specifying for a single student's entrance exam history
- tasks = task_api.get_entrance_exam_instructor_task_history(
- course_id,
- entrance_exam_key,
- student
- )
- else:
- # Specifying for all student's entrance exam history
- tasks = task_api.get_entrance_exam_instructor_task_history(
- course_id,
- entrance_exam_key
- )
+ @method_decorator(ensure_csrf_cookie)
+ def post(self, request, course_id):
+ """
+ List entrance exam related instructor tasks.
- response_payload = {
- 'tasks': list(map(extract_task_features, tasks)),
- }
- return JsonResponse(response_payload)
+ Takes either of the following query parameters
+ - unique_student_identifier is an email or username
+ - all_students is a boolean
+ """
+ course_id = CourseKey.from_string(course_id)
+ course = get_course_by_id(course_id)
+ student = request.POST.get('unique_student_identifier', None)
+ if student is not None:
+ student = get_student_from_identifier(student)
+
+ try:
+ entrance_exam_key = UsageKey.from_string(course.entrance_exam_id).map_into_course(course_id)
+ except InvalidKeyError:
+ return HttpResponseBadRequest(_("Course has no valid entrance exam section."))
+ if student:
+ # Specifying for a single student's entrance exam history
+ tasks = task_api.get_entrance_exam_instructor_task_history(
+ course_id,
+ entrance_exam_key,
+ student
+ )
+ else:
+ # Specifying for all student's entrance exam history
+ tasks = task_api.get_entrance_exam_instructor_task_history(
+ course_id,
+ entrance_exam_key
+ )
+
+ response_payload = {
+ 'tasks': list(map(extract_task_features, tasks)),
+ }
+ return JsonResponse(response_payload)
class ReportDownloadSerializer(serializers.Serializer): # pylint: disable=abstract-method
diff --git a/lms/djangoapps/instructor/views/api_urls.py b/lms/djangoapps/instructor/views/api_urls.py
index 6b072ef0c1..7f94a369ff 100644
--- a/lms/djangoapps/instructor/views/api_urls.py
+++ b/lms/djangoapps/instructor/views/api_urls.py
@@ -40,7 +40,7 @@ urlpatterns = [
path('reset_student_attempts_for_entrance_exam', api.reset_student_attempts_for_entrance_exam,
name='reset_student_attempts_for_entrance_exam'),
path('rescore_entrance_exam', api.rescore_entrance_exam, name='rescore_entrance_exam'),
- path('list_entrance_exam_instructor_tasks', api.list_entrance_exam_instructor_tasks,
+ path('list_entrance_exam_instructor_tasks', api.ListEntranceExamInstructorTasks.as_view(),
name='list_entrance_exam_instructor_tasks'),
path('mark_student_can_skip_entrance_exam', api.mark_student_can_skip_entrance_exam,
name='mark_student_can_skip_entrance_exam'),
From 3c64b747e5c6f1bc0c12296c825ce5610425a1d9 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 6 Aug 2024 10:25:00 -0400
Subject: [PATCH 006/242] feat: Upgrade Python dependency edx-django-utils
(#35235)
Update to latest version with updates to function trace functionality.
Commit generated by workflow `openedx/edx-platform/.github/workflows/upgrade-one-python-dependency.yml@refs/heads/master`
Co-authored-by: dianakhuang <2952947+dianakhuang@users.noreply.github.com>
---
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
scripts/user_retirement/requirements/base.txt | 2 +-
scripts/user_retirement/requirements/testing.txt | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 5ce0de2aaf..cb8799b700 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -427,7 +427,7 @@ edx-django-release-util==1.4.0
# edxval
edx-django-sites-extensions==4.2.0
# via -r requirements/edx/kernel.in
-edx-django-utils==5.14.2
+edx-django-utils==5.15.0
# via
# -r requirements/edx/kernel.in
# django-config-models
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index cfadac2df6..7683bf6dd2 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -696,7 +696,7 @@ edx-django-sites-extensions==4.2.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-edx-django-utils==5.14.2
+edx-django-utils==5.15.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 120986d6f3..39c1b8d668 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -499,7 +499,7 @@ edx-django-release-util==1.4.0
# edxval
edx-django-sites-extensions==4.2.0
# via -r requirements/edx/base.txt
-edx-django-utils==5.14.2
+edx-django-utils==5.15.0
# via
# -r requirements/edx/base.txt
# django-config-models
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 3f948fa735..278423303c 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -530,7 +530,7 @@ edx-django-release-util==1.4.0
# edxval
edx-django-sites-extensions==4.2.0
# via -r requirements/edx/base.txt
-edx-django-utils==5.14.2
+edx-django-utils==5.15.0
# via
# -r requirements/edx/base.txt
# django-config-models
diff --git a/scripts/user_retirement/requirements/base.txt b/scripts/user_retirement/requirements/base.txt
index 30d5df674f..98972d24c4 100644
--- a/scripts/user_retirement/requirements/base.txt
+++ b/scripts/user_retirement/requirements/base.txt
@@ -46,7 +46,7 @@ django-crum==0.7.9
# via edx-django-utils
django-waffle==4.1.0
# via edx-django-utils
-edx-django-utils==5.14.2
+edx-django-utils==5.15.0
# via edx-rest-api-client
edx-rest-api-client==5.7.1
# via -r scripts/user_retirement/requirements/base.in
diff --git a/scripts/user_retirement/requirements/testing.txt b/scripts/user_retirement/requirements/testing.txt
index c10b36f0de..c0c8d5abba 100644
--- a/scripts/user_retirement/requirements/testing.txt
+++ b/scripts/user_retirement/requirements/testing.txt
@@ -66,7 +66,7 @@ django-waffle==4.1.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
-edx-django-utils==5.14.2
+edx-django-utils==5.15.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-rest-api-client
From 94f4c488a601bb435b2479fde872468b7a2e515d Mon Sep 17 00:00:00 2001
From: Diana Huang
Date: Tue, 6 Aug 2024 14:09:25 -0400
Subject: [PATCH 007/242] build: Pin social-auth-app-django as a requirement.
(#35237)
There are a few migrations going into this library
which cause operational headaches for operators.
We would like to pin until the migrations settle down
and then we can unpin this again.
---
requirements/constraints.txt | 6 ++++++
requirements/edx/base.txt | 1 +
requirements/edx/development.txt | 1 +
requirements/edx/doc.txt | 1 +
requirements/edx/testing.txt | 1 +
5 files changed, 10 insertions(+)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 89f265931d..0bdb5ad228 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -130,3 +130,9 @@ numpy<2.0.0
# Two lines were added in 1.14.4 that make file_exists_in_storage function always return False,
# as the default value of AWS_S3_FILE_OVERWRITE is True
django-storages<1.14.4
+
+# social-auth-app-django 5.4.2 introduces a new migration that will not play nicely with large installations. This will touch
+# user tables, which are quite large, especially on instances like edx.org.
+# We are pinning this until after all the smaller migrations get handled and then we can migrate this all at once.
+# Ticket to unpin: https://github.com/edx/edx-arch-experiments/issues/760
+social-auth-app-django<=5.4.1
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index cb8799b700..fcfe7548a0 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -1065,6 +1065,7 @@ snowflake-connector-python==3.11.0
# via edx-enterprise
social-auth-app-django==5.4.1
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
# edx-auth-backends
social-auth-core==4.5.4
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index 7683bf6dd2..d6487d2ffa 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -1884,6 +1884,7 @@ snowflake-connector-python==3.11.0
# edx-enterprise
social-auth-app-django==5.4.1
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# edx-auth-backends
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 39c1b8d668..c90be69760 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -1255,6 +1255,7 @@ snowflake-connector-python==3.11.0
# edx-enterprise
social-auth-app-django==5.4.1
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# edx-auth-backends
social-auth-core==4.5.4
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 278423303c..a679ac373e 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -1416,6 +1416,7 @@ snowflake-connector-python==3.11.0
# edx-enterprise
social-auth-app-django==5.4.1
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# edx-auth-backends
social-auth-core==4.5.4
From 834dc3107388ac08248fe2aac6d3884153187c84 Mon Sep 17 00:00:00 2001
From: edX requirements bot
<49161187+edx-requirements-bot@users.noreply.github.com>
Date: Tue, 6 Aug 2024 15:18:33 -0400
Subject: [PATCH 008/242] chore: Upgrade Python requirements (#35241)
---
requirements/edx-sandbox/base.txt | 12 +-
requirements/edx/base.txt | 134 ++++++--
requirements/edx/coverage.txt | 4 +-
requirements/edx/development.txt | 310 +++++++++++-------
requirements/edx/doc.txt | 186 +++++++++--
requirements/edx/semgrep.txt | 6 +-
requirements/edx/testing.txt | 250 ++++++++------
requirements/pip-tools.txt | 2 +-
requirements/pip.txt | 6 +-
.../requirements/testing.txt | 2 +-
scripts/user_retirement/requirements/base.txt | 18 +-
.../user_retirement/requirements/testing.txt | 20 +-
12 files changed, 640 insertions(+), 310 deletions(-)
diff --git a/requirements/edx-sandbox/base.txt b/requirements/edx-sandbox/base.txt
index 241deba480..8942bc7dd9 100644
--- a/requirements/edx-sandbox/base.txt
+++ b/requirements/edx-sandbox/base.txt
@@ -4,7 +4,7 @@
#
# make upgrade
#
-cffi==1.16.0
+cffi==1.17.0
# via cryptography
chem==1.3.0
# via -r requirements/edx-sandbox/base.in
@@ -16,7 +16,7 @@ codejail-includes==1.0.0
# via -r requirements/edx-sandbox/base.in
contourpy==1.2.1
# via matplotlib
-cryptography==42.0.8
+cryptography==43.0.0
# via -r requirements/edx-sandbox/base.in
cycler==0.12.1
# via matplotlib
@@ -35,7 +35,7 @@ markupsafe==2.1.5
# via
# chem
# openedx-calc
-matplotlib==3.9.1
+matplotlib==3.9.0
# via -r requirements/edx-sandbox/base.in
mpmath==1.3.0
# via sympy
@@ -71,7 +71,7 @@ python-dateutil==2.9.0.post0
# via matplotlib
random2==1.0.2
# via -r requirements/edx-sandbox/base.in
-regex==2024.5.15
+regex==2024.7.24
# via nltk
scipy==1.14.0
# via
@@ -82,9 +82,9 @@ six==1.16.0
# via
# codejail-includes
# python-dateutil
-sympy==1.13.0
+sympy==1.13.1
# via
# -r requirements/edx-sandbox/base.in
# openedx-calc
-tqdm==4.66.4
+tqdm==4.66.5
# via nltk
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index fcfe7548a0..c0c4c5b32c 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -8,7 +8,9 @@
# via -r requirements/edx/github.in
acid-xblock==0.3.1
# via -r requirements/edx/kernel.in
-aiohttp==3.9.5
+aiohappyeyeballs==2.3.4
+ # via aiohttp
+aiohttp==3.10.1
# via
# geoip2
# openai
@@ -33,7 +35,7 @@ asgiref==3.8.1
# django-countries
asn1crypto==1.5.1
# via snowflake-connector-python
-attrs==23.2.0
+attrs==24.2.0
# via
# -r requirements/edx/kernel.in
# aiohttp
@@ -50,7 +52,7 @@ babel==2.15.0
# enmerkar-underscore
backoff==1.10.0
# via analytics-python
-bcrypt==4.1.3
+bcrypt==4.2.0
# via paramiko
beautifulsoup4==4.12.3
# via pynliner
@@ -66,19 +68,23 @@ bleach[css]==6.1.0
# xblock-poll
boto==2.49.0
# via -r requirements/edx/kernel.in
-boto3==1.34.144
+boto3==1.34.154
# via
# -r requirements/edx/kernel.in
# django-ses
# fs-s3fs
# ora2
-botocore==1.34.144
+botocore==1.34.154
# via
# -r requirements/edx/kernel.in
# boto3
# s3transfer
bridgekeeper==0.9
# via -r requirements/edx/kernel.in
+cachecontrol==0.14.0
+ # via firebase-admin
+cachetools==5.4.0
+ # via google-auth
camel-converter[pydantic]==3.1.2
# via meilisearch
celery==5.4.0
@@ -98,7 +104,7 @@ certifi==2024.7.4
# py2neo
# requests
# snowflake-connector-python
-cffi==1.16.0
+cffi==1.17.0
# via
# cryptography
# pynacl
@@ -160,7 +166,7 @@ defusedxml==0.7.1
# ora2
# python3-openid
# social-auth-core
-django==4.2.14
+django==4.2.15
# via
# -c requirements/edx/../common_constraints.txt
# -c requirements/edx/../constraints.txt
@@ -179,6 +185,7 @@ django==4.2.14
# django-multi-email-field
# django-mysql
# django-oauth-toolkit
+ # django-push-notifications
# django-sekizai
# django-ses
# django-statici18n
@@ -262,7 +269,7 @@ django-crum==0.7.9
# super-csv
django-fernet-fields-v2==0.9
# via edx-enterprise
-django-filter==24.2
+django-filter==24.3
# via
# -r requirements/edx/kernel.in
# edx-enterprise
@@ -311,6 +318,8 @@ django-object-actions==4.2.0
# via edx-enterprise
django-pipeline==3.1.0
# via -r requirements/edx/kernel.in
+django-push-notifications==3.1.0
+ # via edx-ace
django-ratelimit==4.1.0
# via -r requirements/edx/kernel.in
django-sekizai==4.1.0
@@ -390,7 +399,7 @@ drf-yasg==1.21.7
# via
# django-user-tasks
# edx-api-doc-tools
-edx-ace==1.9.1
+edx-ace==1.11.0
# via -r requirements/edx/kernel.in
edx-api-doc-tools==1.8.0
# via
@@ -418,7 +427,7 @@ edx-celeryutils==1.3.0
# super-csv
edx-codejail==3.4.1
# via -r requirements/edx/kernel.in
-edx-completion==4.6.6
+edx-completion==4.6.7
# via -r requirements/edx/kernel.in
edx-django-release-util==1.4.0
# via
@@ -460,7 +469,7 @@ edx-enterprise==4.22.4
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
-edx-event-bus-kafka==5.7.0
+edx-event-bus-kafka==5.8.1
# via -r requirements/edx/kernel.in
edx-event-bus-redis==0.5.0
# via -r requirements/edx/kernel.in
@@ -502,11 +511,11 @@ edx-rest-api-client==5.7.1
# -r requirements/edx/kernel.in
# edx-enterprise
# edx-proctoring
-edx-search==3.9.1
+edx-search==4.0.0
# via -r requirements/edx/kernel.in
edx-sga==0.25.0
# via -r requirements/edx/bundled.in
-edx-submissions==3.7.5
+edx-submissions==3.7.6
# via
# -r requirements/edx/kernel.in
# ora2
@@ -551,6 +560,8 @@ fastavro==1.9.5
# via openedx-events
filelock==3.15.4
# via snowflake-connector-python
+firebase-admin==6.5.0
+ # via edx-ace
frozenlist==1.4.1
# via
# aiohttp
@@ -571,6 +582,49 @@ geoip2==4.8.0
# via -r requirements/edx/kernel.in
glob2==0.7
# via -r requirements/edx/kernel.in
+google-api-core[grpc]==2.19.1
+ # via
+ # firebase-admin
+ # google-api-python-client
+ # google-cloud-core
+ # google-cloud-firestore
+ # google-cloud-storage
+google-api-python-client==2.139.0
+ # via firebase-admin
+google-auth==2.32.0
+ # via
+ # google-api-core
+ # google-api-python-client
+ # google-auth-httplib2
+ # google-cloud-core
+ # google-cloud-firestore
+ # google-cloud-storage
+google-auth-httplib2==0.2.0
+ # via google-api-python-client
+google-cloud-core==2.4.1
+ # via
+ # google-cloud-firestore
+ # google-cloud-storage
+google-cloud-firestore==2.17.0
+ # via firebase-admin
+google-cloud-storage==2.18.0
+ # via firebase-admin
+google-crc32c==1.5.0
+ # via
+ # google-cloud-storage
+ # google-resumable-media
+google-resumable-media==2.7.1
+ # via google-cloud-storage
+googleapis-common-protos==1.63.2
+ # via
+ # google-api-core
+ # grpcio-status
+grpcio==1.65.4
+ # via
+ # google-api-core
+ # grpcio-status
+grpcio-status==1.62.3
+ # via google-api-core
gunicorn==22.0.0
# via -r requirements/edx/kernel.in
help-tokens==2.4.0
@@ -579,6 +633,10 @@ html5lib==1.1
# via
# -r requirements/edx/kernel.in
# ora2
+httplib2==0.22.0
+ # via
+ # google-api-python-client
+ # google-auth-httplib2
icalendar==5.0.13
# via -r requirements/edx/kernel.in
idna==3.7
@@ -610,7 +668,7 @@ jmespath==1.0.1
# botocore
joblib==1.4.2
# via nltk
-jsondiff==2.1.2
+jsondiff==2.2.0
# via edx-enterprise
jsonfield==3.1.0
# via
@@ -631,7 +689,7 @@ jwcrypto==1.5.6
# via
# django-oauth-toolkit
# pylti1p3
-kombu==5.3.7
+kombu==5.4.0
# via celery
laboratory==1.0.2
# via -r requirements/edx/kernel.in
@@ -703,6 +761,8 @@ more-itertools==10.3.0
# via cssutils
mpmath==1.3.0
# via sympy
+msgpack==1.0.8
+ # via cachecontrol
multidict==6.0.5
# via
# aiohttp
@@ -819,6 +879,17 @@ polib==1.2.0
# via edx-i18n-tools
prompt-toolkit==3.0.47
# via click-repl
+proto-plus==1.24.0
+ # via
+ # google-api-core
+ # google-cloud-firestore
+protobuf==4.25.4
+ # via
+ # google-api-core
+ # google-cloud-firestore
+ # googleapis-common-protos
+ # grpcio-status
+ # proto-plus
psutil==6.0.0
# via
# -r requirements/edx/paver.txt
@@ -828,7 +899,12 @@ py2neo @ https://github.com/overhangio/py2neo/releases/download/2021.2.3/py2neo-
# -c requirements/edx/../constraints.txt
# -r requirements/edx/bundled.in
pyasn1==0.6.0
- # via pgpy
+ # via
+ # pgpy
+ # pyasn1-modules
+ # rsa
+pyasn1-modules==0.4.0
+ # via google-auth
pycountry==24.6.1
# via -r requirements/edx/kernel.in
pycparser==2.22
@@ -852,7 +928,7 @@ pyjwkest==1.4.2
# -r requirements/edx/kernel.in
# edx-token-utils
# lti-consumer-xblock
-pyjwt[crypto]==2.8.0
+pyjwt[crypto]==2.9.0
# via
# -r requirements/edx/kernel.in
# drf-jwt
@@ -860,6 +936,7 @@ pyjwt[crypto]==2.8.0
# edx-drf-extensions
# edx-proctoring
# edx-rest-api-client
+ # firebase-admin
# pylti1p3
# snowflake-connector-python
# social-auth-core
@@ -884,13 +961,14 @@ pynacl==1.5.0
# paramiko
pynliner==0.8.0
# via -r requirements/edx/kernel.in
-pyopenssl==24.1.0
+pyopenssl==24.2.1
# via
# optimizely-sdk
# snowflake-connector-python
pyparsing==3.1.2
# via
# chem
+ # httplib2
# openedx-calc
pyrsistent==0.20.0
# via optimizely-sdk
@@ -960,7 +1038,7 @@ random2==1.0.2
# via -r requirements/edx/kernel.in
recommender-xblock==2.2.0
# via -r requirements/edx/bundled.in
-redis==5.0.7
+redis==5.0.8
# via
# -r requirements/edx/kernel.in
# walrus
@@ -968,19 +1046,22 @@ referencing==0.35.1
# via
# jsonschema
# jsonschema-specifications
-regex==2024.5.15
+regex==2024.7.24
# via nltk
requests==2.32.3
# via
# -r requirements/edx/paver.txt
# algoliasearch
# analytics-python
+ # cachecontrol
# django-oauth-toolkit
# edx-bulk-grades
# edx-drf-extensions
# edx-enterprise
# edx-rest-api-client
# geoip2
+ # google-api-core
+ # google-cloud-storage
# mailsnake
# meilisearch
# openai
@@ -998,10 +1079,12 @@ requests-oauthlib==2.0.0
# via
# -r requirements/edx/kernel.in
# social-auth-core
-rpds-py==0.19.0
+rpds-py==0.20.0
# via
# jsonschema
# referencing
+rsa==4.9
+ # via google-auth
rules==3.4
# via
# -r requirements/edx/kernel.in
@@ -1061,7 +1144,7 @@ slumber==0.7.1
# edx-bulk-grades
# edx-enterprise
# edx-rest-api-client
-snowflake-connector-python==3.11.0
+snowflake-connector-python==3.12.0
# via edx-enterprise
social-auth-app-django==5.4.1
# via
@@ -1098,7 +1181,7 @@ stevedore==5.2.0
# edx-opaque-keys
super-csv==3.2.0
# via edx-bulk-grades
-sympy==1.13.0
+sympy==1.13.1
# via openedx-calc
testfixtures==8.3.0
# via edx-enterprise
@@ -1108,7 +1191,7 @@ tinycss2==1.2.1
# via bleach
tomlkit==0.13.0
# via snowflake-connector-python
-tqdm==4.66.4
+tqdm==4.66.5
# via
# nltk
# openai
@@ -1132,6 +1215,7 @@ uritemplate==4.1.1
# via
# drf-spectacular
# drf-yasg
+ # google-api-python-client
urllib3==1.26.19
# via
# -c requirements/edx/../constraints.txt
@@ -1174,7 +1258,7 @@ webob==1.8.7
# xblock
wrapt==1.16.0
# via -r requirements/edx/paver.txt
-xblock[django]==4.0.1
+xblock[django]==5.0.0
# via
# -r requirements/edx/kernel.in
# acid-xblock
diff --git a/requirements/edx/coverage.txt b/requirements/edx/coverage.txt
index 8c3b834163..a004eeeb9f 100644
--- a/requirements/edx/coverage.txt
+++ b/requirements/edx/coverage.txt
@@ -6,9 +6,9 @@
#
chardet==5.2.0
# via diff-cover
-coverage==7.6.0
+coverage==7.6.1
# via -r requirements/edx/coverage.in
-diff-cover==9.1.0
+diff-cover==9.1.1
# via -r requirements/edx/coverage.in
jinja2==3.1.4
# via diff-cover
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index d6487d2ffa..cf8b8da20f 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -16,7 +16,12 @@ acid-xblock==0.3.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-aiohttp==3.9.5
+aiohappyeyeballs==2.3.4
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # aiohttp
+aiohttp==3.10.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -27,7 +32,7 @@ aiosignal==1.3.1
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# aiohttp
-alabaster==0.7.16
+alabaster==1.0.0
# via
# -r requirements/edx/doc.txt
# sphinx
@@ -57,9 +62,7 @@ annotated-types==0.7.0
anyio==4.4.0
# via
# -r requirements/edx/testing.txt
- # httpcore
# starlette
- # watchfiles
appdirs==1.4.4
# via
# -r requirements/edx/doc.txt
@@ -82,7 +85,7 @@ astroid==2.13.5
# -r requirements/edx/testing.txt
# pylint
# pylint-celery
-attrs==23.2.0
+attrs==24.2.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -106,7 +109,7 @@ backoff==1.10.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# analytics-python
-bcrypt==4.1.3
+bcrypt==4.2.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -136,14 +139,14 @@ boto==2.49.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-boto3==1.34.144
+boto3==1.34.154
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# django-ses
# fs-s3fs
# ora2
-botocore==1.34.144
+botocore==1.34.154
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -157,9 +160,16 @@ build==1.2.1
# via
# -r requirements/edx/../pip-tools.txt
# pip-tools
+cachecontrol==0.14.0
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # firebase-admin
cachetools==5.4.0
# via
+ # -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
+ # google-auth
# tox
camel-converter[pydantic]==3.1.2
# via
@@ -182,16 +192,15 @@ certifi==2024.7.4
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# elasticsearch
- # httpcore
- # httpx
# py2neo
# requests
# snowflake-connector-python
-cffi==1.16.0
+cffi==1.17.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# cryptography
+ # pact-python
# pynacl
# snowflake-connector-python
chardet==5.2.0
@@ -232,7 +241,6 @@ click==8.1.6
# nltk
# pact-python
# pip-tools
- # typer
# user-util
# uvicorn
click-didyoumean==0.3.1
@@ -269,7 +277,7 @@ colorama==0.4.6
# via
# -r requirements/edx/testing.txt
# tox
-coverage[toml]==7.6.0
+coverage[toml]==7.6.1
# via
# -r requirements/edx/testing.txt
# pytest-cov
@@ -314,7 +322,7 @@ defusedxml==0.7.1
# ora2
# python3-openid
# social-auth-core
-diff-cover==9.1.0
+diff-cover==9.1.1
# via -r requirements/edx/testing.txt
dill==0.3.8
# via
@@ -324,7 +332,7 @@ distlib==0.3.8
# via
# -r requirements/edx/testing.txt
# virtualenv
-django==4.2.14
+django==4.2.15
# via
# -c requirements/edx/../common_constraints.txt
# -c requirements/edx/../constraints.txt
@@ -345,6 +353,7 @@ django==4.2.14
# django-multi-email-field
# django-mysql
# django-oauth-toolkit
+ # django-push-notifications
# django-sekizai
# django-ses
# django-statici18n
@@ -451,7 +460,7 @@ django-fernet-fields-v2==0.9
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# edx-enterprise
-django-filter==24.2
+django-filter==24.3
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -520,6 +529,11 @@ django-pipeline==3.1.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
+django-push-notifications==3.1.0
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # edx-ace
django-ratelimit==4.1.0
# via
# -r requirements/edx/doc.txt
@@ -561,7 +575,7 @@ django-stubs==1.16.0
# -c requirements/edx/../constraints.txt
# -r requirements/edx/development.in
# djangorestframework-stubs
-django-stubs-ext==5.0.2
+django-stubs-ext==5.0.4
# via django-stubs
django-user-tasks==3.2.0
# via
@@ -616,7 +630,6 @@ dnspython==2.6.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
- # email-validator
# pymongo
docutils==0.21.2
# via
@@ -643,7 +656,7 @@ drf-yasg==1.21.7
# -r requirements/edx/testing.txt
# django-user-tasks
# edx-api-doc-tools
-edx-ace==1.9.1
+edx-ace==1.11.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -682,7 +695,7 @@ edx-codejail==3.4.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-edx-completion==4.6.6
+edx-completion==4.6.7
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -732,7 +745,7 @@ edx-enterprise==4.22.4
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-edx-event-bus-kafka==5.7.0
+edx-event-bus-kafka==5.8.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -792,7 +805,7 @@ edx-rest-api-client==5.7.1
# -r requirements/edx/testing.txt
# edx-enterprise
# edx-proctoring
-edx-search==3.9.1
+edx-search==4.0.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -800,7 +813,7 @@ edx-sga==0.25.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-edx-submissions==3.7.5
+edx-submissions==3.7.6
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -842,10 +855,6 @@ elasticsearch==7.13.4
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# edx-search
-email-validator==2.2.0
- # via
- # -r requirements/edx/testing.txt
- # fastapi
enmerkar==0.7.1
# via
# -r requirements/edx/doc.txt
@@ -869,18 +878,14 @@ execnet==2.1.1
# pytest-xdist
factory-boy==3.3.0
# via -r requirements/edx/testing.txt
-faker==26.0.0
+faker==26.2.0
# via
# -r requirements/edx/testing.txt
# factory-boy
-fastapi==0.111.1
+fastapi==0.112.0
# via
# -r requirements/edx/testing.txt
# pact-python
-fastapi-cli==0.0.4
- # via
- # -r requirements/edx/testing.txt
- # fastapi
fastavro==1.9.5
# via
# -r requirements/edx/doc.txt
@@ -893,6 +898,11 @@ filelock==3.15.4
# snowflake-connector-python
# tox
# virtualenv
+firebase-admin==6.5.0
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # edx-ace
freezegun==1.5.1
# via -r requirements/edx/testing.txt
frozenlist==1.4.1
@@ -932,10 +942,83 @@ glob2==0.7
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
+google-api-core[grpc]==2.19.1
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # firebase-admin
+ # google-api-python-client
+ # google-cloud-core
+ # google-cloud-firestore
+ # google-cloud-storage
+google-api-python-client==2.139.0
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # firebase-admin
+google-auth==2.32.0
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # google-api-core
+ # google-api-python-client
+ # google-auth-httplib2
+ # google-cloud-core
+ # google-cloud-firestore
+ # google-cloud-storage
+google-auth-httplib2==0.2.0
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # google-api-python-client
+google-cloud-core==2.4.1
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # google-cloud-firestore
+ # google-cloud-storage
+google-cloud-firestore==2.17.0
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # firebase-admin
+google-cloud-storage==2.18.0
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # firebase-admin
+google-crc32c==1.5.0
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # google-cloud-storage
+ # google-resumable-media
+google-resumable-media==2.7.1
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # google-cloud-storage
+googleapis-common-protos==1.63.2
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # google-api-core
+ # grpcio-status
grimp==3.4.1
# via
# -r requirements/edx/testing.txt
# import-linter
+grpcio==1.65.4
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # google-api-core
+ # grpcio-status
+grpcio-status==1.62.3
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # google-api-core
gunicorn==22.0.0
# via
# -r requirements/edx/doc.txt
@@ -943,7 +1026,6 @@ gunicorn==22.0.0
h11==0.14.0
# via
# -r requirements/edx/testing.txt
- # httpcore
# uvicorn
help-tokens==2.4.0
# via
@@ -954,21 +1036,14 @@ html5lib==1.1
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# ora2
-httpcore==0.16.3
+httplib2==0.22.0
# via
+ # -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
- # httpx
+ # google-api-python-client
+ # google-auth-httplib2
httpretty==1.1.4
# via -r requirements/edx/testing.txt
-httptools==0.6.1
- # via
- # -r requirements/edx/testing.txt
- # uvicorn
-httpx==0.23.3
- # via
- # -r requirements/edx/testing.txt
- # fastapi
- # pact-python
icalendar==5.0.13
# via
# -r requirements/edx/doc.txt
@@ -978,10 +1053,8 @@ idna==3.7
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# anyio
- # email-validator
# optimizely-sdk
# requests
- # rfc3986
# snowflake-connector-python
# yarl
imagesize==1.4.1
@@ -1029,7 +1102,6 @@ jinja2==3.1.4
# -r requirements/edx/testing.txt
# code-annotations
# diff-cover
- # fastapi
# sphinx
jmespath==1.0.1
# via
@@ -1042,7 +1114,7 @@ joblib==1.4.2
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# nltk
-jsondiff==2.1.2
+jsondiff==2.2.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1075,7 +1147,7 @@ jwcrypto==1.5.6
# -r requirements/edx/testing.txt
# django-oauth-toolkit
# pylti1p3
-kombu==5.3.7
+kombu==5.4.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1146,10 +1218,6 @@ markdown==3.3.7
# openedx-django-wiki
# staff-graded-xblock
# xblock-poll
-markdown-it-py==3.0.0
- # via
- # -r requirements/edx/testing.txt
- # rich
markupsafe==2.1.5
# via
# -r requirements/edx/doc.txt
@@ -1168,10 +1236,6 @@ mccabe==0.7.0
# via
# -r requirements/edx/testing.txt
# pylint
-mdurl==0.1.2
- # via
- # -r requirements/edx/testing.txt
- # markdown-it-py
meilisearch==0.31.4
# via
# -r requirements/edx/doc.txt
@@ -1204,13 +1268,18 @@ mpmath==1.3.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# sympy
+msgpack==1.0.8
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # cachecontrol
multidict==6.0.5
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# aiohttp
# yarl
-mypy==1.10.1
+mypy==1.11.1
# via
# -r requirements/edx/development.in
# django-stubs
@@ -1336,7 +1405,7 @@ packaging==24.1
# snowflake-connector-python
# sphinx
# tox
-pact-python==2.0.1
+pact-python==2.2.1
# via -r requirements/edx/testing.txt
pansi==2020.7.3
# via
@@ -1417,6 +1486,21 @@ prompt-toolkit==3.0.47
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# click-repl
+proto-plus==1.24.0
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # google-api-core
+ # google-cloud-firestore
+protobuf==4.25.4
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # google-api-core
+ # google-cloud-firestore
+ # googleapis-common-protos
+ # grpcio-status
+ # proto-plus
psutil==6.0.0
# via
# -r requirements/edx/doc.txt
@@ -1436,6 +1520,13 @@ pyasn1==0.6.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# pgpy
+ # pyasn1-modules
+ # rsa
+pyasn1-modules==0.4.0
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # google-auth
pycodestyle==2.8.0
# via
# -c requirements/edx/../constraints.txt
@@ -1479,7 +1570,6 @@ pygments==2.18.0
# diff-cover
# py2neo
# pydata-sphinx-theme
- # rich
# sphinx
# sphinx-mdinclude
pyjwkest==1.4.2
@@ -1488,7 +1578,7 @@ pyjwkest==1.4.2
# -r requirements/edx/testing.txt
# edx-token-utils
# lti-consumer-xblock
-pyjwt[crypto]==2.8.0
+pyjwt[crypto]==2.9.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1497,6 +1587,7 @@ pyjwt[crypto]==2.8.0
# edx-drf-extensions
# edx-proctoring
# edx-rest-api-client
+ # firebase-admin
# pylti1p3
# snowflake-connector-python
# social-auth-core
@@ -1556,7 +1647,7 @@ pynliner==0.8.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-pyopenssl==24.1.0
+pyopenssl==24.2.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1567,6 +1658,7 @@ pyparsing==3.1.2
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# chem
+ # httplib2
# openedx-calc
pyproject-api==1.7.1
# via
@@ -1589,7 +1681,7 @@ pysrt==1.1.2
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# edxval
-pytest==8.2.2
+pytest==8.3.2
# via
# -r requirements/edx/testing.txt
# pylint-pytest
@@ -1632,10 +1724,6 @@ python-dateutil==2.9.0.post0
# olxcleaner
# ora2
# xblock
-python-dotenv==1.0.1
- # via
- # -r requirements/edx/testing.txt
- # uvicorn
python-ipware==3.0.0
# via
# -r requirements/edx/doc.txt
@@ -1645,10 +1733,6 @@ python-memcached==1.62
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-python-multipart==0.0.9
- # via
- # -r requirements/edx/testing.txt
- # fastapi
python-slugify==8.0.4
# via
# -r requirements/edx/doc.txt
@@ -1705,7 +1789,6 @@ pyyaml==6.0.1
# edx-i18n-tools
# jsondiff
# sphinxcontrib-openapi
- # uvicorn
# xblock
random2==1.0.2
# via
@@ -1715,7 +1798,7 @@ recommender-xblock==2.2.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-redis==5.0.7
+redis==5.0.8
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1726,7 +1809,7 @@ referencing==0.35.1
# -r requirements/edx/testing.txt
# jsonschema
# jsonschema-specifications
-regex==2024.5.15
+regex==2024.7.24
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1737,6 +1820,7 @@ requests==2.32.3
# -r requirements/edx/testing.txt
# algoliasearch
# analytics-python
+ # cachecontrol
# django-oauth-toolkit
# djangorestframework-stubs
# edx-bulk-grades
@@ -1744,6 +1828,8 @@ requests==2.32.3
# edx-enterprise
# edx-rest-api-client
# geoip2
+ # google-api-core
+ # google-cloud-storage
# mailsnake
# meilisearch
# openai
@@ -1764,20 +1850,17 @@ requests-oauthlib==2.0.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# social-auth-core
-rfc3986[idna2008]==1.5.0
- # via
- # -r requirements/edx/testing.txt
- # httpx
-rich==13.7.1
- # via
- # -r requirements/edx/testing.txt
- # typer
-rpds-py==0.19.0
+rpds-py==0.20.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# jsonschema
# referencing
+rsa==4.9
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # google-auth
rules==3.4
# via
# -r requirements/edx/doc.txt
@@ -1810,10 +1893,6 @@ shapely==2.0.5
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-shellingham==1.5.4
- # via
- # -r requirements/edx/testing.txt
- # typer
simplejson==3.19.2
# via
# -r requirements/edx/doc.txt
@@ -1871,13 +1950,11 @@ sniffio==1.3.1
# via
# -r requirements/edx/testing.txt
# anyio
- # httpcore
- # httpx
snowballstemmer==2.2.0
# via
# -r requirements/edx/doc.txt
# sphinx
-snowflake-connector-python==3.11.0
+snowflake-connector-python==3.12.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1909,7 +1986,7 @@ soupsieve==2.5
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# beautifulsoup4
-sphinx==7.4.4
+sphinx==8.0.2
# via
# -r requirements/edx/doc.txt
# pydata-sphinx-theme
@@ -1922,23 +1999,23 @@ sphinx==7.4.4
# sphinxext-rediraffe
sphinx-book-theme==1.1.3
# via -r requirements/edx/doc.txt
-sphinx-design==0.6.0
+sphinx-design==0.6.1
# via -r requirements/edx/doc.txt
-sphinx-mdinclude==0.6.1
+sphinx-mdinclude==0.6.2
# via
# -r requirements/edx/doc.txt
# sphinxcontrib-openapi
sphinx-reredirects==0.1.5
# via -r requirements/edx/doc.txt
-sphinxcontrib-applehelp==1.0.8
+sphinxcontrib-applehelp==2.0.0
# via
# -r requirements/edx/doc.txt
# sphinx
-sphinxcontrib-devhelp==1.0.6
+sphinxcontrib-devhelp==2.0.0
# via
# -r requirements/edx/doc.txt
# sphinx
-sphinxcontrib-htmlhelp==2.0.5
+sphinxcontrib-htmlhelp==2.1.0
# via
# -r requirements/edx/doc.txt
# sphinx
@@ -1952,11 +2029,11 @@ sphinxcontrib-jsmath==1.0.1
# sphinx
sphinxcontrib-openapi[markdown]==0.8.4
# via -r requirements/edx/doc.txt
-sphinxcontrib-qthelp==1.0.7
+sphinxcontrib-qthelp==2.0.0
# via
# -r requirements/edx/doc.txt
# sphinx
-sphinxcontrib-serializinghtml==1.1.10
+sphinxcontrib-serializinghtml==2.0.0
# via
# -r requirements/edx/doc.txt
# sphinx
@@ -1990,7 +2067,7 @@ super-csv==3.2.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# edx-bulk-grades
-sympy==1.13.0
+sympy==1.13.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -2018,21 +2095,17 @@ tomlkit==0.13.0
# -r requirements/edx/testing.txt
# pylint
# snowflake-connector-python
-tox==4.16.0
+tox==4.17.0
# via -r requirements/edx/testing.txt
-tqdm==4.66.4
+tqdm==4.66.5
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# nltk
# openai
-typer==0.12.3
- # via
- # -r requirements/edx/testing.txt
- # fastapi-cli
types-pytz==2024.1.0.20240417
# via django-stubs
-types-pyyaml==6.0.12.20240311
+types-pyyaml==6.0.12.20240724
# via
# django-stubs
# djangorestframework-stubs
@@ -2059,7 +2132,6 @@ typing-extensions==4.12.2
# pydata-sphinx-theme
# pylti1p3
# snowflake-connector-python
- # typer
tzdata==2024.1
# via
# -r requirements/edx/doc.txt
@@ -2078,6 +2150,7 @@ uritemplate==4.1.1
# -r requirements/edx/testing.txt
# drf-spectacular
# drf-yasg
+ # google-api-python-client
urllib3==1.26.19
# via
# -c requirements/edx/../constraints.txt
@@ -2085,22 +2158,16 @@ urllib3==1.26.19
# -r requirements/edx/testing.txt
# botocore
# elasticsearch
- # pact-python
# py2neo
# requests
user-util==1.1.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-uvicorn[standard]==0.30.1
+uvicorn==0.30.5
# via
# -r requirements/edx/testing.txt
- # fastapi
# pact-python
-uvloop==0.19.0
- # via
- # -r requirements/edx/testing.txt
- # uvicorn
vine==5.1.0
# via
# -r requirements/edx/doc.txt
@@ -2129,10 +2196,6 @@ watchdog==4.0.1
# -r requirements/edx/development.in
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-watchfiles==0.22.0
- # via
- # -r requirements/edx/testing.txt
- # uvicorn
wcwidth==0.2.13
# via
# -r requirements/edx/doc.txt
@@ -2159,11 +2222,7 @@ webob==1.8.7
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# xblock
-websockets==12.0
- # via
- # -r requirements/edx/testing.txt
- # uvicorn
-wheel==0.43.0
+wheel==0.44.0
# via
# -r requirements/edx/../pip-tools.txt
# pip-tools
@@ -2172,7 +2231,7 @@ wrapt==1.16.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# astroid
-xblock[django]==4.0.1
+xblock[django]==5.0.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -2221,6 +2280,7 @@ yarl==1.9.4
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# aiohttp
+ # pact-python
zipp==3.19.2
# via
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index c90be69760..33cf7edaec 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -10,7 +10,11 @@ accessible-pygments==0.0.5
# via pydata-sphinx-theme
acid-xblock==0.3.1
# via -r requirements/edx/base.txt
-aiohttp==3.9.5
+aiohappyeyeballs==2.3.4
+ # via
+ # -r requirements/edx/base.txt
+ # aiohttp
+aiohttp==3.10.1
# via
# -r requirements/edx/base.txt
# geoip2
@@ -19,7 +23,7 @@ aiosignal==1.3.1
# via
# -r requirements/edx/base.txt
# aiohttp
-alabaster==0.7.16
+alabaster==1.0.0
# via sphinx
algoliasearch==3.0.0
# via -r requirements/edx/base.txt
@@ -51,7 +55,7 @@ asn1crypto==1.5.1
# via
# -r requirements/edx/base.txt
# snowflake-connector-python
-attrs==23.2.0
+attrs==24.2.0
# via
# -r requirements/edx/base.txt
# aiohttp
@@ -72,7 +76,7 @@ backoff==1.10.0
# via
# -r requirements/edx/base.txt
# analytics-python
-bcrypt==4.1.3
+bcrypt==4.2.0
# via
# -r requirements/edx/base.txt
# paramiko
@@ -96,19 +100,27 @@ bleach[css]==6.1.0
# xblock-poll
boto==2.49.0
# via -r requirements/edx/base.txt
-boto3==1.34.144
+boto3==1.34.154
# via
# -r requirements/edx/base.txt
# django-ses
# fs-s3fs
# ora2
-botocore==1.34.144
+botocore==1.34.154
# via
# -r requirements/edx/base.txt
# boto3
# s3transfer
bridgekeeper==0.9
# via -r requirements/edx/base.txt
+cachecontrol==0.14.0
+ # via
+ # -r requirements/edx/base.txt
+ # firebase-admin
+cachetools==5.4.0
+ # via
+ # -r requirements/edx/base.txt
+ # google-auth
camel-converter[pydantic]==3.1.2
# via
# -r requirements/edx/base.txt
@@ -130,7 +142,7 @@ certifi==2024.7.4
# py2neo
# requests
# snowflake-connector-python
-cffi==1.16.0
+cffi==1.17.0
# via
# -r requirements/edx/base.txt
# cryptography
@@ -208,7 +220,7 @@ defusedxml==0.7.1
# ora2
# python3-openid
# social-auth-core
-django==4.2.14
+django==4.2.15
# via
# -c requirements/edx/../common_constraints.txt
# -c requirements/edx/../constraints.txt
@@ -227,6 +239,7 @@ django==4.2.14
# django-multi-email-field
# django-mysql
# django-oauth-toolkit
+ # django-push-notifications
# django-sekizai
# django-ses
# django-statici18n
@@ -318,7 +331,7 @@ django-fernet-fields-v2==0.9
# via
# -r requirements/edx/base.txt
# edx-enterprise
-django-filter==24.2
+django-filter==24.3
# via
# -r requirements/edx/base.txt
# edx-enterprise
@@ -373,6 +386,10 @@ django-object-actions==4.2.0
# edx-enterprise
django-pipeline==3.1.0
# via -r requirements/edx/base.txt
+django-push-notifications==3.1.0
+ # via
+ # -r requirements/edx/base.txt
+ # edx-ace
django-ratelimit==4.1.0
# via -r requirements/edx/base.txt
django-sekizai==4.1.0
@@ -462,7 +479,7 @@ drf-yasg==1.21.7
# -r requirements/edx/base.txt
# django-user-tasks
# edx-api-doc-tools
-edx-ace==1.9.1
+edx-ace==1.11.0
# via -r requirements/edx/base.txt
edx-api-doc-tools==1.8.0
# via
@@ -490,7 +507,7 @@ edx-celeryutils==1.3.0
# super-csv
edx-codejail==3.4.1
# via -r requirements/edx/base.txt
-edx-completion==4.6.6
+edx-completion==4.6.7
# via -r requirements/edx/base.txt
edx-django-release-util==1.4.0
# via
@@ -532,7 +549,7 @@ edx-enterprise==4.22.4
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
-edx-event-bus-kafka==5.7.0
+edx-event-bus-kafka==5.8.1
# via -r requirements/edx/base.txt
edx-event-bus-redis==0.5.0
# via -r requirements/edx/base.txt
@@ -575,11 +592,11 @@ edx-rest-api-client==5.7.1
# -r requirements/edx/base.txt
# edx-enterprise
# edx-proctoring
-edx-search==3.9.1
+edx-search==4.0.0
# via -r requirements/edx/base.txt
edx-sga==0.25.0
# via -r requirements/edx/base.txt
-edx-submissions==3.7.5
+edx-submissions==3.7.6
# via
# -r requirements/edx/base.txt
# ora2
@@ -633,6 +650,10 @@ filelock==3.15.4
# via
# -r requirements/edx/base.txt
# snowflake-connector-python
+firebase-admin==6.5.0
+ # via
+ # -r requirements/edx/base.txt
+ # edx-ace
frozenlist==1.4.1
# via
# -r requirements/edx/base.txt
@@ -660,6 +681,67 @@ gitpython==3.1.43
# via -r requirements/edx/doc.in
glob2==0.7
# via -r requirements/edx/base.txt
+google-api-core[grpc]==2.19.1
+ # via
+ # -r requirements/edx/base.txt
+ # firebase-admin
+ # google-api-python-client
+ # google-cloud-core
+ # google-cloud-firestore
+ # google-cloud-storage
+google-api-python-client==2.139.0
+ # via
+ # -r requirements/edx/base.txt
+ # firebase-admin
+google-auth==2.32.0
+ # via
+ # -r requirements/edx/base.txt
+ # google-api-core
+ # google-api-python-client
+ # google-auth-httplib2
+ # google-cloud-core
+ # google-cloud-firestore
+ # google-cloud-storage
+google-auth-httplib2==0.2.0
+ # via
+ # -r requirements/edx/base.txt
+ # google-api-python-client
+google-cloud-core==2.4.1
+ # via
+ # -r requirements/edx/base.txt
+ # google-cloud-firestore
+ # google-cloud-storage
+google-cloud-firestore==2.17.0
+ # via
+ # -r requirements/edx/base.txt
+ # firebase-admin
+google-cloud-storage==2.18.0
+ # via
+ # -r requirements/edx/base.txt
+ # firebase-admin
+google-crc32c==1.5.0
+ # via
+ # -r requirements/edx/base.txt
+ # google-cloud-storage
+ # google-resumable-media
+google-resumable-media==2.7.1
+ # via
+ # -r requirements/edx/base.txt
+ # google-cloud-storage
+googleapis-common-protos==1.63.2
+ # via
+ # -r requirements/edx/base.txt
+ # google-api-core
+ # grpcio-status
+grpcio==1.65.4
+ # via
+ # -r requirements/edx/base.txt
+ # google-api-core
+ # grpcio-status
+grpcio-status==1.62.3
+ # via
+ # -r requirements/edx/base.txt
+ # google-api-core
gunicorn==22.0.0
# via -r requirements/edx/base.txt
help-tokens==2.4.0
@@ -668,6 +750,11 @@ html5lib==1.1
# via
# -r requirements/edx/base.txt
# ora2
+httplib2==0.22.0
+ # via
+ # -r requirements/edx/base.txt
+ # google-api-python-client
+ # google-auth-httplib2
icalendar==5.0.13
# via -r requirements/edx/base.txt
idna==3.7
@@ -712,7 +799,7 @@ joblib==1.4.2
# via
# -r requirements/edx/base.txt
# nltk
-jsondiff==2.1.2
+jsondiff==2.2.0
# via
# -r requirements/edx/base.txt
# edx-enterprise
@@ -740,7 +827,7 @@ jwcrypto==1.5.6
# -r requirements/edx/base.txt
# django-oauth-toolkit
# pylti1p3
-kombu==5.3.7
+kombu==5.4.0
# via
# -r requirements/edx/base.txt
# celery
@@ -825,6 +912,10 @@ mpmath==1.3.0
# via
# -r requirements/edx/base.txt
# sympy
+msgpack==1.0.8
+ # via
+ # -r requirements/edx/base.txt
+ # cachecontrol
multidict==6.0.5
# via
# -r requirements/edx/base.txt
@@ -964,6 +1055,19 @@ prompt-toolkit==3.0.47
# via
# -r requirements/edx/base.txt
# click-repl
+proto-plus==1.24.0
+ # via
+ # -r requirements/edx/base.txt
+ # google-api-core
+ # google-cloud-firestore
+protobuf==4.25.4
+ # via
+ # -r requirements/edx/base.txt
+ # google-api-core
+ # google-cloud-firestore
+ # googleapis-common-protos
+ # grpcio-status
+ # proto-plus
psutil==6.0.0
# via
# -r requirements/edx/base.txt
@@ -976,6 +1080,12 @@ pyasn1==0.6.0
# via
# -r requirements/edx/base.txt
# pgpy
+ # pyasn1-modules
+ # rsa
+pyasn1-modules==0.4.0
+ # via
+ # -r requirements/edx/base.txt
+ # google-auth
pycountry==24.6.1
# via -r requirements/edx/base.txt
pycparser==2.22
@@ -1011,7 +1121,7 @@ pyjwkest==1.4.2
# -r requirements/edx/base.txt
# edx-token-utils
# lti-consumer-xblock
-pyjwt[crypto]==2.8.0
+pyjwt[crypto]==2.9.0
# via
# -r requirements/edx/base.txt
# drf-jwt
@@ -1019,6 +1129,7 @@ pyjwt[crypto]==2.8.0
# edx-drf-extensions
# edx-proctoring
# edx-rest-api-client
+ # firebase-admin
# pylti1p3
# snowflake-connector-python
# social-auth-core
@@ -1045,7 +1156,7 @@ pynacl==1.5.0
# paramiko
pynliner==0.8.0
# via -r requirements/edx/base.txt
-pyopenssl==24.1.0
+pyopenssl==24.2.1
# via
# -r requirements/edx/base.txt
# optimizely-sdk
@@ -1054,6 +1165,7 @@ pyparsing==3.1.2
# via
# -r requirements/edx/base.txt
# chem
+ # httplib2
# openedx-calc
pyrsistent==0.20.0
# via
@@ -1132,7 +1244,7 @@ random2==1.0.2
# via -r requirements/edx/base.txt
recommender-xblock==2.2.0
# via -r requirements/edx/base.txt
-redis==5.0.7
+redis==5.0.8
# via
# -r requirements/edx/base.txt
# walrus
@@ -1141,7 +1253,7 @@ referencing==0.35.1
# -r requirements/edx/base.txt
# jsonschema
# jsonschema-specifications
-regex==2024.5.15
+regex==2024.7.24
# via
# -r requirements/edx/base.txt
# nltk
@@ -1150,12 +1262,15 @@ requests==2.32.3
# -r requirements/edx/base.txt
# algoliasearch
# analytics-python
+ # cachecontrol
# django-oauth-toolkit
# edx-bulk-grades
# edx-drf-extensions
# edx-enterprise
# edx-rest-api-client
# geoip2
+ # google-api-core
+ # google-cloud-storage
# mailsnake
# meilisearch
# openai
@@ -1174,11 +1289,15 @@ requests-oauthlib==2.0.0
# via
# -r requirements/edx/base.txt
# social-auth-core
-rpds-py==0.19.0
+rpds-py==0.20.0
# via
# -r requirements/edx/base.txt
# jsonschema
# referencing
+rsa==4.9
+ # via
+ # -r requirements/edx/base.txt
+ # google-auth
rules==3.4
# via
# -r requirements/edx/base.txt
@@ -1249,7 +1368,7 @@ smmap==5.0.1
# via gitdb
snowballstemmer==2.2.0
# via sphinx
-snowflake-connector-python==3.11.0
+snowflake-connector-python==3.12.0
# via
# -r requirements/edx/base.txt
# edx-enterprise
@@ -1275,7 +1394,7 @@ soupsieve==2.5
# via
# -r requirements/edx/base.txt
# beautifulsoup4
-sphinx==7.4.4
+sphinx==8.0.2
# via
# -r requirements/edx/doc.in
# pydata-sphinx-theme
@@ -1288,17 +1407,17 @@ sphinx==7.4.4
# sphinxext-rediraffe
sphinx-book-theme==1.1.3
# via -r requirements/edx/doc.in
-sphinx-design==0.6.0
+sphinx-design==0.6.1
# via -r requirements/edx/doc.in
-sphinx-mdinclude==0.6.1
+sphinx-mdinclude==0.6.2
# via sphinxcontrib-openapi
sphinx-reredirects==0.1.5
# via -r requirements/edx/doc.in
-sphinxcontrib-applehelp==1.0.8
+sphinxcontrib-applehelp==2.0.0
# via sphinx
-sphinxcontrib-devhelp==1.0.6
+sphinxcontrib-devhelp==2.0.0
# via sphinx
-sphinxcontrib-htmlhelp==2.0.5
+sphinxcontrib-htmlhelp==2.1.0
# via sphinx
sphinxcontrib-httpdomain==1.8.1
# via sphinxcontrib-openapi
@@ -1306,9 +1425,9 @@ sphinxcontrib-jsmath==1.0.1
# via sphinx
sphinxcontrib-openapi[markdown]==0.8.4
# via -r requirements/edx/doc.in
-sphinxcontrib-qthelp==1.0.7
+sphinxcontrib-qthelp==2.0.0
# via sphinx
-sphinxcontrib-serializinghtml==1.1.10
+sphinxcontrib-serializinghtml==2.0.0
# via sphinx
sphinxext-rediraffe==0.2.7
# via -r requirements/edx/doc.in
@@ -1330,7 +1449,7 @@ super-csv==3.2.0
# via
# -r requirements/edx/base.txt
# edx-bulk-grades
-sympy==1.13.0
+sympy==1.13.1
# via
# -r requirements/edx/base.txt
# openedx-calc
@@ -1350,7 +1469,7 @@ tomlkit==0.13.0
# via
# -r requirements/edx/base.txt
# snowflake-connector-python
-tqdm==4.66.4
+tqdm==4.66.5
# via
# -r requirements/edx/base.txt
# nltk
@@ -1379,6 +1498,7 @@ uritemplate==4.1.1
# -r requirements/edx/base.txt
# drf-spectacular
# drf-yasg
+ # google-api-python-client
urllib3==1.26.19
# via
# -c requirements/edx/../constraints.txt
@@ -1429,7 +1549,7 @@ webob==1.8.7
# xblock
wrapt==1.16.0
# via -r requirements/edx/base.txt
-xblock[django]==4.0.1
+xblock[django]==5.0.0
# via
# -r requirements/edx/base.txt
# acid-xblock
diff --git a/requirements/edx/semgrep.txt b/requirements/edx/semgrep.txt
index 5c3ec421aa..292f131904 100644
--- a/requirements/edx/semgrep.txt
+++ b/requirements/edx/semgrep.txt
@@ -4,7 +4,7 @@
#
# make upgrade
#
-attrs==23.2.0
+attrs==24.2.0
# via
# glom
# jsonschema
@@ -15,7 +15,7 @@ boltons==21.0.0
# face
# glom
# semgrep
-bracex==2.4
+bracex==2.5
# via wcmatch
certifi==2024.7.4
# via requests
@@ -62,7 +62,7 @@ requests==2.32.3
# via semgrep
rich==13.7.1
# via semgrep
-rpds-py==0.19.0
+rpds-py==0.20.0
# via
# jsonschema
# referencing
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index a679ac373e..5cca40a4ea 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -8,7 +8,11 @@
# via -r requirements/edx/base.txt
acid-xblock==0.3.1
# via -r requirements/edx/base.txt
-aiohttp==3.9.5
+aiohappyeyeballs==2.3.4
+ # via
+ # -r requirements/edx/base.txt
+ # aiohttp
+aiohttp==3.10.1
# via
# -r requirements/edx/base.txt
# geoip2
@@ -34,10 +38,7 @@ annotated-types==0.7.0
# -r requirements/edx/base.txt
# pydantic
anyio==4.4.0
- # via
- # httpcore
- # starlette
- # watchfiles
+ # via starlette
appdirs==1.4.4
# via
# -r requirements/edx/base.txt
@@ -56,7 +57,7 @@ astroid==2.13.5
# via
# pylint
# pylint-celery
-attrs==23.2.0
+attrs==24.2.0
# via
# -r requirements/edx/base.txt
# aiohttp
@@ -75,7 +76,7 @@ backoff==1.10.0
# via
# -r requirements/edx/base.txt
# analytics-python
-bcrypt==4.1.3
+bcrypt==4.2.0
# via
# -r requirements/edx/base.txt
# paramiko
@@ -99,21 +100,28 @@ bleach[css]==6.1.0
# xblock-poll
boto==2.49.0
# via -r requirements/edx/base.txt
-boto3==1.34.144
+boto3==1.34.154
# via
# -r requirements/edx/base.txt
# django-ses
# fs-s3fs
# ora2
-botocore==1.34.144
+botocore==1.34.154
# via
# -r requirements/edx/base.txt
# boto3
# s3transfer
bridgekeeper==0.9
# via -r requirements/edx/base.txt
+cachecontrol==0.14.0
+ # via
+ # -r requirements/edx/base.txt
+ # firebase-admin
cachetools==5.4.0
- # via tox
+ # via
+ # -r requirements/edx/base.txt
+ # google-auth
+ # tox
camel-converter[pydantic]==3.1.2
# via
# -r requirements/edx/base.txt
@@ -132,15 +140,14 @@ certifi==2024.7.4
# via
# -r requirements/edx/base.txt
# elasticsearch
- # httpcore
- # httpx
# py2neo
# requests
# snowflake-connector-python
-cffi==1.16.0
+cffi==1.17.0
# via
# -r requirements/edx/base.txt
# cryptography
+ # pact-python
# pynacl
# snowflake-connector-python
chardet==5.2.0
@@ -173,7 +180,6 @@ click==8.1.6
# import-linter
# nltk
# pact-python
- # typer
# user-util
# uvicorn
click-didyoumean==0.3.1
@@ -201,7 +207,7 @@ codejail-includes==1.0.0
# via -r requirements/edx/base.txt
colorama==0.4.6
# via tox
-coverage[toml]==7.6.0
+coverage[toml]==7.6.1
# via
# -r requirements/edx/coverage.txt
# pytest-cov
@@ -237,13 +243,13 @@ defusedxml==0.7.1
# ora2
# python3-openid
# social-auth-core
-diff-cover==9.1.0
+diff-cover==9.1.1
# via -r requirements/edx/coverage.txt
dill==0.3.8
# via pylint
distlib==0.3.8
# via virtualenv
-django==4.2.14
+django==4.2.15
# via
# -c requirements/edx/../common_constraints.txt
# -c requirements/edx/../constraints.txt
@@ -262,6 +268,7 @@ django==4.2.14
# django-multi-email-field
# django-mysql
# django-oauth-toolkit
+ # django-push-notifications
# django-sekizai
# django-ses
# django-statici18n
@@ -353,7 +360,7 @@ django-fernet-fields-v2==0.9
# via
# -r requirements/edx/base.txt
# edx-enterprise
-django-filter==24.2
+django-filter==24.3
# via
# -r requirements/edx/base.txt
# edx-enterprise
@@ -408,6 +415,10 @@ django-object-actions==4.2.0
# edx-enterprise
django-pipeline==3.1.0
# via -r requirements/edx/base.txt
+django-push-notifications==3.1.0
+ # via
+ # -r requirements/edx/base.txt
+ # edx-ace
django-ratelimit==4.1.0
# via -r requirements/edx/base.txt
django-sekizai==4.1.0
@@ -478,7 +489,6 @@ djangorestframework-xml==2.0.0
dnspython==2.6.1
# via
# -r requirements/edx/base.txt
- # email-validator
# pymongo
done-xblock==2.3.0
# via -r requirements/edx/base.txt
@@ -493,7 +503,7 @@ drf-yasg==1.21.7
# -r requirements/edx/base.txt
# django-user-tasks
# edx-api-doc-tools
-edx-ace==1.9.1
+edx-ace==1.11.0
# via -r requirements/edx/base.txt
edx-api-doc-tools==1.8.0
# via
@@ -521,7 +531,7 @@ edx-celeryutils==1.3.0
# super-csv
edx-codejail==3.4.1
# via -r requirements/edx/base.txt
-edx-completion==4.6.6
+edx-completion==4.6.7
# via -r requirements/edx/base.txt
edx-django-release-util==1.4.0
# via
@@ -563,7 +573,7 @@ edx-enterprise==4.22.4
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
-edx-event-bus-kafka==5.7.0
+edx-event-bus-kafka==5.8.1
# via -r requirements/edx/base.txt
edx-event-bus-redis==0.5.0
# via -r requirements/edx/base.txt
@@ -608,11 +618,11 @@ edx-rest-api-client==5.7.1
# -r requirements/edx/base.txt
# edx-enterprise
# edx-proctoring
-edx-search==3.9.1
+edx-search==4.0.0
# via -r requirements/edx/base.txt
edx-sga==0.25.0
# via -r requirements/edx/base.txt
-edx-submissions==3.7.5
+edx-submissions==3.7.6
# via
# -r requirements/edx/base.txt
# ora2
@@ -645,8 +655,6 @@ elasticsearch==7.13.4
# -c requirements/edx/../common_constraints.txt
# -r requirements/edx/base.txt
# edx-search
-email-validator==2.2.0
- # via fastapi
enmerkar==0.7.1
# via
# -r requirements/edx/base.txt
@@ -664,12 +672,10 @@ execnet==2.1.1
# via pytest-xdist
factory-boy==3.3.0
# via -r requirements/edx/testing.in
-faker==26.0.0
+faker==26.2.0
# via factory-boy
-fastapi==0.111.1
+fastapi==0.112.0
# via pact-python
-fastapi-cli==0.0.4
- # via fastapi
fastavro==1.9.5
# via
# -r requirements/edx/base.txt
@@ -680,6 +686,10 @@ filelock==3.15.4
# snowflake-connector-python
# tox
# virtualenv
+firebase-admin==6.5.0
+ # via
+ # -r requirements/edx/base.txt
+ # edx-ace
freezegun==1.5.1
# via -r requirements/edx/testing.in
frozenlist==1.4.1
@@ -705,40 +715,94 @@ geoip2==4.8.0
# via -r requirements/edx/base.txt
glob2==0.7
# via -r requirements/edx/base.txt
+google-api-core[grpc]==2.19.1
+ # via
+ # -r requirements/edx/base.txt
+ # firebase-admin
+ # google-api-python-client
+ # google-cloud-core
+ # google-cloud-firestore
+ # google-cloud-storage
+google-api-python-client==2.139.0
+ # via
+ # -r requirements/edx/base.txt
+ # firebase-admin
+google-auth==2.32.0
+ # via
+ # -r requirements/edx/base.txt
+ # google-api-core
+ # google-api-python-client
+ # google-auth-httplib2
+ # google-cloud-core
+ # google-cloud-firestore
+ # google-cloud-storage
+google-auth-httplib2==0.2.0
+ # via
+ # -r requirements/edx/base.txt
+ # google-api-python-client
+google-cloud-core==2.4.1
+ # via
+ # -r requirements/edx/base.txt
+ # google-cloud-firestore
+ # google-cloud-storage
+google-cloud-firestore==2.17.0
+ # via
+ # -r requirements/edx/base.txt
+ # firebase-admin
+google-cloud-storage==2.18.0
+ # via
+ # -r requirements/edx/base.txt
+ # firebase-admin
+google-crc32c==1.5.0
+ # via
+ # -r requirements/edx/base.txt
+ # google-cloud-storage
+ # google-resumable-media
+google-resumable-media==2.7.1
+ # via
+ # -r requirements/edx/base.txt
+ # google-cloud-storage
+googleapis-common-protos==1.63.2
+ # via
+ # -r requirements/edx/base.txt
+ # google-api-core
+ # grpcio-status
grimp==3.4.1
# via import-linter
+grpcio==1.65.4
+ # via
+ # -r requirements/edx/base.txt
+ # google-api-core
+ # grpcio-status
+grpcio-status==1.62.3
+ # via
+ # -r requirements/edx/base.txt
+ # google-api-core
gunicorn==22.0.0
# via -r requirements/edx/base.txt
h11==0.14.0
- # via
- # httpcore
- # uvicorn
+ # via uvicorn
help-tokens==2.4.0
# via -r requirements/edx/base.txt
html5lib==1.1
# via
# -r requirements/edx/base.txt
# ora2
-httpcore==0.16.3
- # via httpx
+httplib2==0.22.0
+ # via
+ # -r requirements/edx/base.txt
+ # google-api-python-client
+ # google-auth-httplib2
httpretty==1.1.4
# via -r requirements/edx/testing.in
-httptools==0.6.1
- # via uvicorn
-httpx==0.23.3
- # via
- # fastapi
- # pact-python
icalendar==5.0.13
# via -r requirements/edx/base.txt
idna==3.7
# via
# -r requirements/edx/base.txt
# anyio
- # email-validator
# optimizely-sdk
# requests
- # rfc3986
# snowflake-connector-python
# yarl
import-linter==2.0
@@ -774,7 +838,6 @@ jinja2==3.1.4
# -r requirements/edx/coverage.txt
# code-annotations
# diff-cover
- # fastapi
jmespath==1.0.1
# via
# -r requirements/edx/base.txt
@@ -784,7 +847,7 @@ joblib==1.4.2
# via
# -r requirements/edx/base.txt
# nltk
-jsondiff==2.1.2
+jsondiff==2.2.0
# via
# -r requirements/edx/base.txt
# edx-enterprise
@@ -811,7 +874,7 @@ jwcrypto==1.5.6
# -r requirements/edx/base.txt
# django-oauth-toolkit
# pylti1p3
-kombu==5.3.7
+kombu==5.4.0
# via
# -r requirements/edx/base.txt
# celery
@@ -866,8 +929,6 @@ markdown==3.3.7
# openedx-django-wiki
# staff-graded-xblock
# xblock-poll
-markdown-it-py==3.0.0
- # via rich
markupsafe==2.1.5
# via
# -r requirements/edx/base.txt
@@ -883,8 +944,6 @@ maxminddb==2.6.2
# geoip2
mccabe==0.7.0
# via pylint
-mdurl==0.1.2
- # via markdown-it-py
meilisearch==0.31.4
# via -r requirements/edx/base.txt
mock==5.1.0
@@ -904,6 +963,10 @@ mpmath==1.3.0
# via
# -r requirements/edx/base.txt
# sympy
+msgpack==1.0.8
+ # via
+ # -r requirements/edx/base.txt
+ # cachecontrol
multidict==6.0.5
# via
# -r requirements/edx/base.txt
@@ -992,7 +1055,7 @@ packaging==24.1
# pytest
# snowflake-connector-python
# tox
-pact-python==2.0.1
+pact-python==2.2.1
# via -r requirements/edx/testing.in
pansi==2020.7.3
# via
@@ -1054,6 +1117,19 @@ prompt-toolkit==3.0.47
# via
# -r requirements/edx/base.txt
# click-repl
+proto-plus==1.24.0
+ # via
+ # -r requirements/edx/base.txt
+ # google-api-core
+ # google-cloud-firestore
+protobuf==4.25.4
+ # via
+ # -r requirements/edx/base.txt
+ # google-api-core
+ # google-cloud-firestore
+ # googleapis-common-protos
+ # grpcio-status
+ # proto-plus
psutil==6.0.0
# via
# -r requirements/edx/base.txt
@@ -1070,6 +1146,12 @@ pyasn1==0.6.0
# via
# -r requirements/edx/base.txt
# pgpy
+ # pyasn1-modules
+ # rsa
+pyasn1-modules==0.4.0
+ # via
+ # -r requirements/edx/base.txt
+ # google-auth
pycodestyle==2.8.0
# via
# -c requirements/edx/../constraints.txt
@@ -1101,13 +1183,12 @@ pygments==2.18.0
# -r requirements/edx/coverage.txt
# diff-cover
# py2neo
- # rich
pyjwkest==1.4.2
# via
# -r requirements/edx/base.txt
# edx-token-utils
# lti-consumer-xblock
-pyjwt[crypto]==2.8.0
+pyjwt[crypto]==2.9.0
# via
# -r requirements/edx/base.txt
# drf-jwt
@@ -1115,6 +1196,7 @@ pyjwt[crypto]==2.8.0
# edx-drf-extensions
# edx-proctoring
# edx-rest-api-client
+ # firebase-admin
# pylti1p3
# snowflake-connector-python
# social-auth-core
@@ -1159,7 +1241,7 @@ pynacl==1.5.0
# paramiko
pynliner==0.8.0
# via -r requirements/edx/base.txt
-pyopenssl==24.1.0
+pyopenssl==24.2.1
# via
# -r requirements/edx/base.txt
# optimizely-sdk
@@ -1168,6 +1250,7 @@ pyparsing==3.1.2
# via
# -r requirements/edx/base.txt
# chem
+ # httplib2
# openedx-calc
pyproject-api==1.7.1
# via tox
@@ -1181,7 +1264,7 @@ pysrt==1.1.2
# via
# -r requirements/edx/base.txt
# edxval
-pytest==8.2.2
+pytest==8.3.2
# via
# -r requirements/edx/testing.in
# pylint-pytest
@@ -1223,16 +1306,12 @@ python-dateutil==2.9.0.post0
# olxcleaner
# ora2
# xblock
-python-dotenv==1.0.1
- # via uvicorn
python-ipware==3.0.0
# via
# -r requirements/edx/base.txt
# django-ipware
python-memcached==1.62
# via -r requirements/edx/base.txt
-python-multipart==0.0.9
- # via fastapi
python-slugify==8.0.4
# via
# -r requirements/edx/base.txt
@@ -1277,13 +1356,12 @@ pyyaml==6.0.1
# edx-django-release-util
# edx-i18n-tools
# jsondiff
- # uvicorn
# xblock
random2==1.0.2
# via -r requirements/edx/base.txt
recommender-xblock==2.2.0
# via -r requirements/edx/base.txt
-redis==5.0.7
+redis==5.0.8
# via
# -r requirements/edx/base.txt
# walrus
@@ -1292,7 +1370,7 @@ referencing==0.35.1
# -r requirements/edx/base.txt
# jsonschema
# jsonschema-specifications
-regex==2024.5.15
+regex==2024.7.24
# via
# -r requirements/edx/base.txt
# nltk
@@ -1301,12 +1379,15 @@ requests==2.32.3
# -r requirements/edx/base.txt
# algoliasearch
# analytics-python
+ # cachecontrol
# django-oauth-toolkit
# edx-bulk-grades
# edx-drf-extensions
# edx-enterprise
# edx-rest-api-client
# geoip2
+ # google-api-core
+ # google-cloud-storage
# mailsnake
# meilisearch
# openai
@@ -1325,15 +1406,15 @@ requests-oauthlib==2.0.0
# via
# -r requirements/edx/base.txt
# social-auth-core
-rfc3986[idna2008]==1.5.0
- # via httpx
-rich==13.7.1
- # via typer
-rpds-py==0.19.0
+rpds-py==0.20.0
# via
# -r requirements/edx/base.txt
# jsonschema
# referencing
+rsa==4.9
+ # via
+ # -r requirements/edx/base.txt
+ # google-auth
rules==3.4
# via
# -r requirements/edx/base.txt
@@ -1359,8 +1440,6 @@ semantic-version==2.10.0
# edx-drf-extensions
shapely==2.0.5
# via -r requirements/edx/base.txt
-shellingham==1.5.4
- # via typer
simplejson==3.19.2
# via
# -r requirements/edx/base.txt
@@ -1406,11 +1485,8 @@ slumber==0.7.1
# edx-enterprise
# edx-rest-api-client
sniffio==1.3.1
- # via
- # anyio
- # httpcore
- # httpx
-snowflake-connector-python==3.11.0
+ # via anyio
+snowflake-connector-python==3.12.0
# via
# -r requirements/edx/base.txt
# edx-enterprise
@@ -1456,7 +1532,7 @@ super-csv==3.2.0
# via
# -r requirements/edx/base.txt
# edx-bulk-grades
-sympy==1.13.0
+sympy==1.13.1
# via
# -r requirements/edx/base.txt
# openedx-calc
@@ -1478,15 +1554,13 @@ tomlkit==0.13.0
# -r requirements/edx/base.txt
# pylint
# snowflake-connector-python
-tox==4.16.0
+tox==4.17.0
# via -r requirements/edx/testing.in
-tqdm==4.66.4
+tqdm==4.66.5
# via
# -r requirements/edx/base.txt
# nltk
# openai
-typer==0.12.3
- # via fastapi-cli
typing-extensions==4.12.2
# via
# -r requirements/edx/base.txt
@@ -1500,7 +1574,6 @@ typing-extensions==4.12.2
# pydantic-core
# pylti1p3
# snowflake-connector-python
- # typer
tzdata==2024.1
# via
# -r requirements/edx/base.txt
@@ -1516,23 +1589,19 @@ uritemplate==4.1.1
# -r requirements/edx/base.txt
# drf-spectacular
# drf-yasg
+ # google-api-python-client
urllib3==1.26.19
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# botocore
# elasticsearch
- # pact-python
# py2neo
# requests
user-util==1.1.0
# via -r requirements/edx/base.txt
-uvicorn[standard]==0.30.1
- # via
- # fastapi
- # pact-python
-uvloop==0.19.0
- # via uvicorn
+uvicorn==0.30.5
+ # via pact-python
vine==5.1.0
# via
# -r requirements/edx/base.txt
@@ -1551,8 +1620,6 @@ walrus==0.9.4
# edx-event-bus-redis
watchdog==4.0.1
# via -r requirements/edx/base.txt
-watchfiles==0.22.0
- # via uvicorn
wcwidth==0.2.13
# via
# -r requirements/edx/base.txt
@@ -1575,13 +1642,11 @@ webob==1.8.7
# via
# -r requirements/edx/base.txt
# xblock
-websockets==12.0
- # via uvicorn
wrapt==1.16.0
# via
# -r requirements/edx/base.txt
# astroid
-xblock[django]==4.0.1
+xblock[django]==5.0.0
# via
# -r requirements/edx/base.txt
# acid-xblock
@@ -1618,6 +1683,7 @@ yarl==1.9.4
# via
# -r requirements/edx/base.txt
# aiohttp
+ # pact-python
zipp==3.19.2
# via
# -r requirements/edx/base.txt
diff --git a/requirements/pip-tools.txt b/requirements/pip-tools.txt
index 3630835e94..f7b35489c3 100644
--- a/requirements/pip-tools.txt
+++ b/requirements/pip-tools.txt
@@ -18,7 +18,7 @@ pyproject-hooks==1.1.0
# via
# build
# pip-tools
-wheel==0.43.0
+wheel==0.44.0
# via pip-tools
# The following packages are considered to be unsafe in a requirements file:
diff --git a/requirements/pip.txt b/requirements/pip.txt
index df29e61c60..7a6ada8e0a 100644
--- a/requirements/pip.txt
+++ b/requirements/pip.txt
@@ -4,11 +4,11 @@
#
# make upgrade
#
-wheel==0.43.0
+wheel==0.44.0
# via -r requirements/pip.in
# The following packages are considered to be unsafe in a requirements file:
-pip==24.1.2
+pip==24.2
# via -r requirements/pip.in
-setuptools==70.3.0
+setuptools==72.1.0
# via -r requirements/pip.in
diff --git a/scripts/structures_pruning/requirements/testing.txt b/scripts/structures_pruning/requirements/testing.txt
index d74b204fad..47648d50fd 100644
--- a/scripts/structures_pruning/requirements/testing.txt
+++ b/scripts/structures_pruning/requirements/testing.txt
@@ -32,7 +32,7 @@ pymongo==4.4.0
# via
# -r scripts/structures_pruning/requirements/base.txt
# edx-opaque-keys
-pytest==8.2.2
+pytest==8.3.2
# via -r scripts/structures_pruning/requirements/testing.in
stevedore==5.2.0
# via
diff --git a/scripts/user_retirement/requirements/base.txt b/scripts/user_retirement/requirements/base.txt
index 98972d24c4..47e6e79c22 100644
--- a/scripts/user_retirement/requirements/base.txt
+++ b/scripts/user_retirement/requirements/base.txt
@@ -6,13 +6,13 @@
#
asgiref==3.8.1
# via django
-attrs==23.2.0
+attrs==24.2.0
# via zeep
backoff==2.2.1
# via -r scripts/user_retirement/requirements/base.in
-boto3==1.34.144
+boto3==1.34.154
# via -r scripts/user_retirement/requirements/base.in
-botocore==1.34.144
+botocore==1.34.154
# via
# boto3
# s3transfer
@@ -20,7 +20,7 @@ cachetools==5.4.0
# via google-auth
certifi==2024.7.4
# via requests
-cffi==1.16.0
+cffi==1.17.0
# via
# cryptography
# pynacl
@@ -33,9 +33,9 @@ click==8.1.6
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
# -r scripts/user_retirement/requirements/base.in
# edx-django-utils
-cryptography==42.0.8
+cryptography==43.0.0
# via pyjwt
-django==4.2.14
+django==4.2.15
# via
# -c scripts/user_retirement/requirements/../../../requirements/common_constraints.txt
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
@@ -52,7 +52,7 @@ edx-rest-api-client==5.7.1
# via -r scripts/user_retirement/requirements/base.in
google-api-core==2.19.1
# via google-api-python-client
-google-api-python-client==2.137.0
+google-api-python-client==2.139.0
# via -r scripts/user_retirement/requirements/base.in
google-auth==2.32.0
# via
@@ -91,7 +91,7 @@ platformdirs==4.2.2
# via zeep
proto-plus==1.24.0
# via google-api-core
-protobuf==5.27.2
+protobuf==5.27.3
# via
# google-api-core
# googleapis-common-protos
@@ -106,7 +106,7 @@ pyasn1-modules==0.4.0
# via google-auth
pycparser==2.22
# via cffi
-pyjwt[crypto]==2.8.0
+pyjwt[crypto]==2.9.0
# via
# edx-rest-api-client
# simple-salesforce
diff --git a/scripts/user_retirement/requirements/testing.txt b/scripts/user_retirement/requirements/testing.txt
index c0c8d5abba..006eabeef4 100644
--- a/scripts/user_retirement/requirements/testing.txt
+++ b/scripts/user_retirement/requirements/testing.txt
@@ -8,17 +8,17 @@ asgiref==3.8.1
# via
# -r scripts/user_retirement/requirements/base.txt
# django
-attrs==23.2.0
+attrs==24.2.0
# via
# -r scripts/user_retirement/requirements/base.txt
# zeep
backoff==2.2.1
# via -r scripts/user_retirement/requirements/base.txt
-boto3==1.34.144
+boto3==1.34.154
# via
# -r scripts/user_retirement/requirements/base.txt
# moto
-botocore==1.34.144
+botocore==1.34.154
# via
# -r scripts/user_retirement/requirements/base.txt
# boto3
@@ -32,7 +32,7 @@ certifi==2024.7.4
# via
# -r scripts/user_retirement/requirements/base.txt
# requests
-cffi==1.16.0
+cffi==1.17.0
# via
# -r scripts/user_retirement/requirements/base.txt
# cryptography
@@ -45,14 +45,14 @@ click==8.1.6
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
-cryptography==42.0.8
+cryptography==43.0.0
# via
# -r scripts/user_retirement/requirements/base.txt
# moto
# pyjwt
ddt==1.7.2
# via -r scripts/user_retirement/requirements/testing.in
-django==4.2.14
+django==4.2.15
# via
# -r scripts/user_retirement/requirements/base.txt
# django-crum
@@ -76,7 +76,7 @@ google-api-core==2.19.1
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-python-client
-google-api-python-client==2.137.0
+google-api-python-client==2.139.0
# via -r scripts/user_retirement/requirements/base.txt
google-auth==2.32.0
# via
@@ -152,7 +152,7 @@ proto-plus==1.24.0
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-core
-protobuf==5.27.2
+protobuf==5.27.3
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-core
@@ -175,7 +175,7 @@ pycparser==2.22
# via
# -r scripts/user_retirement/requirements/base.txt
# cffi
-pyjwt[crypto]==2.8.0
+pyjwt[crypto]==2.9.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-rest-api-client
@@ -188,7 +188,7 @@ pyparsing==3.1.2
# via
# -r scripts/user_retirement/requirements/base.txt
# httplib2
-pytest==8.2.2
+pytest==8.3.2
# via -r scripts/user_retirement/requirements/testing.in
python-dateutil==2.9.0.post0
# via
From 9bd692413ad207877e87fd3a796698a308275f1e Mon Sep 17 00:00:00 2001
From: Ahtisham Shahid
Date: Wed, 7 Aug 2024 14:55:02 +0500
Subject: [PATCH 009/242] fix: make course update email pref false (#35224)
fix: updated unit tests
fix: updated unit tests
---
cms/djangoapps/contentstore/utils.py | 2 +-
openedx/core/djangoapps/notifications/base_notification.py | 6 +++---
.../core/djangoapps/notifications/email/tests/test_utils.py | 4 ++--
openedx/core/djangoapps/notifications/models.py | 2 +-
openedx/core/djangoapps/notifications/tests/test_tasks.py | 2 +-
openedx/core/djangoapps/notifications/tests/test_views.py | 4 ++--
6 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py
index b268bd6fcb..631ceeb270 100644
--- a/cms/djangoapps/contentstore/utils.py
+++ b/cms/djangoapps/contentstore/utils.py
@@ -2255,7 +2255,7 @@ def send_course_update_notification(course_key, content, user):
"course_update_content": text_content if len(text_content.strip()) < 10 else "Click here to view",
**extra_context,
},
- notification_type="course_update",
+ notification_type="course_updates",
content_url=f"{settings.LMS_ROOT_URL}/courses/{str(course_key)}/course/updates",
app_name="updates",
audience_filters={},
diff --git a/openedx/core/djangoapps/notifications/base_notification.py b/openedx/core/djangoapps/notifications/base_notification.py
index 302d773287..2813002fdb 100644
--- a/openedx/core/djangoapps/notifications/base_notification.py
+++ b/openedx/core/djangoapps/notifications/base_notification.py
@@ -169,13 +169,13 @@ COURSE_NOTIFICATION_TYPES = {
'email_template': '',
'filters': [FILTER_AUDIT_EXPIRED_USERS_WITH_NO_ROLE]
},
- 'course_update': {
+ 'course_updates': {
'notification_app': 'updates',
- 'name': 'course_update',
+ 'name': 'course_updates',
'is_core': False,
'info': '',
'web': True,
- 'email': True,
+ 'email': False,
'push': True,
'email_cadence': EmailCadence.DAILY,
'non_editable': [],
diff --git a/openedx/core/djangoapps/notifications/email/tests/test_utils.py b/openedx/core/djangoapps/notifications/email/tests/test_utils.py
index 8d72ffd748..6e79c497a4 100644
--- a/openedx/core/djangoapps/notifications/email/tests/test_utils.py
+++ b/openedx/core/djangoapps/notifications/email/tests/test_utils.py
@@ -70,7 +70,7 @@ class TestUtilFunctions(ModuleStoreTestCase):
"""
Notification.objects.all().delete()
create_notification(self.user, self.course.id, app_name='discussion', notification_type='new_comment')
- create_notification(self.user, self.course.id, app_name='updates', notification_type='course_update')
+ create_notification(self.user, self.course.id, app_name='updates', notification_type='course_updates')
app_dict = create_app_notifications_dict(Notification.objects.all())
assert len(app_dict.keys()) == 2
for key in ['discussion', 'updates']:
@@ -130,7 +130,7 @@ class TestContextFunctions(ModuleStoreTestCase):
discussion_notification = create_notification(self.user, self.course.id, app_name='discussion',
notification_type='new_comment')
update_notification = create_notification(self.user, self.course.id, app_name='updates',
- notification_type='course_update')
+ notification_type='course_updates')
app_dict = create_app_notifications_dict(Notification.objects.all())
end_date = datetime.datetime(2024, 3, 24, 12, 0)
params = {
diff --git a/openedx/core/djangoapps/notifications/models.py b/openedx/core/djangoapps/notifications/models.py
index 8bf19edce5..e1bdf94acc 100644
--- a/openedx/core/djangoapps/notifications/models.py
+++ b/openedx/core/djangoapps/notifications/models.py
@@ -23,7 +23,7 @@ NOTIFICATION_CHANNELS = ['web', 'push', 'email']
ADDITIONAL_NOTIFICATION_CHANNEL_SETTINGS = ['email_cadence']
# Update this version when there is a change to any course specific notification type or app.
-COURSE_NOTIFICATION_CONFIG_VERSION = 10
+COURSE_NOTIFICATION_CONFIG_VERSION = 11
def get_course_notification_preference_config():
diff --git a/openedx/core/djangoapps/notifications/tests/test_tasks.py b/openedx/core/djangoapps/notifications/tests/test_tasks.py
index 5058c0f492..706ae29898 100644
--- a/openedx/core/djangoapps/notifications/tests/test_tasks.py
+++ b/openedx/core/djangoapps/notifications/tests/test_tasks.py
@@ -390,7 +390,7 @@ class TestDeleteNotificationTask(ModuleStoreTestCase):
"""
assert not Notification.objects.all()
create_notification(self.user, self.course_1.id, app_name='discussion', notification_type='new_comment')
- create_notification(self.user, self.course_1.id, app_name='updates', notification_type='course_update')
+ create_notification(self.user, self.course_1.id, app_name='updates', notification_type='course_updates')
delete_notifications({'app_name': 'discussion'})
assert not Notification.objects.filter(app_name='discussion')
assert Notification.objects.filter(app_name='updates')
diff --git a/openedx/core/djangoapps/notifications/tests/test_views.py b/openedx/core/djangoapps/notifications/tests/test_views.py
index 389c9e7e06..d2968749ef 100644
--- a/openedx/core/djangoapps/notifications/tests/test_views.py
+++ b/openedx/core/djangoapps/notifications/tests/test_views.py
@@ -275,9 +275,9 @@ class UserNotificationPreferenceAPITest(ModuleStoreTestCase):
'enabled': True,
'core_notification_types': [],
'notification_types': {
- 'course_update': {
+ 'course_updates': {
'web': True,
- 'email': True,
+ 'email': False,
'push': True,
'email_cadence': 'Daily',
'info': ''
From 99760f80f0f7d8c16f77fd1741ebb2346d740b4c Mon Sep 17 00:00:00 2001
From: Awais Qureshi
Date: Wed, 7 Aug 2024 16:40:27 +0500
Subject: [PATCH 010/242] feat: Upgrading api list_course_role_members ( 2nd
api ) (#35105)
* feat: upgrading simple api to drf compatible.
---
lms/djangoapps/instructor/views/api.py | 63 ++++++++++---------
lms/djangoapps/instructor/views/api_urls.py | 2 +-
lms/djangoapps/instructor/views/serializer.py | 30 +++++++++
3 files changed, 65 insertions(+), 30 deletions(-)
create mode 100644 lms/djangoapps/instructor/views/serializer.py
diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py
index a58762d0f3..6abfc81e75 100644
--- a/lms/djangoapps/instructor/views/api.py
+++ b/lms/djangoapps/instructor/views/api.py
@@ -105,6 +105,7 @@ from lms.djangoapps.instructor_task import api as task_api
from lms.djangoapps.instructor_task.api_helper import AlreadyRunningError, QueueConnectionError
from lms.djangoapps.instructor_task.data import InstructorTaskTypes
from lms.djangoapps.instructor_task.models import ReportStore
+from lms.djangoapps.instructor.views.serializer import RoleNameSerializer, UserSerializer
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort, is_course_cohorted
from openedx.core.djangoapps.course_groups.models import CourseUserGroup
@@ -1064,15 +1065,11 @@ def modify_access(request, course_id):
return JsonResponse(response_payload)
-@require_POST
-@ensure_csrf_cookie
-@cache_control(no_cache=True, no_store=True, must_revalidate=True)
-@require_course_permission(permissions.EDIT_COURSE_ACCESS)
-@require_post_params(rolename="'instructor', 'staff', or 'beta'")
-def list_course_role_members(request, course_id):
+@method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True), name='dispatch')
+class ListCourseRoleMembersView(APIView):
"""
- List instructors and staff.
- Requires instructor access.
+ View to list instructors and staff for a specific course.
+ Requires the user to have instructor access.
rolename is one of ['instructor', 'staff', 'beta', 'ccx_coach']
@@ -1088,33 +1085,41 @@ def list_course_role_members(request, course_id):
]
}
"""
- course_id = CourseKey.from_string(course_id)
- course = get_course_with_access(
- request.user, 'instructor', course_id, depth=None
- )
+ permission_classes = (IsAuthenticated, permissions.InstructorPermission)
+ permission_name = permissions.EDIT_COURSE_ACCESS
- rolename = request.POST.get('rolename')
+ @method_decorator(ensure_csrf_cookie)
+ def post(self, request, course_id):
+ """
+ Handles POST request to list instructors and staff.
- if rolename not in ROLES:
- return HttpResponseBadRequest()
+ Args:
+ request (HttpRequest): The request object containing user data.
+ course_id (str): The ID of the course to list instructors and staff for.
- def extract_user_info(user):
- """ convert user into dicts for json view """
+ Returns:
+ Response: A Response object containing the list of instructors and staff or an error message.
- return {
- 'username': user.username,
- 'email': user.email,
- 'first_name': user.first_name,
- 'last_name': user.last_name,
+ Raises:
+ Http404: If the course does not exist.
+ """
+ course_id = CourseKey.from_string(course_id)
+ course = get_course_with_access(
+ request.user, 'instructor', course_id, depth=None
+ )
+ role_serializer = RoleNameSerializer(data=request.data)
+ role_serializer.is_valid(raise_exception=True)
+ rolename = role_serializer.data['rolename']
+
+ users = list_with_level(course.id, rolename)
+ serializer = UserSerializer(users, many=True)
+
+ response_payload = {
+ 'course_id': str(course_id),
+ rolename: serializer.data,
}
- response_payload = {
- 'course_id': str(course_id),
- rolename: list(map(extract_user_info, list_with_level(
- course.id, rolename
- ))),
- }
- return JsonResponse(response_payload)
+ return Response(response_payload, status=status.HTTP_200_OK)
class ProblemResponseReportPostParamsSerializer(serializers.Serializer): # pylint: disable=abstract-method
diff --git a/lms/djangoapps/instructor/views/api_urls.py b/lms/djangoapps/instructor/views/api_urls.py
index 7f94a369ff..fc9d5a3427 100644
--- a/lms/djangoapps/instructor/views/api_urls.py
+++ b/lms/djangoapps/instructor/views/api_urls.py
@@ -23,7 +23,7 @@ v1_api_urls = [
urlpatterns = [
path('students_update_enrollment', api.students_update_enrollment, name='students_update_enrollment'),
path('register_and_enroll_students', api.register_and_enroll_students, name='register_and_enroll_students'),
- path('list_course_role_members', api.list_course_role_members, name='list_course_role_members'),
+ path('list_course_role_members', api.ListCourseRoleMembersView.as_view(), name='list_course_role_members'),
path('modify_access', api.modify_access, name='modify_access'),
path('bulk_beta_modify_access', api.bulk_beta_modify_access, name='bulk_beta_modify_access'),
path('get_problem_responses', api.get_problem_responses, name='get_problem_responses'),
diff --git a/lms/djangoapps/instructor/views/serializer.py b/lms/djangoapps/instructor/views/serializer.py
new file mode 100644
index 0000000000..b4f6f76260
--- /dev/null
+++ b/lms/djangoapps/instructor/views/serializer.py
@@ -0,0 +1,30 @@
+""" Instructor apis serializers. """
+
+from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
+from django.core.exceptions import ValidationError
+from django.utils.translation import gettext as _
+from rest_framework import serializers
+
+from lms.djangoapps.instructor.access import ROLES
+
+
+class RoleNameSerializer(serializers.Serializer): # pylint: disable=abstract-method
+ """
+ Serializer that describes the response of the problem response report generation API.
+ """
+
+ rolename = serializers.CharField(help_text=_("Role name"))
+
+ def validate_rolename(self, value):
+ """
+ Check that the rolename is valid.
+ """
+ if value not in ROLES:
+ raise ValidationError(_("Invalid role name."))
+ return value
+
+
+class UserSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = User
+ fields = ['username', 'email', 'first_name', 'last_name']
From 35651daccb4ee3b02db94ca94670a9875006a46d Mon Sep 17 00:00:00 2001
From: Diana Huang
Date: Wed, 7 Aug 2024 10:58:49 -0400
Subject: [PATCH 011/242] build: Downgrade xblock due to behavior regression.
(#35244)
Xblock used to allow overrides of the built-in xblocks.
The latest version introduced a regression.
---
requirements/constraints.txt | 4 ++++
requirements/edx/base.txt | 3 ++-
requirements/edx/development.txt | 3 ++-
requirements/edx/doc.txt | 3 ++-
requirements/edx/testing.txt | 3 ++-
5 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 0bdb5ad228..c9ff711b8a 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -136,3 +136,7 @@ django-storages<1.14.4
# We are pinning this until after all the smaller migrations get handled and then we can migrate this all at once.
# Ticket to unpin: https://github.com/edx/edx-arch-experiments/issues/760
social-auth-app-django<=5.4.1
+
+# Xblock==5.0.0 changed how entrypoints were loaded, breaking a workaround for overriding blocks.
+# See ticket: https://github.com/openedx/XBlock/issues/777
+xblock[django]==4.0.1
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index c0c4c5b32c..98f79c76d4 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -1258,8 +1258,9 @@ webob==1.8.7
# xblock
wrapt==1.16.0
# via -r requirements/edx/paver.txt
-xblock[django]==5.0.0
+xblock[django]==4.0.1
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
# acid-xblock
# crowdsourcehinter-xblock
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index cf8b8da20f..8266dacec7 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -2231,8 +2231,9 @@ wrapt==1.16.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# astroid
-xblock[django]==5.0.0
+xblock[django]==4.0.1
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# acid-xblock
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 33cf7edaec..8a33f627f9 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -1549,8 +1549,9 @@ webob==1.8.7
# xblock
wrapt==1.16.0
# via -r requirements/edx/base.txt
-xblock[django]==5.0.0
+xblock[django]==4.0.1
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# acid-xblock
# crowdsourcehinter-xblock
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 5cca40a4ea..35ff5a90da 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -1646,8 +1646,9 @@ wrapt==1.16.0
# via
# -r requirements/edx/base.txt
# astroid
-xblock[django]==5.0.0
+xblock[django]==4.0.1
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# acid-xblock
# crowdsourcehinter-xblock
From 2228129dca4f83e7baf3bf3bde9b903a4e524588 Mon Sep 17 00:00:00 2001
From: katrinan029
Date: Wed, 7 Aug 2024 15:34:42 +0000
Subject: [PATCH 012/242] chore: version bump
---
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index c9ff711b8a..6399f234e7 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -20,7 +20,7 @@ celery>=5.2.2,<6.0.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==4.22.4
+edx-enterprise==4.22.6
# Stay on LTS version, remove once this is added to common constraint
Django<5.0
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 98f79c76d4..70250e00b8 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -465,7 +465,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.22.4
+edx-enterprise==4.22.6
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index 8266dacec7..da816531b5 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -740,7 +740,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.22.4
+edx-enterprise==4.22.6
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 8a33f627f9..bda63ad22b 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -545,7 +545,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.22.4
+edx-enterprise==4.22.6
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 35ff5a90da..deecea0d7e 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -569,7 +569,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.22.4
+edx-enterprise==4.22.6
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
From 36200f4232a76ac4363912fb7ca04871f31d0b6f Mon Sep 17 00:00:00 2001
From: Tim McCormack
Date: Wed, 7 Aug 2024 17:05:03 +0000
Subject: [PATCH 013/242] fix: Fix matching of /c4x/ in course asset view
urlpatterns (#35247)
This was failing to capture /c4x/ URLs when the `content_server.use_view`
waffle flag was enabled, so we were returning 404s for those during our
rollout test.
Part of https://github.com/openedx/edx-platform/issues/34702
---
.../contentserver/test/test_views.py | 26 +++++++++++++++++++
openedx/core/djangoapps/contentserver/urls.py | 4 +--
2 files changed, 28 insertions(+), 2 deletions(-)
create mode 100644 openedx/core/djangoapps/contentserver/test/test_views.py
diff --git a/openedx/core/djangoapps/contentserver/test/test_views.py b/openedx/core/djangoapps/contentserver/test/test_views.py
new file mode 100644
index 0000000000..9e0de99ed5
--- /dev/null
+++ b/openedx/core/djangoapps/contentserver/test/test_views.py
@@ -0,0 +1,26 @@
+"""
+Tests for the view version of course asset serving.
+"""
+
+import ddt
+from django.test import TestCase
+from django.urls import resolve
+
+
+@ddt.ddt
+class UrlsTest(TestCase):
+ """
+ Tests for ensuring that the urlpatterns registered to the view are
+ appropriate for the URLs that the middleware historically handled.
+ """
+
+ @ddt.data(
+ '/c4x/edX/Open_DemoX/asset/images_course_image.jpg',
+ '/asset-v1:edX+DemoX.1+2T2019+type@asset+block/DemoX-poster.jpg',
+ '/assets/courseware/v1/0123456789abcdef0123456789abcdef/asset-v1:edX+FAKE101+2024+type@asset+block/HW1.png',
+ )
+ def test_sample_urls(self, sample_url):
+ """
+ Regression test -- c4x URL was previously incorrect in urls.py.
+ """
+ assert resolve(sample_url).view_name == 'openedx.core.djangoapps.contentserver.views.course_assets_view'
diff --git a/openedx/core/djangoapps/contentserver/urls.py b/openedx/core/djangoapps/contentserver/urls.py
index 96bbe6bf38..f3fce5a6f2 100644
--- a/openedx/core/djangoapps/contentserver/urls.py
+++ b/openedx/core/djangoapps/contentserver/urls.py
@@ -2,7 +2,7 @@
URL patterns for course asset serving.
"""
-from django.urls import path, re_path
+from django.urls import re_path
from . import views
@@ -10,7 +10,7 @@ from . import views
# components of the URLs. That's because the view itself is separately
# parsing the paths, for historical reasons. See docstring on views.py.
urlpatterns = [
- path("c4x/", views.course_assets_view),
+ re_path("^c4x/", views.course_assets_view),
re_path("^asset-v1:", views.course_assets_view),
re_path("^assets/courseware/", views.course_assets_view),
]
From 63f327b6829a18439f39b432d57605fa9387a9f0 Mon Sep 17 00:00:00 2001
From: Ahtisham Shahid
Date: Thu, 8 Aug 2024 14:57:32 +0500
Subject: [PATCH 014/242] feat: added events to bulk email tool (#35147)
* feat: added events to bulk email tool
---
lms/djangoapps/bulk_email/apps.py | 4 ++++
lms/djangoapps/bulk_email/signals.py | 30 ++++++++++++++++++++++++++--
lms/djangoapps/bulk_email/tasks.py | 10 +++++++++-
lms/djangoapps/bulk_email/views.py | 9 +++++++++
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
8 files changed, 54 insertions(+), 7 deletions(-)
diff --git a/lms/djangoapps/bulk_email/apps.py b/lms/djangoapps/bulk_email/apps.py
index 2cfb725ba8..63a44fcfcd 100644
--- a/lms/djangoapps/bulk_email/apps.py
+++ b/lms/djangoapps/bulk_email/apps.py
@@ -7,3 +7,7 @@ class BulkEmailConfig(AppConfig):
Application Configuration for bulk_email.
"""
name = 'lms.djangoapps.bulk_email'
+
+ def ready(self):
+ import lms.djangoapps.bulk_email.signals # lint-amnesty, pylint: disable=unused-import
+ from edx_ace.signals import ACE_MESSAGE_SENT # lint-amnesty, pylint: disable=unused-import
diff --git a/lms/djangoapps/bulk_email/signals.py b/lms/djangoapps/bulk_email/signals.py
index 818d222b7a..d45d0ae017 100644
--- a/lms/djangoapps/bulk_email/signals.py
+++ b/lms/djangoapps/bulk_email/signals.py
@@ -1,12 +1,13 @@
"""
Signal handlers for the bulk_email app
"""
-
-
+from django.contrib.auth import get_user_model
from django.dispatch import receiver
+from eventtracking import tracker
from common.djangoapps.student.models import CourseEnrollment
from openedx.core.djangoapps.user_api.accounts.signals import USER_RETIRE_MAILINGS
+from edx_ace.signals import ACE_MESSAGE_SENT
from .models import Optout
@@ -24,3 +25,28 @@ def force_optout_all(sender, **kwargs): # lint-amnesty, pylint: disable=unused-
for enrollment in CourseEnrollment.objects.filter(user=user):
Optout.objects.get_or_create(user=user, course_id=enrollment.course.id)
+
+
+@receiver(ACE_MESSAGE_SENT)
+def ace_email_sent_handler(sender, **kwargs):
+ """
+ When an email is sent using ACE, this method will create an event to detect ace email success status
+ """
+ # Fetch the message object from kwargs, defaulting to None if not present
+ message = kwargs.get('message', None)
+
+ user_model = get_user_model()
+ try:
+ user_id = user_model.objects.get(email=message.recipient.email_address).id
+ except user_model.DoesNotExist:
+ user_id = None
+ course_email = message.context.get('course_email', None)
+ course_id = course_email.course_id if course_email else None
+ tracker.emit(
+ 'edx.bulk_email.sent',
+ {
+ 'message_type': message.name,
+ 'course_id': course_id,
+ 'user_id': user_id,
+ }
+ )
diff --git a/lms/djangoapps/bulk_email/tasks.py b/lms/djangoapps/bulk_email/tasks.py
index afad888fe0..60d45c1564 100644
--- a/lms/djangoapps/bulk_email/tasks.py
+++ b/lms/djangoapps/bulk_email/tasks.py
@@ -26,6 +26,7 @@ from django.utils import timezone
from django.utils.translation import gettext as _
from django.utils.translation import override as override_language
from edx_django_utils.monitoring import set_code_owner_attribute
+from eventtracking import tracker
from markupsafe import escape
from common.djangoapps.util.date_utils import get_default_time_display
@@ -467,7 +468,14 @@ def _send_course_email(entry_id, email_id, to_list, global_email_context, subtas
"send."
)
raise exc
-
+ tracker.emit(
+ 'edx.bulk_email.created',
+ {
+ 'course_id': str(course_email.course_id),
+ 'to_list': to_list,
+ 'total_recipients': total_recipients,
+ }
+ )
# Exclude optouts (if not a retry):
# Note that we don't have to do the optout logic at all if this is a retry,
# because we have presumably already performed the optout logic on the first
diff --git a/lms/djangoapps/bulk_email/views.py b/lms/djangoapps/bulk_email/views.py
index 528baf97b5..9276990915 100644
--- a/lms/djangoapps/bulk_email/views.py
+++ b/lms/djangoapps/bulk_email/views.py
@@ -7,6 +7,7 @@ import logging
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.http import Http404
+from eventtracking import tracker
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
@@ -60,4 +61,12 @@ def opt_out_email_updates(request, token, course_id):
course_id,
)
+ tracker.emit(
+ 'edx.bulk_email.opt_out',
+ {
+ 'course_id': course_id,
+ 'user_id': user.id,
+ }
+ )
+
return render_to_response('bulk_email/unsubscribe_success.html', context)
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 70250e00b8..bb2b85fe92 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -399,7 +399,7 @@ drf-yasg==1.21.7
# via
# django-user-tasks
# edx-api-doc-tools
-edx-ace==1.11.0
+edx-ace==1.11.1
# via -r requirements/edx/kernel.in
edx-api-doc-tools==1.8.0
# via
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index da816531b5..349cbd91fe 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -656,7 +656,7 @@ drf-yasg==1.21.7
# -r requirements/edx/testing.txt
# django-user-tasks
# edx-api-doc-tools
-edx-ace==1.11.0
+edx-ace==1.11.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index bda63ad22b..b7c4a67228 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -479,7 +479,7 @@ drf-yasg==1.21.7
# -r requirements/edx/base.txt
# django-user-tasks
# edx-api-doc-tools
-edx-ace==1.11.0
+edx-ace==1.11.1
# via -r requirements/edx/base.txt
edx-api-doc-tools==1.8.0
# via
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index deecea0d7e..6137e1cea1 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -503,7 +503,7 @@ drf-yasg==1.21.7
# -r requirements/edx/base.txt
# django-user-tasks
# edx-api-doc-tools
-edx-ace==1.11.0
+edx-ace==1.11.1
# via -r requirements/edx/base.txt
edx-api-doc-tools==1.8.0
# via
From e7dbc4883c99bc1ff9c279c00ac6e47679bfc761 Mon Sep 17 00:00:00 2001
From: Ahtisham Shahid
Date: Thu, 8 Aug 2024 19:10:47 +0500
Subject: [PATCH 015/242] fix: resolved content issue in renamed notification
(#35252)
---
openedx/core/djangoapps/notifications/base_notification.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/openedx/core/djangoapps/notifications/base_notification.py b/openedx/core/djangoapps/notifications/base_notification.py
index 2813002fdb..fb558dfec0 100644
--- a/openedx/core/djangoapps/notifications/base_notification.py
+++ b/openedx/core/djangoapps/notifications/base_notification.py
@@ -465,6 +465,8 @@ def get_notification_content(notification_type, context):
'strong': 'strong',
'p': 'p',
}
+ if notification_type == 'course_update':
+ notification_type = 'course_updates'
notification_type = NotificationTypeManager().notification_types.get(notification_type, None)
if notification_type:
notification_type_content_template = notification_type.get('content_template', None)
From aa952c93321eeec06f30c05fccc6f66b0a339873 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=B4mulo=20Penido?=
Date: Thu, 8 Aug 2024 13:31:33 -0300
Subject: [PATCH 016/242] feat: allow copying library v2 XBlocks via existing
copy REST API (#35211)
---
.../core/djangoapps/content_staging/views.py | 31 ++++++++++++++-----
1 file changed, 23 insertions(+), 8 deletions(-)
diff --git a/openedx/core/djangoapps/content_staging/views.py b/openedx/core/djangoapps/content_staging/views.py
index e8b97df412..1b9790cfbe 100644
--- a/openedx/core/djangoapps/content_staging/views.py
+++ b/openedx/core/djangoapps/content_staging/views.py
@@ -10,12 +10,14 @@ from django.utils.decorators import method_decorator
import edx_api_doc_tools as apidocs
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import UsageKey
-from opaque_keys.edx.locator import CourseLocator
+from opaque_keys.edx.locator import CourseLocator, LibraryLocatorV2
from rest_framework.exceptions import NotFound, PermissionDenied, ValidationError
from rest_framework.response import Response
from rest_framework.views import APIView
from common.djangoapps.student.auth import has_studio_read_access
+from openedx.core.djangoapps.content_libraries import api as lib_api
+from openedx.core.djangoapps.xblock import api as xblock_api
from openedx.core.lib.api.view_utils import view_auth_classes
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
@@ -89,18 +91,31 @@ class ClipboardEndpoint(APIView):
if usage_key.block_type in ('course', 'chapter', 'sequential'):
raise ValidationError('Requested XBlock tree is too large')
course_key = usage_key.context_key
- if not isinstance(course_key, CourseLocator):
- # In the future, we'll support libraries too but for now we don't.
- raise ValidationError('Invalid usage key: not a modulestore course')
- # Make sure the user has permission on that course
- if not has_studio_read_access(request.user, course_key):
- raise PermissionDenied("You must be a member of the course team in Studio to export OLX using this API.")
# Load the block and copy it to the user's clipboard
try:
- block = modulestore().get_item(usage_key)
+ if isinstance(course_key, CourseLocator):
+ # Make sure the user has permission on that course
+ if not has_studio_read_access(request.user, course_key):
+ raise PermissionDenied(
+ "You must be a member of the course team in Studio to export OLX using this API."
+ )
+ block = modulestore().get_item(usage_key)
+
+ elif isinstance(course_key, LibraryLocatorV2):
+ lib_api.require_permission_for_library_key(
+ course_key,
+ request.user,
+ lib_api.permissions.CAN_VIEW_THIS_CONTENT_LIBRARY
+ )
+ block = xblock_api.load_block(usage_key, user=None)
+
+ else:
+ raise ValidationError("Invalid usage_key for the content.")
+
except ItemNotFoundError as exc:
raise NotFound("The requested usage key does not exist.") from exc
+
clipboard = api.save_xblock_to_user_clipboard(block=block, user_id=request.user.id)
# Return the current clipboard exactly as if GET was called:
From eb5bef0c23c7eb62bd95eb56e0cf87b3f34373a2 Mon Sep 17 00:00:00 2001
From: Juliana Kang
Date: Thu, 8 Aug 2024 13:39:04 -0400
Subject: [PATCH 017/242] fix: Remove Program details Subscriptions portal CTA
(#35249)
REV-4003
---
.../spec/program_details_view_spec.js | 8 +--
.../program_details_tab_view.underscore | 59 +------------------
.../program_details_view.underscore | 59 +------------------
3 files changed, 6 insertions(+), 120 deletions(-)
diff --git a/lms/static/js/learner_dashboard/spec/program_details_view_spec.js b/lms/static/js/learner_dashboard/spec/program_details_view_spec.js
index 2387ade00b..feaf725261 100644
--- a/lms/static/js/learner_dashboard/spec/program_details_view_spec.js
+++ b/lms/static/js/learner_dashboard/spec/program_details_view_spec.js
@@ -739,8 +739,8 @@ describe('Program Details View', () => {
);
});
- it('should render appropriate subscription text when subscription is active with trial', () => {
- testSubscriptionState(
+ it('should not render appropriate subscription text when subscription is active with trial', () => {
+ testSubscriptionSunsetting(
'active',
'Manage my subscription',
'Trial ends',
@@ -748,8 +748,8 @@ describe('Program Details View', () => {
);
});
- it('should render appropriate subscription text when subscription is active', () => {
- testSubscriptionState(
+ it('should not render appropriate subscription text when subscription is active', () => {
+ testSubscriptionSunsetting(
'active',
'Manage my subscription',
'Your next billing date is',
diff --git a/lms/templates/learner_dashboard/program_details_tab_view.underscore b/lms/templates/learner_dashboard/program_details_tab_view.underscore
index f15265119d..37691cca55 100644
--- a/lms/templates/learner_dashboard/program_details_tab_view.underscore
+++ b/lms/templates/learner_dashboard/program_details_tab_view.underscore
@@ -46,64 +46,7 @@
<% } %>
- <% if (isSubscriptionEligible && (
- completedCount !== totalCount
- || subscriptionState === 'active'
- )
- ) { %>
-
- <% } else if (
+ <% if (
!isSubscriptionEligible
&& is_learner_eligible_for_one_click_purchase
&& (typeof is_mobile_only === 'undefined' || is_mobile_only === false)
diff --git a/lms/templates/learner_dashboard/program_details_view.underscore b/lms/templates/learner_dashboard/program_details_view.underscore
index c2bdca897e..25f1cd5b88 100644
--- a/lms/templates/learner_dashboard/program_details_view.underscore
+++ b/lms/templates/learner_dashboard/program_details_view.underscore
@@ -21,64 +21,7 @@
<% } %>
- <% if (isSubscriptionEligible && (
- completedCount !== totalCount
- || subscriptionState === 'active'
- )
- ) { %>
-
- <% } else if (
+ <% if (
!isSubscriptionEligible
&& is_learner_eligible_for_one_click_purchase
&& (typeof is_mobile_only === 'undefined' || is_mobile_only === false)
From aaa733edd0c664bad95e2a07381a4323d2139449 Mon Sep 17 00:00:00 2001
From: Marlon Keating
Date: Thu, 8 Aug 2024 19:18:18 +0000
Subject: [PATCH 018/242] chore: bump edx-enterprise to 4.23.3
---
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 6399f234e7..02cd4b01ab 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -20,7 +20,7 @@ celery>=5.2.2,<6.0.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==4.22.6
+edx-enterprise==4.23.3
# Stay on LTS version, remove once this is added to common constraint
Django<5.0
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index bb2b85fe92..66923e2501 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -465,7 +465,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.22.6
+edx-enterprise==4.23.3
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index 349cbd91fe..7adede611a 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -740,7 +740,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.22.6
+edx-enterprise==4.23.3
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index b7c4a67228..d61a022ecf 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -545,7 +545,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.22.6
+edx-enterprise==4.23.3
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 6137e1cea1..b95e1f9f0a 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -569,7 +569,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.22.6
+edx-enterprise==4.23.3
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
From 25d5d084e0bae76126d98b33415c1b9eceb7c2ff Mon Sep 17 00:00:00 2001
From: Muhammad Adeel Tajamul
<77053848+muhammadadeeltajamul@users.noreply.github.com>
Date: Mon, 12 Aug 2024 06:18:33 +0500
Subject: [PATCH 019/242] feat: fetched notifications based on time not date
(#35250)
---
.../djangoapps/notifications/email/tasks.py | 8 ++-
.../notifications/email/tests/test_tasks.py | 55 +++++++++++++++----
.../djangoapps/notifications/email/utils.py | 9 +--
3 files changed, 51 insertions(+), 21 deletions(-)
diff --git a/openedx/core/djangoapps/notifications/email/tasks.py b/openedx/core/djangoapps/notifications/email/tasks.py
index 75af99aa86..0d450fe9a9 100644
--- a/openedx/core/djangoapps/notifications/email/tasks.py
+++ b/openedx/core/djangoapps/notifications/email/tasks.py
@@ -70,10 +70,12 @@ def get_user_preferences_for_courses(course_ids, user):
return new_preferences
-def send_digest_email_to_user(user, cadence_type, course_language='en', courses_data=None):
+def send_digest_email_to_user(user, cadence_type, start_date, end_date, course_language='en', courses_data=None):
"""
Send [cadence_type] email to user.
Cadence Type can be EmailCadence.DAILY or EmailCadence.WEEKLY
+ start_date: Datetime object
+ end_date: Datetime object
"""
if cadence_type not in [EmailCadence.DAILY, EmailCadence.WEEKLY]:
raise ValueError('Invalid cadence_type')
@@ -81,7 +83,6 @@ def send_digest_email_to_user(user, cadence_type, course_language='en', courses_
if not is_email_notification_flag_enabled(user):
logger.info(f' Flag disabled for {user.username} ==Temp Log==')
return
- start_date, end_date = get_start_end_date(cadence_type)
notifications = Notification.objects.filter(user=user, email=True,
created__gte=start_date, created__lte=end_date)
if not notifications:
@@ -115,6 +116,7 @@ def send_digest_email_to_all_users(cadence_type):
logger.info(f' Sending cadence email of type {cadence_type}')
users = get_audience_for_cadence_email(cadence_type)
courses_data = {}
+ start_date, end_date = get_start_end_date(cadence_type)
logger.info(f' Email Cadence Audience {len(users)}')
for user in users:
- send_digest_email_to_user(user, cadence_type, courses_data=courses_data)
+ send_digest_email_to_user(user, cadence_type, start_date, end_date, courses_data=courses_data)
diff --git a/openedx/core/djangoapps/notifications/email/tests/test_tasks.py b/openedx/core/djangoapps/notifications/email/tests/test_tasks.py
index 5c88ef3edb..785dcf2a1b 100644
--- a/openedx/core/djangoapps/notifications/email/tests/test_tasks.py
+++ b/openedx/core/djangoapps/notifications/email/tests/test_tasks.py
@@ -16,6 +16,7 @@ from openedx.core.djangoapps.notifications.email.tasks import (
send_digest_email_to_all_users,
send_digest_email_to_user
)
+from openedx.core.djangoapps.notifications.email.utils import get_start_end_date
from openedx.core.djangoapps.notifications.models import CourseNotificationPreference
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@@ -42,8 +43,9 @@ class TestEmailDigestForUser(ModuleStoreTestCase):
"""
Tests email is sent iff waffle flag is enabled
"""
+ start_date, end_date = get_start_end_date(EmailCadence.DAILY)
with override_waffle_flag(ENABLE_EMAIL_NOTIFICATIONS, True):
- send_digest_email_to_user(self.user, EmailCadence.DAILY)
+ send_digest_email_to_user(self.user, EmailCadence.DAILY, start_date, end_date)
assert not mock_func.called
@ddt.data(True, False)
@@ -54,8 +56,9 @@ class TestEmailDigestForUser(ModuleStoreTestCase):
"""
created_date = datetime.datetime.now() - datetime.timedelta(days=1)
create_notification(self.user, self.course.id, created=created_date)
+ start_date, end_date = get_start_end_date(EmailCadence.DAILY)
with override_waffle_flag(ENABLE_EMAIL_NOTIFICATIONS, flag_value):
- send_digest_email_to_user(self.user, EmailCadence.DAILY)
+ send_digest_email_to_user(self.user, EmailCadence.DAILY, start_date, end_date)
assert mock_func.called is flag_value
@patch('edx_ace.ace.send')
@@ -63,9 +66,10 @@ class TestEmailDigestForUser(ModuleStoreTestCase):
"""
Tests email is not sent if notification is created on next day
"""
- create_notification(self.user, self.course.id)
+ start_date, end_date = get_start_end_date(EmailCadence.DAILY)
+ create_notification(self.user, self.course.id, created=end_date + datetime.timedelta(minutes=2))
with override_waffle_flag(ENABLE_EMAIL_NOTIFICATIONS, True):
- send_digest_email_to_user(self.user, EmailCadence.DAILY)
+ send_digest_email_to_user(self.user, EmailCadence.DAILY, start_date, end_date)
assert not mock_func.called
@patch('edx_ace.ace.send')
@@ -73,12 +77,35 @@ class TestEmailDigestForUser(ModuleStoreTestCase):
"""
Tests email is not sent if notification is created day before yesterday
"""
- created_date = datetime.datetime.now() - datetime.timedelta(days=2)
+ start_date, end_date = get_start_end_date(EmailCadence.DAILY)
+ created_date = datetime.datetime.now() - datetime.timedelta(days=1, minutes=18)
create_notification(self.user, self.course.id, created=created_date)
with override_waffle_flag(ENABLE_EMAIL_NOTIFICATIONS, True):
- send_digest_email_to_user(self.user, EmailCadence.DAILY)
+ send_digest_email_to_user(self.user, EmailCadence.DAILY, start_date, end_date)
assert not mock_func.called
+ @ddt.data(
+ (EmailCadence.DAILY, datetime.datetime.now() - datetime.timedelta(days=1, minutes=30), False),
+ (EmailCadence.DAILY, datetime.datetime.now() - datetime.timedelta(minutes=10), True),
+ (EmailCadence.DAILY, datetime.datetime.now() - datetime.timedelta(days=1), True),
+ (EmailCadence.DAILY, datetime.datetime.now() + datetime.timedelta(minutes=20), False),
+ (EmailCadence.WEEKLY, datetime.datetime.now() - datetime.timedelta(days=7, minutes=30), False),
+ (EmailCadence.WEEKLY, datetime.datetime.now() - datetime.timedelta(days=7), True),
+ (EmailCadence.WEEKLY, datetime.datetime.now() - datetime.timedelta(minutes=20), True),
+ (EmailCadence.WEEKLY, datetime.datetime.now() + datetime.timedelta(minutes=20), False),
+ )
+ @ddt.unpack
+ @patch('edx_ace.ace.send')
+ def test_notification_content(self, cadence_type, created_time, notification_created, mock_func):
+ """
+ Tests email only contains notification created within date
+ """
+ start_date, end_date = get_start_end_date(cadence_type)
+ create_notification(self.user, self.course.id, created=created_time)
+ with override_waffle_flag(ENABLE_EMAIL_NOTIFICATIONS, True):
+ send_digest_email_to_user(self.user, EmailCadence.DAILY, start_date, end_date)
+ assert mock_func.called is notification_created
+
@ddt.ddt
class TestEmailDigestAudience(ModuleStoreTestCase):
@@ -146,10 +173,11 @@ class TestEmailDigestAudience(ModuleStoreTestCase):
"""
Tests email is sent only when notifications with email=True exists
"""
- created_date = datetime.datetime.now() - datetime.timedelta(days=1)
+ start_date, end_date = get_start_end_date(EmailCadence.DAILY)
+ created_date = datetime.datetime.now() - datetime.timedelta(hours=23, minutes=59)
create_notification(self.user, self.course.id, created=created_date, email=email_value)
with override_waffle_flag(ENABLE_EMAIL_NOTIFICATIONS, True):
- send_digest_email_to_user(self.user, EmailCadence.DAILY)
+ send_digest_email_to_user(self.user, EmailCadence.DAILY, start_date, end_date)
assert mock_func.called is email_value
@@ -166,7 +194,7 @@ class TestPreferences(ModuleStoreTestCase):
self.user = UserFactory()
self.course = CourseFactory.create(display_name='test course', run="Testing_course")
self.preference = CourseNotificationPreference.objects.create(user=self.user, course_id=self.course.id)
- created_date = datetime.datetime.now() - datetime.timedelta(days=1)
+ created_date = datetime.datetime.now() - datetime.timedelta(hours=23)
create_notification(self.user, self.course.id, notification_type='new_discussion_post', created=created_date)
@patch('edx_ace.ace.send')
@@ -174,13 +202,14 @@ class TestPreferences(ModuleStoreTestCase):
"""
Tests email is send for digest notification preference
"""
+ start_date, end_date = get_start_end_date(EmailCadence.DAILY)
config = self.preference.notification_preference_config
types = config['discussion']['notification_types']
types['new_discussion_post']['email_cadence'] = EmailCadence.DAILY
types['new_discussion_post']['email'] = True
self.preference.save()
with override_waffle_flag(ENABLE_EMAIL_NOTIFICATIONS, True):
- send_digest_email_to_user(self.user, EmailCadence.DAILY)
+ send_digest_email_to_user(self.user, EmailCadence.DAILY, start_date, end_date)
assert mock_func.called
@ddt.data(True, False)
@@ -189,13 +218,14 @@ class TestPreferences(ModuleStoreTestCase):
"""
Tests email is sent iff preference value is True
"""
+ start_date, end_date = get_start_end_date(EmailCadence.DAILY)
config = self.preference.notification_preference_config
types = config['discussion']['notification_types']
types['new_discussion_post']['email_cadence'] = EmailCadence.DAILY
types['new_discussion_post']['email'] = pref_value
self.preference.save()
with override_waffle_flag(ENABLE_EMAIL_NOTIFICATIONS, True):
- send_digest_email_to_user(self.user, EmailCadence.DAILY)
+ send_digest_email_to_user(self.user, EmailCadence.DAILY, start_date, end_date)
assert mock_func.called is pref_value
@patch('edx_ace.ace.send')
@@ -203,10 +233,11 @@ class TestPreferences(ModuleStoreTestCase):
"""
Tests email is not send if digest notification preference doesnot match
"""
+ start_date, end_date = get_start_end_date(EmailCadence.DAILY)
config = self.preference.notification_preference_config
types = config['discussion']['notification_types']
types['new_discussion_post']['email_cadence'] = EmailCadence.WEEKLY
self.preference.save()
with override_waffle_flag(ENABLE_EMAIL_NOTIFICATIONS, True):
- send_digest_email_to_user(self.user, EmailCadence.DAILY)
+ send_digest_email_to_user(self.user, EmailCadence.DAILY, start_date, end_date)
assert not mock_func.called
diff --git a/openedx/core/djangoapps/notifications/email/utils.py b/openedx/core/djangoapps/notifications/email/utils.py
index 3ce2306435..1e0f4c81c7 100644
--- a/openedx/core/djangoapps/notifications/email/utils.py
+++ b/openedx/core/djangoapps/notifications/email/utils.py
@@ -171,13 +171,10 @@ def get_start_end_date(cadence_type):
"""
if cadence_type not in [EmailCadence.DAILY, EmailCadence.WEEKLY]:
raise ValueError('Invalid cadence_type')
- date_today = datetime.datetime.now()
- yesterday = date_today - datetime.timedelta(days=1)
- end_date = datetime.datetime.combine(yesterday, datetime.time.max)
- start_date = end_date
+ end_date = datetime.datetime.now()
+ start_date = end_date - datetime.timedelta(days=1, minutes=15)
if cadence_type == EmailCadence.WEEKLY:
- start_date = end_date - datetime.timedelta(days=6)
- start_date = datetime.datetime.combine(start_date, datetime.time.min)
+ start_date = start_date - datetime.timedelta(days=6)
return utc.localize(start_date), utc.localize(end_date)
From 152b678e62c038507ece293c548500059c8f532c Mon Sep 17 00:00:00 2001
From: Eemaan Amir <57627710+eemaanamir@users.noreply.github.com>
Date: Mon, 12 Aug 2024 12:04:20 +0500
Subject: [PATCH 020/242] feat: save author pronoun separately for notification
to prevent info loss (#35234)
* feat: save author pronoun separately for notification to prevent info loss
* fix: missing pronoun in comment on followed post
* test: updated tests for new comment notifications
---
.../rest_api/discussions_notifications.py | 18 ++++++++--
.../discussion/rest_api/tests/test_tasks.py | 10 ++++--
.../notifications/base_notification.py | 9 +++--
.../notifications/notification_content.py | 35 +++++++++++++++++++
4 files changed, 65 insertions(+), 7 deletions(-)
create mode 100644 openedx/core/djangoapps/notifications/notification_content.py
diff --git a/lms/djangoapps/discussion/rest_api/discussions_notifications.py b/lms/djangoapps/discussion/rest_api/discussions_notifications.py
index f72271f3a6..7249d086cc 100644
--- a/lms/djangoapps/discussion/rest_api/discussions_notifications.py
+++ b/lms/djangoapps/discussion/rest_api/discussions_notifications.py
@@ -118,9 +118,10 @@ class DiscussionNotificationSender:
self.parent_response and
self.creator.id != int(self.thread.user_id)
):
+ author_name = f"{self.parent_response.username}'s"
# use your if author of response is same as author of post.
# use 'their' if comment author is also response author.
- author_name = (
+ author_pronoun = (
# Translators: Replier commented on "your" response to your post
_("your")
if self._response_and_thread_has_same_creator()
@@ -129,10 +130,12 @@ class DiscussionNotificationSender:
_("their")
if self._response_and_comment_has_same_creator()
else f"{self.parent_response.username}'s"
+
)
)
context = {
"author_name": str(author_name),
+ "author_pronoun": str(author_pronoun),
}
self._send_notification([self.thread.user_id], "new_comment", extra_context=context)
@@ -189,10 +192,21 @@ class DiscussionNotificationSender:
if not self.parent_id:
self._send_notification(users, "response_on_followed_post")
else:
+ author_name = f"{self.parent_response.username}'s"
+ # use 'their' if comment author is also response author.
+ author_pronoun = (
+ # Translators: Replier commented on "their" response in a post you're following
+ _("their")
+ if self._response_and_comment_has_same_creator()
+ else f"{self.parent_response.username}'s"
+ )
self._send_notification(
users,
"comment_on_followed_post",
- extra_context={"author_name": self.parent_response.username}
+ extra_context={
+ "author_name": str(author_name),
+ "author_pronoun": str(author_pronoun),
+ }
)
def _create_cohort_course_audience(self):
diff --git a/lms/djangoapps/discussion/rest_api/tests/test_tasks.py b/lms/djangoapps/discussion/rest_api/tests/test_tasks.py
index f3f2a68ae6..28cfe3395c 100644
--- a/lms/djangoapps/discussion/rest_api/tests/test_tasks.py
+++ b/lms/djangoapps/discussion/rest_api/tests/test_tasks.py
@@ -338,6 +338,7 @@ class TestSendResponseNotifications(DiscussionAPIViewTestMixin, ModuleStoreTestC
'replier_name': self.user_3.username,
'post_title': self.thread.title,
'author_name': 'dummy\'s',
+ 'author_pronoun': 'dummy\'s',
'course_name': self.course.display_name,
'sender_id': self.user_3.id
}
@@ -399,7 +400,8 @@ class TestSendResponseNotifications(DiscussionAPIViewTestMixin, ModuleStoreTestC
expected_context = {
'replier_name': self.user_3.username,
'post_title': self.thread.title,
- 'author_name': 'your',
+ 'author_name': 'dummy\'s',
+ 'author_pronoun': 'your',
'course_name': self.course.display_name,
'sender_id': self.user_3.id,
}
@@ -441,7 +443,8 @@ class TestSendResponseNotifications(DiscussionAPIViewTestMixin, ModuleStoreTestC
'sender_id': self.user_2.id,
}
if parent_id:
- expected_context['author_name'] = 'dummy'
+ expected_context['author_name'] = 'dummy\'s'
+ expected_context['author_pronoun'] = 'dummy\'s'
self.assertDictEqual(args.context, expected_context)
self.assertEqual(
args.content_url,
@@ -531,7 +534,8 @@ class TestSendCommentNotification(DiscussionAPIViewTestMixin, ModuleStoreTestCas
send_response_notifications(thread.id, str(self.course.id), self.user_2.id, parent_id=response.id)
handler.assert_called_once()
context = handler.call_args[1]['notification_data'].context
- self.assertEqual(context['author_name'], 'their')
+ self.assertEqual(context['author_name'], 'dummy\'s')
+ self.assertEqual(context['author_pronoun'], 'their')
@override_waffle_flag(ENABLE_NOTIFICATIONS, active=True)
diff --git a/openedx/core/djangoapps/notifications/base_notification.py b/openedx/core/djangoapps/notifications/base_notification.py
index fb558dfec0..2c696ec60d 100644
--- a/openedx/core/djangoapps/notifications/base_notification.py
+++ b/openedx/core/djangoapps/notifications/base_notification.py
@@ -7,6 +7,7 @@ from .email_notifications import EmailCadence
from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole
from .utils import find_app_in_normalized_apps, find_pref_in_normalized_prefs
from ..django_comment_common.models import FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA
+from .notification_content import get_notification_type_content_function
FILTER_AUDIT_EXPIRED_USERS_WITH_NO_ROLE = 'filter_audit_expired_users_with_no_role'
@@ -109,8 +110,9 @@ COURSE_NOTIFICATION_TYPES = {
'is_core': True,
'info': '',
'non_editable': [],
- 'content_template': _('<{p}><{strong}>{replier_name}{strong}> commented on {author_name}\'s response in '
- 'a post you’re following <{strong}>{post_title}{strong}>{p}>'),
+ 'content_template': _('<{p}><{strong}>{replier_name}{strong}> commented on <{strong}>{author_name}'
+ '{strong}> response in a post you’re following <{strong}>{post_title}'
+ '{strong}>{p}>'),
'content_context': {
'post_title': 'Post title',
'author_name': 'author name',
@@ -465,10 +467,13 @@ def get_notification_content(notification_type, context):
'strong': 'strong',
'p': 'p',
}
+ content_function = get_notification_type_content_function(notification_type)
if notification_type == 'course_update':
notification_type = 'course_updates'
notification_type = NotificationTypeManager().notification_types.get(notification_type, None)
if notification_type:
+ if content_function:
+ return content_function(notification_type, context)
notification_type_content_template = notification_type.get('content_template', None)
if notification_type_content_template:
return notification_type_content_template.format(**context, **html_tags_context)
diff --git a/openedx/core/djangoapps/notifications/notification_content.py b/openedx/core/djangoapps/notifications/notification_content.py
new file mode 100644
index 0000000000..1dcdc4fb5a
--- /dev/null
+++ b/openedx/core/djangoapps/notifications/notification_content.py
@@ -0,0 +1,35 @@
+"""
+Helper functions for overriding notification content for given notification type.
+"""
+
+
+def get_notification_type_content_function(notification_type):
+ """
+ Returns the content function for the given notification if it exists.
+ """
+ try:
+ return globals()[f"get_{notification_type}_notification_content"]
+ except KeyError:
+ return None
+
+
+def get_notification_content_with_author_pronoun(notification_type, context):
+ """
+ Helper function to get notification content with author's pronoun.
+ """
+ html_tags_context = {
+ 'strong': 'strong',
+ 'p': 'p',
+ }
+ notification_type_content_template = notification_type.get('content_template', None)
+ if 'author_pronoun' in context:
+ context['author_name'] = context['author_pronoun']
+ if notification_type_content_template:
+ return notification_type_content_template.format(**context, **html_tags_context)
+ return ''
+
+
+# Returns notification content for the new_comment notification.
+get_new_comment_notification_content = get_notification_content_with_author_pronoun
+# Returns notification content for the comment_on_followed_post notification.
+get_comment_on_followed_post_notification_content = get_notification_content_with_author_pronoun
From 5c0942481ce292a90f86259bd223d66e7ceffe9f Mon Sep 17 00:00:00 2001
From: Ahtisham Shahid
Date: Mon, 12 Aug 2024 12:53:55 +0500
Subject: [PATCH 021/242] fix: update course discussion config before course
load (#35219)
fix: update course discussion config before course load
fix: updated unit tests
fix: resolved content issue in renamed notification
---
cms/djangoapps/contentstore/views/course.py | 5 +++++
cms/djangoapps/contentstore/views/tests/test_course_index.py | 4 ++--
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py
index 9f6cfb7c43..4647e4fdcc 100644
--- a/cms/djangoapps/contentstore/views/course.py
+++ b/cms/djangoapps/contentstore/views/course.py
@@ -58,6 +58,7 @@ from common.djangoapps.util.json_request import JsonResponse, JsonResponseBadReq
from common.djangoapps.util.string_utils import _has_non_ascii_characters
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.credit.tasks import update_credit_course_requirements
+from openedx.core.djangoapps.discussions.tasks import update_discussions_settings_from_course
from openedx.core.djangoapps.models.course_details import CourseDetails
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangolib.js_utils import dump_js_escaped_json
@@ -302,6 +303,10 @@ def course_handler(request, course_key_string=None):
else:
return HttpResponseBadRequest()
elif request.method == 'GET': # assume html
+ # Update course discussion settings, sometimes the course discussion settings are not updated
+ # when the course is created, so we need to update them here.
+ course_key = CourseKey.from_string(course_key_string)
+ update_discussions_settings_from_course(course_key)
if course_key_string is None:
return redirect(reverse('home'))
else:
diff --git a/cms/djangoapps/contentstore/views/tests/test_course_index.py b/cms/djangoapps/contentstore/views/tests/test_course_index.py
index c3dcfe5305..30e02214a1 100644
--- a/cms/djangoapps/contentstore/views/tests/test_course_index.py
+++ b/cms/djangoapps/contentstore/views/tests/test_course_index.py
@@ -717,8 +717,8 @@ class TestCourseOutline(CourseTestCase):
"""
Test to check number of queries made to mysql and mongo
"""
- with self.assertNumQueries(29, table_ignorelist=WAFFLE_TABLES):
- with check_mongo_calls(3):
+ with self.assertNumQueries(32, table_ignorelist=WAFFLE_TABLES):
+ with check_mongo_calls(5):
self.client.get_html(reverse_course_url('course_handler', self.course.id))
From 78831cdc62f9b2d5a6c453537cbe1a45928b9783 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 12 Aug 2024 10:01:06 +0000
Subject: [PATCH 022/242] chore(deps): update dependency jest-enzyme to v6.1.2
---
package-lock.json | 13 +++++++------
package.json | 2 +-
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 02dde6f701..837f0adcac 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -86,7 +86,7 @@
"jasmine-core": "2.6.4",
"jasmine-jquery": "git+https://git@github.com/velesin/jasmine-jquery.git#ebad463d592d3fea00c69f26ea18a930e09c7b58",
"jest": "26.0.0",
- "jest-enzyme": "6.0.2",
+ "jest-enzyme": "6.1.2",
"karma": "0.13.22",
"karma-chrome-launcher": "0.2.3",
"karma-coverage": "0.5.5",
@@ -13709,14 +13709,15 @@
}
},
"node_modules/jest-enzyme": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/jest-enzyme/-/jest-enzyme-6.0.2.tgz",
- "integrity": "sha512-lf6tjFA5Lthcazqx1acwo/rTjIKIsOp2jbZCZSi1WmRX0JHOJXa5NfioaaJ4wJeIWoIShU/9Go0OjKCXiAzCAg==",
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/jest-enzyme/-/jest-enzyme-6.1.2.tgz",
+ "integrity": "sha512-+ds7r2ru3QkNJxelQ2tnC6d33pjUSsZHPD3v4TlnHlNMuGX3UKdxm5C46yZBvJICYBvIF+RFKBhLMM4evNM95Q==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "enzyme-matchers": "^6.0.2",
+ "enzyme-matchers": "^6.1.2",
"enzyme-to-json": "^3.3.0",
- "jest-environment-enzyme": "^6.0.2"
+ "jest-environment-enzyme": "^6.1.2"
},
"peerDependencies": {
"enzyme": "3.x",
diff --git a/package.json b/package.json
index 49ab2820d1..397fb287d0 100644
--- a/package.json
+++ b/package.json
@@ -92,7 +92,7 @@
"jasmine-core": "2.6.4",
"jasmine-jquery": "git+https://git@github.com/velesin/jasmine-jquery.git#ebad463d592d3fea00c69f26ea18a930e09c7b58",
"jest": "26.0.0",
- "jest-enzyme": "6.0.2",
+ "jest-enzyme": "6.1.2",
"karma": "0.13.22",
"karma-chrome-launcher": "0.2.3",
"karma-coverage": "0.5.5",
From 456f261fa6a852b2996ab29f28eb5776313310bd Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 12 Aug 2024 10:01:30 +0000
Subject: [PATCH 023/242] chore(deps): update dependency karma-junit-reporter
to v1.2.0
---
package-lock.json | 9 +++++----
package.json | 2 +-
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 837f0adcac..1073a0ae5b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -93,7 +93,7 @@
"karma-firefox-launcher": "0.1.7",
"karma-jasmine": "0.3.8",
"karma-jasmine-html-reporter": "0.2.2",
- "karma-junit-reporter": "1.1.0",
+ "karma-junit-reporter": "1.2.0",
"karma-requirejs": "0.2.6",
"karma-selenium-webdriver-launcher": "github:openedx/karma-selenium-webdriver-launcher#0.0.4-openedx.0",
"karma-sourcemap-loader": "0.3.7",
@@ -15556,10 +15556,11 @@
}
},
"node_modules/karma-junit-reporter": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/karma-junit-reporter/-/karma-junit-reporter-1.1.0.tgz",
- "integrity": "sha512-Iucb7SsKqYxn7azHltDHSI2wNzZ7MEzCQjZPNStEAlxyJMkX5OBWd0D6Sesy/mMKh9FltLDqe23hsZFyNvIKxQ==",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/karma-junit-reporter/-/karma-junit-reporter-1.2.0.tgz",
+ "integrity": "sha512-FeuLOKlXNtJhIQK3oQASbO5QOib762CEHV8+L9wwTQpiZJgp7xKg3sNno66rL5bQPV2soG6fJdAFWqqnMJuh2w==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"path-is-absolute": "^1.0.0",
"xmlbuilder": "8.2.2"
diff --git a/package.json b/package.json
index 397fb287d0..25f128fc7d 100644
--- a/package.json
+++ b/package.json
@@ -99,7 +99,7 @@
"karma-firefox-launcher": "0.1.7",
"karma-jasmine": "0.3.8",
"karma-jasmine-html-reporter": "0.2.2",
- "karma-junit-reporter": "1.1.0",
+ "karma-junit-reporter": "1.2.0",
"karma-requirejs": "0.2.6",
"karma-selenium-webdriver-launcher": "github:openedx/karma-selenium-webdriver-launcher#0.0.4-openedx.0",
"karma-sourcemap-loader": "0.3.7",
From 84644e15974cef33a486c28483cb1e31d94039da Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 12 Aug 2024 10:01:54 +0000
Subject: [PATCH 024/242] chore(deps): update dependency karma-sourcemap-loader
to v0.4.0
---
package-lock.json | 11 ++++++-----
package.json | 2 +-
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 1073a0ae5b..13fba08112 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -96,7 +96,7 @@
"karma-junit-reporter": "1.2.0",
"karma-requirejs": "0.2.6",
"karma-selenium-webdriver-launcher": "github:openedx/karma-selenium-webdriver-launcher#0.0.4-openedx.0",
- "karma-sourcemap-loader": "0.3.7",
+ "karma-sourcemap-loader": "0.4.0",
"karma-spec-reporter": "0.0.36",
"karma-webpack": "^5.0.1",
"plato": "1.7.0",
@@ -15593,12 +15593,13 @@
}
},
"node_modules/karma-sourcemap-loader": {
- "version": "0.3.7",
- "resolved": "https://registry.npmjs.org/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.7.tgz",
- "integrity": "sha512-zu99gTgKf6KhLg14c+e+SHb297A8MX9TRxjW+USEmk/xq2ULnyvkfZuOZOLHU6522QfJai1SZznP/brNwivtDg==",
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/karma-sourcemap-loader/-/karma-sourcemap-loader-0.4.0.tgz",
+ "integrity": "sha512-xCRL3/pmhAYF3I6qOrcn0uhbQevitc2DERMPH82FMnG+4WReoGcGFZb1pURf2a5apyrOHRdvD+O6K7NljqKHyA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "graceful-fs": "^4.1.2"
+ "graceful-fs": "^4.2.10"
}
},
"node_modules/karma-spec-reporter": {
diff --git a/package.json b/package.json
index 25f128fc7d..ccb46f0729 100644
--- a/package.json
+++ b/package.json
@@ -102,7 +102,7 @@
"karma-junit-reporter": "1.2.0",
"karma-requirejs": "0.2.6",
"karma-selenium-webdriver-launcher": "github:openedx/karma-selenium-webdriver-launcher#0.0.4-openedx.0",
- "karma-sourcemap-loader": "0.3.7",
+ "karma-sourcemap-loader": "0.4.0",
"karma-spec-reporter": "0.0.36",
"karma-webpack": "^5.0.1",
"plato": "1.7.0",
From 212f3ea2c201897878e5d2d81df06f682e10aecf Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 12 Aug 2024 10:02:17 +0000
Subject: [PATCH 025/242] chore(deps): update dependency react-test-renderer to
v16.14.0
---
package-lock.json | 29 +++++++++++++++++++++--------
package.json | 2 +-
2 files changed, 22 insertions(+), 9 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 13fba08112..947929ee12 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -100,7 +100,7 @@
"karma-spec-reporter": "0.0.36",
"karma-webpack": "^5.0.1",
"plato": "1.7.0",
- "react-test-renderer": "16.4.0",
+ "react-test-renderer": "16.14.0",
"selenium-webdriver": "3.4.0",
"sinon": "2.4.1",
"squirejs": "0.1.0",
@@ -19974,18 +19974,31 @@
}
},
"node_modules/react-test-renderer": {
- "version": "16.4.0",
- "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.4.0.tgz",
- "integrity": "sha512-Seh1t9xFY6TKiV/hRlPzUkqX1xHOiKIMsctfU0cggo1ajsLjoIJFL520LlrxV+4/VIj+clrCeH6s/aVv/vTStg==",
+ "version": "16.14.0",
+ "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.14.0.tgz",
+ "integrity": "sha512-L8yPjqPE5CZO6rKsKXRO/rVPiaCOy0tQQJbC+UjPNlobl5mad59lvPjwFsQHTvL03caVDIVr9x9/OSgDe6I5Eg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "fbjs": "^0.8.16",
"object-assign": "^4.1.1",
- "prop-types": "^15.6.0",
- "react-is": "^16.4.0"
+ "prop-types": "^15.6.2",
+ "react-is": "^16.8.6",
+ "scheduler": "^0.19.1"
},
"peerDependencies": {
- "react": "^16.0.0"
+ "react": "^16.14.0"
+ }
+ },
+ "node_modules/react-test-renderer/node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
}
},
"node_modules/react-transition-group": {
diff --git a/package.json b/package.json
index ccb46f0729..1ae38f189b 100644
--- a/package.json
+++ b/package.json
@@ -106,7 +106,7 @@
"karma-spec-reporter": "0.0.36",
"karma-webpack": "^5.0.1",
"plato": "1.7.0",
- "react-test-renderer": "16.4.0",
+ "react-test-renderer": "16.14.0",
"selenium-webdriver": "3.4.0",
"sinon": "2.4.1",
"squirejs": "0.1.0",
From 892ca4d0fdd5b05cabe3b64027fda1439a8b1edc Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 12 Aug 2024 10:03:41 +0000
Subject: [PATCH 026/242] fix(deps): update babel monorepo
---
package-lock.json | 1265 ++++++++++++++++++++++++---------------------
package.json | 4 +-
2 files changed, 690 insertions(+), 579 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 947929ee12..ac2919ce21 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,11 +9,11 @@
"version": "0.1.0",
"hasInstallScript": true,
"dependencies": {
- "@babel/core": "7.19.0",
+ "@babel/core": "7.25.2",
"@babel/plugin-proposal-object-rest-spread": "^7.18.9",
"@babel/plugin-transform-object-assign": "^7.18.6",
"@babel/preset-env": "^7.19.0",
- "@babel/preset-react": "7.18.6",
+ "@babel/preset-react": "7.24.7",
"@edx/brand-edx.org": "^2.0.7",
"@edx/edx-bootstrap": "1.0.4",
"@edx/edx-proctoring": "^4.18.1",
@@ -132,11 +132,12 @@
}
},
"node_modules/@babel/code-frame": {
- "version": "7.24.2",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz",
- "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz",
+ "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==",
+ "license": "MIT",
"dependencies": {
- "@babel/highlight": "^7.24.2",
+ "@babel/highlight": "^7.24.7",
"picocolors": "^1.0.0"
},
"engines": {
@@ -144,33 +145,35 @@
}
},
"node_modules/@babel/compat-data": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz",
- "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==",
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz",
+ "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/core": {
- "version": "7.19.0",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.0.tgz",
- "integrity": "sha512-reM4+U7B9ss148rh2n1Qs9ASS+w94irYXga7c2jaQv9RVzpS7Mv1a9rnYYwuDa45G+DkORt9g6An2k/V4d9LbQ==",
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz",
+ "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==",
+ "license": "MIT",
"dependencies": {
- "@ampproject/remapping": "^2.1.0",
- "@babel/code-frame": "^7.18.6",
- "@babel/generator": "^7.19.0",
- "@babel/helper-compilation-targets": "^7.19.0",
- "@babel/helper-module-transforms": "^7.19.0",
- "@babel/helpers": "^7.19.0",
- "@babel/parser": "^7.19.0",
- "@babel/template": "^7.18.10",
- "@babel/traverse": "^7.19.0",
- "@babel/types": "^7.19.0",
- "convert-source-map": "^1.7.0",
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.24.7",
+ "@babel/generator": "^7.25.0",
+ "@babel/helper-compilation-targets": "^7.25.2",
+ "@babel/helper-module-transforms": "^7.25.2",
+ "@babel/helpers": "^7.25.0",
+ "@babel/parser": "^7.25.0",
+ "@babel/template": "^7.25.0",
+ "@babel/traverse": "^7.25.2",
+ "@babel/types": "^7.25.2",
+ "convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
- "json5": "^2.2.1",
- "semver": "^6.3.0"
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
},
"engines": {
"node": ">=6.9.0"
@@ -180,12 +183,19 @@
"url": "https://opencollective.com/babel"
}
},
+ "node_modules/@babel/core/node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "license": "MIT"
+ },
"node_modules/@babel/generator": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz",
- "integrity": "sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==",
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz",
+ "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==",
+ "license": "MIT",
"dependencies": {
- "@babel/types": "^7.24.0",
+ "@babel/types": "^7.25.0",
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25",
"jsesc": "^2.5.1"
@@ -195,35 +205,39 @@
}
},
"node_modules/@babel/helper-annotate-as-pure": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz",
- "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz",
+ "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==",
+ "license": "MIT",
"dependencies": {
- "@babel/types": "^7.22.5"
+ "@babel/types": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-builder-binary-assignment-operator-visitor": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz",
- "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz",
+ "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==",
+ "license": "MIT",
"dependencies": {
- "@babel/types": "^7.22.15"
+ "@babel/traverse": "^7.24.7",
+ "@babel/types": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-compilation-targets": {
- "version": "7.23.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz",
- "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==",
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz",
+ "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==",
+ "license": "MIT",
"dependencies": {
- "@babel/compat-data": "^7.23.5",
- "@babel/helper-validator-option": "^7.23.5",
- "browserslist": "^4.22.2",
+ "@babel/compat-data": "^7.25.2",
+ "@babel/helper-validator-option": "^7.24.8",
+ "browserslist": "^4.23.1",
"lru-cache": "^5.1.1",
"semver": "^6.3.1"
},
@@ -232,18 +246,17 @@
}
},
"node_modules/@babel/helper-create-class-features-plugin": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.1.tgz",
- "integrity": "sha512-1yJa9dX9g//V6fDebXoEfEsxkZHk3Hcbm+zLhyu6qVgYFLvmTALTeV+jNU9e5RnYtioBrGEOdoI2joMSNQ/+aA==",
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.0.tgz",
+ "integrity": "sha512-GYM6BxeQsETc9mnct+nIIpf63SAyzvyYN7UB/IlTyd+MBg06afFGp0mIeUqGyWgS2mxad6vqbMrHVlaL3m70sQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-annotate-as-pure": "^7.22.5",
- "@babel/helper-environment-visitor": "^7.22.20",
- "@babel/helper-function-name": "^7.23.0",
- "@babel/helper-member-expression-to-functions": "^7.23.0",
- "@babel/helper-optimise-call-expression": "^7.22.5",
- "@babel/helper-replace-supers": "^7.24.1",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
- "@babel/helper-split-export-declaration": "^7.22.6",
+ "@babel/helper-annotate-as-pure": "^7.24.7",
+ "@babel/helper-member-expression-to-functions": "^7.24.8",
+ "@babel/helper-optimise-call-expression": "^7.24.7",
+ "@babel/helper-replace-supers": "^7.25.0",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7",
+ "@babel/traverse": "^7.25.0",
"semver": "^6.3.1"
},
"engines": {
@@ -254,11 +267,12 @@
}
},
"node_modules/@babel/helper-create-regexp-features-plugin": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz",
- "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==",
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz",
+ "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-annotate-as-pure": "^7.22.5",
+ "@babel/helper-annotate-as-pure": "^7.24.7",
"regexpu-core": "^5.3.1",
"semver": "^6.3.1"
},
@@ -284,69 +298,42 @@
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
}
},
- "node_modules/@babel/helper-environment-visitor": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
- "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-function-name": {
- "version": "7.23.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
- "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
- "dependencies": {
- "@babel/template": "^7.22.15",
- "@babel/types": "^7.23.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-hoist-variables": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
- "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
- "dependencies": {
- "@babel/types": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/helper-member-expression-to-functions": {
- "version": "7.23.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz",
- "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==",
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz",
+ "integrity": "sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==",
+ "license": "MIT",
"dependencies": {
- "@babel/types": "^7.23.0"
+ "@babel/traverse": "^7.24.8",
+ "@babel/types": "^7.24.8"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-imports": {
- "version": "7.24.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz",
- "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz",
+ "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==",
+ "license": "MIT",
"dependencies": {
- "@babel/types": "^7.24.0"
+ "@babel/traverse": "^7.24.7",
+ "@babel/types": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
- "version": "7.23.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz",
- "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==",
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz",
+ "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-environment-visitor": "^7.22.20",
- "@babel/helper-module-imports": "^7.22.15",
- "@babel/helper-simple-access": "^7.22.5",
- "@babel/helper-split-export-declaration": "^7.22.6",
- "@babel/helper-validator-identifier": "^7.22.20"
+ "@babel/helper-module-imports": "^7.24.7",
+ "@babel/helper-simple-access": "^7.24.7",
+ "@babel/helper-validator-identifier": "^7.24.7",
+ "@babel/traverse": "^7.25.2"
},
"engines": {
"node": ">=6.9.0"
@@ -356,32 +343,35 @@
}
},
"node_modules/@babel/helper-optimise-call-expression": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz",
- "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz",
+ "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==",
+ "license": "MIT",
"dependencies": {
- "@babel/types": "^7.22.5"
+ "@babel/types": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-plugin-utils": {
- "version": "7.24.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz",
- "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==",
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz",
+ "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-remap-async-to-generator": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz",
- "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==",
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz",
+ "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-annotate-as-pure": "^7.22.5",
- "@babel/helper-environment-visitor": "^7.22.20",
- "@babel/helper-wrap-function": "^7.22.20"
+ "@babel/helper-annotate-as-pure": "^7.24.7",
+ "@babel/helper-wrap-function": "^7.25.0",
+ "@babel/traverse": "^7.25.0"
},
"engines": {
"node": ">=6.9.0"
@@ -391,13 +381,14 @@
}
},
"node_modules/@babel/helper-replace-supers": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz",
- "integrity": "sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==",
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz",
+ "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-environment-visitor": "^7.22.20",
- "@babel/helper-member-expression-to-functions": "^7.23.0",
- "@babel/helper-optimise-call-expression": "^7.22.5"
+ "@babel/helper-member-expression-to-functions": "^7.24.8",
+ "@babel/helper-optimise-call-expression": "^7.24.7",
+ "@babel/traverse": "^7.25.0"
},
"engines": {
"node": ">=6.9.0"
@@ -407,94 +398,92 @@
}
},
"node_modules/@babel/helper-simple-access": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
- "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz",
+ "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==",
+ "license": "MIT",
"dependencies": {
- "@babel/types": "^7.22.5"
+ "@babel/traverse": "^7.24.7",
+ "@babel/types": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-skip-transparent-expression-wrappers": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz",
- "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz",
+ "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/types": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-split-export-declaration": {
- "version": "7.22.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
- "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
- "dependencies": {
- "@babel/types": "^7.22.5"
+ "@babel/traverse": "^7.24.7",
+ "@babel/types": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz",
- "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==",
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz",
+ "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
- "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz",
+ "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-option": {
- "version": "7.23.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz",
- "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==",
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz",
+ "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-wrap-function": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz",
- "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==",
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz",
+ "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-function-name": "^7.22.5",
- "@babel/template": "^7.22.15",
- "@babel/types": "^7.22.19"
+ "@babel/template": "^7.25.0",
+ "@babel/traverse": "^7.25.0",
+ "@babel/types": "^7.25.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helpers": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.1.tgz",
- "integrity": "sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==",
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz",
+ "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==",
+ "license": "MIT",
"dependencies": {
- "@babel/template": "^7.24.0",
- "@babel/traverse": "^7.24.1",
- "@babel/types": "^7.24.0"
+ "@babel/template": "^7.25.0",
+ "@babel/types": "^7.25.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/highlight": {
- "version": "7.24.2",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz",
- "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz",
+ "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-validator-identifier": "^7.22.20",
+ "@babel/helper-validator-identifier": "^7.24.7",
"chalk": "^2.4.2",
"js-tokens": "^4.0.0",
"picocolors": "^1.0.0"
@@ -504,9 +493,13 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz",
- "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==",
+ "version": "7.25.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz",
+ "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.25.2"
+ },
"bin": {
"parser": "bin/babel-parser.js"
},
@@ -514,12 +507,44 @@
"node": ">=6.0.0"
}
},
- "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.1.tgz",
- "integrity": "sha512-y4HqEnkelJIOQGd+3g1bTeKsA5c6qM7eOn7VggGVbBc0y8MLSKHacwcIE2PplNlQSj0PqS9rrXL/nkPVK+kUNg==",
+ "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
+ "version": "7.25.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz",
+ "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/traverse": "^7.25.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": {
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz",
+ "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz",
+ "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.8"
},
"engines": {
"node": ">=6.9.0"
@@ -529,13 +554,14 @@
}
},
"node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.1.tgz",
- "integrity": "sha512-Hj791Ii4ci8HqnaKHAlLNs+zaLXb0EzSDhiAWp5VNlyvCNymYfacs64pxTxbH1znW/NcArSmwpmG9IKE/TUVVQ==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz",
+ "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
- "@babel/plugin-transform-optional-chaining": "^7.24.1"
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7",
+ "@babel/plugin-transform-optional-chaining": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -545,12 +571,13 @@
}
},
"node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.1.tgz",
- "integrity": "sha512-m9m/fXsXLiHfwdgydIFnpk+7jlVbnvlK5B2EKiPdLUb6WX654ZaaEWJUjk8TftRbZpK0XibovlLWX4KIZhV6jw==",
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz",
+ "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-environment-visitor": "^7.22.20",
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/traverse": "^7.25.0"
},
"engines": {
"node": ">=6.9.0"
@@ -660,11 +687,12 @@
}
},
"node_modules/@babel/plugin-syntax-import-assertions": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.1.tgz",
- "integrity": "sha512-IuwnI5XnuF189t91XbxmXeCDz3qs6iDRO7GJ++wcfgeXNs/8FmIlKcpDSXNVyuLQxlwvskmI3Ct73wUODkJBlQ==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz",
+ "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -674,11 +702,12 @@
}
},
"node_modules/@babel/plugin-syntax-import-attributes": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.1.tgz",
- "integrity": "sha512-zhQTMH0X2nVLnb04tz+s7AMuasX8U0FnpE+nHTOhSOINjWMnopoZTxtIKsd45n4GQ/HIZLyfIpoul8e2m0DnRA==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz",
+ "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -710,11 +739,12 @@
}
},
"node_modules/@babel/plugin-syntax-jsx": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz",
- "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz",
+ "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -833,11 +863,12 @@
}
},
"node_modules/@babel/plugin-transform-arrow-functions": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.1.tgz",
- "integrity": "sha512-ngT/3NkRhsaep9ck9uj2Xhv9+xB1zShY3tM3g6om4xxCELwCDN4g4Aq5dRn48+0hasAql7s2hdBOysCfNpr4fw==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz",
+ "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -847,14 +878,15 @@
}
},
"node_modules/@babel/plugin-transform-async-generator-functions": {
- "version": "7.24.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.3.tgz",
- "integrity": "sha512-Qe26CMYVjpQxJ8zxM1340JFNjZaF+ISWpr1Kt/jGo+ZTUzKkfw/pphEWbRCb+lmSM6k/TOgfYLvmbHkUQ0asIg==",
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.0.tgz",
+ "integrity": "sha512-uaIi2FdqzjpAMvVqvB51S42oC2JEVgh0LDsGfZVDysWE8LrJtQC2jvKmOqEYThKyB7bDEb7BP1GYWDm7tABA0Q==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-environment-visitor": "^7.22.20",
- "@babel/helper-plugin-utils": "^7.24.0",
- "@babel/helper-remap-async-to-generator": "^7.22.20",
- "@babel/plugin-syntax-async-generators": "^7.8.4"
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/helper-remap-async-to-generator": "^7.25.0",
+ "@babel/plugin-syntax-async-generators": "^7.8.4",
+ "@babel/traverse": "^7.25.0"
},
"engines": {
"node": ">=6.9.0"
@@ -864,13 +896,14 @@
}
},
"node_modules/@babel/plugin-transform-async-to-generator": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.1.tgz",
- "integrity": "sha512-AawPptitRXp1y0n4ilKcGbRYWfbbzFWz2NqNu7dacYDtFtz0CMjG64b3LQsb3KIgnf4/obcUL78hfaOS7iCUfw==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz",
+ "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-module-imports": "^7.24.1",
- "@babel/helper-plugin-utils": "^7.24.0",
- "@babel/helper-remap-async-to-generator": "^7.22.20"
+ "@babel/helper-module-imports": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/helper-remap-async-to-generator": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -880,11 +913,12 @@
}
},
"node_modules/@babel/plugin-transform-block-scoped-functions": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.1.tgz",
- "integrity": "sha512-TWWC18OShZutrv9C6mye1xwtam+uNi2bnTOCBUd5sZxyHOiWbU6ztSROofIMrK84uweEZC219POICK/sTYwfgg==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz",
+ "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -894,11 +928,12 @@
}
},
"node_modules/@babel/plugin-transform-block-scoping": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.1.tgz",
- "integrity": "sha512-h71T2QQvDgM2SmT29UYU6ozjMlAt7s7CSs5Hvy8f8cf/GM/Z4a2zMfN+fjVGaieeCrXR3EdQl6C4gQG+OgmbKw==",
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz",
+ "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.8"
},
"engines": {
"node": ">=6.9.0"
@@ -908,12 +943,13 @@
}
},
"node_modules/@babel/plugin-transform-class-properties": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.1.tgz",
- "integrity": "sha512-OMLCXi0NqvJfORTaPQBwqLXHhb93wkBKZ4aNwMl6WtehO7ar+cmp+89iPEQPqxAnxsOKTaMcs3POz3rKayJ72g==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz",
+ "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.24.1",
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-create-class-features-plugin": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -923,12 +959,13 @@
}
},
"node_modules/@babel/plugin-transform-class-static-block": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.1.tgz",
- "integrity": "sha512-FUHlKCn6J3ERiu8Dv+4eoz7w8+kFLSyeVG4vDAikwADGjUCoHw/JHokyGtr8OR4UjpwPVivyF+h8Q5iv/JmrtA==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz",
+ "integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.24.1",
- "@babel/helper-plugin-utils": "^7.24.0",
+ "@babel/helper-create-class-features-plugin": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7",
"@babel/plugin-syntax-class-static-block": "^7.14.5"
},
"engines": {
@@ -939,17 +976,16 @@
}
},
"node_modules/@babel/plugin-transform-classes": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.1.tgz",
- "integrity": "sha512-ZTIe3W7UejJd3/3R4p7ScyyOoafetUShSf4kCqV0O7F/RiHxVj/wRaRnQlrGwflvcehNA8M42HkAiEDYZu2F1Q==",
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.0.tgz",
+ "integrity": "sha512-xyi6qjr/fYU304fiRwFbekzkqVJZ6A7hOjWZd+89FVcBqPV3S9Wuozz82xdpLspckeaafntbzglaW4pqpzvtSw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-annotate-as-pure": "^7.22.5",
- "@babel/helper-compilation-targets": "^7.23.6",
- "@babel/helper-environment-visitor": "^7.22.20",
- "@babel/helper-function-name": "^7.23.0",
- "@babel/helper-plugin-utils": "^7.24.0",
- "@babel/helper-replace-supers": "^7.24.1",
- "@babel/helper-split-export-declaration": "^7.22.6",
+ "@babel/helper-annotate-as-pure": "^7.24.7",
+ "@babel/helper-compilation-targets": "^7.24.8",
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/helper-replace-supers": "^7.25.0",
+ "@babel/traverse": "^7.25.0",
"globals": "^11.1.0"
},
"engines": {
@@ -960,12 +996,13 @@
}
},
"node_modules/@babel/plugin-transform-computed-properties": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.1.tgz",
- "integrity": "sha512-5pJGVIUfJpOS+pAqBQd+QMaTD2vCL/HcePooON6pDpHgRp4gNRmzyHTPIkXntwKsq3ayUFVfJaIKPw2pOkOcTw==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz",
+ "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0",
- "@babel/template": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/template": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -975,11 +1012,12 @@
}
},
"node_modules/@babel/plugin-transform-destructuring": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.1.tgz",
- "integrity": "sha512-ow8jciWqNxR3RYbSNVuF4U2Jx130nwnBnhRw6N6h1bOejNkABmcI5X5oz29K4alWX7vf1C+o6gtKXikzRKkVdw==",
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz",
+ "integrity": "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.8"
},
"engines": {
"node": ">=6.9.0"
@@ -989,12 +1027,13 @@
}
},
"node_modules/@babel/plugin-transform-dotall-regex": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.1.tgz",
- "integrity": "sha512-p7uUxgSoZwZ2lPNMzUkqCts3xlp8n+o05ikjy7gbtFJSt9gdU88jAmtfmOxHM14noQXBxfgzf2yRWECiNVhTCw==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz",
+ "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.22.15",
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-create-regexp-features-plugin": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1004,11 +1043,12 @@
}
},
"node_modules/@babel/plugin-transform-duplicate-keys": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.1.tgz",
- "integrity": "sha512-msyzuUnvsjsaSaocV6L7ErfNsa5nDWL1XKNnDePLgmz+WdU4w/J8+AxBMrWfi9m4IxfL5sZQKUPQKDQeeAT6lA==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz",
+ "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1017,12 +1057,29 @@
"@babel/core": "^7.0.0-0"
}
},
- "node_modules/@babel/plugin-transform-dynamic-import": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.1.tgz",
- "integrity": "sha512-av2gdSTyXcJVdI+8aFZsCAtR29xJt0S5tas+Ef8NvBNmD1a+N/3ecMLeMBgfcK+xzsjdLDT6oHt+DFPyeqUbDA==",
+ "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": {
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz",
+ "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0",
+ "@babel/helper-create-regexp-features-plugin": "^7.25.0",
+ "@babel/helper-plugin-utils": "^7.24.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-dynamic-import": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz",
+ "integrity": "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7",
"@babel/plugin-syntax-dynamic-import": "^7.8.3"
},
"engines": {
@@ -1033,12 +1090,13 @@
}
},
"node_modules/@babel/plugin-transform-exponentiation-operator": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.1.tgz",
- "integrity": "sha512-U1yX13dVBSwS23DEAqU+Z/PkwE9/m7QQy8Y9/+Tdb8UWYaGNDYwTLi19wqIAiROr8sXVum9A/rtiH5H0boUcTw==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz",
+ "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15",
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1048,11 +1106,12 @@
}
},
"node_modules/@babel/plugin-transform-export-namespace-from": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.1.tgz",
- "integrity": "sha512-Ft38m/KFOyzKw2UaJFkWG9QnHPG/Q/2SkOrRk4pNBPg5IPZ+dOxcmkK5IyuBcxiNPyyYowPGUReyBvrvZs7IlQ==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz",
+ "integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0",
+ "@babel/helper-plugin-utils": "^7.24.7",
"@babel/plugin-syntax-export-namespace-from": "^7.8.3"
},
"engines": {
@@ -1063,12 +1122,13 @@
}
},
"node_modules/@babel/plugin-transform-for-of": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.1.tgz",
- "integrity": "sha512-OxBdcnF04bpdQdR3i4giHZNZQn7cm8RQKcSwA17wAAqEELo1ZOwp5FFgeptWUQXFyT9kwHo10aqqauYkRZPCAg==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz",
+ "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5"
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1078,13 +1138,14 @@
}
},
"node_modules/@babel/plugin-transform-function-name": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.1.tgz",
- "integrity": "sha512-BXmDZpPlh7jwicKArQASrj8n22/w6iymRnvHYYd2zO30DbE277JO20/7yXJT3QxDPtiQiOxQBbZH4TpivNXIxA==",
+ "version": "7.25.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz",
+ "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-compilation-targets": "^7.23.6",
- "@babel/helper-function-name": "^7.23.0",
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-compilation-targets": "^7.24.8",
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/traverse": "^7.25.1"
},
"engines": {
"node": ">=6.9.0"
@@ -1094,11 +1155,12 @@
}
},
"node_modules/@babel/plugin-transform-json-strings": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.1.tgz",
- "integrity": "sha512-U7RMFmRvoasscrIFy5xA4gIp8iWnWubnKkKuUGJjsuOH7GfbMkB+XZzeslx2kLdEGdOJDamEmCqOks6e8nv8DQ==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz",
+ "integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0",
+ "@babel/helper-plugin-utils": "^7.24.7",
"@babel/plugin-syntax-json-strings": "^7.8.3"
},
"engines": {
@@ -1109,11 +1171,12 @@
}
},
"node_modules/@babel/plugin-transform-literals": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.1.tgz",
- "integrity": "sha512-zn9pwz8U7nCqOYIiBaOxoQOtYmMODXTJnkxG4AtX8fPmnCRYWBOHD0qcpwS9e2VDSp1zNJYpdnFMIKb8jmwu6g==",
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz",
+ "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.8"
},
"engines": {
"node": ">=6.9.0"
@@ -1123,11 +1186,12 @@
}
},
"node_modules/@babel/plugin-transform-logical-assignment-operators": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.1.tgz",
- "integrity": "sha512-OhN6J4Bpz+hIBqItTeWJujDOfNP+unqv/NJgyhlpSqgBTPm37KkMmZV6SYcOj+pnDbdcl1qRGV/ZiIjX9Iy34w==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz",
+ "integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0",
+ "@babel/helper-plugin-utils": "^7.24.7",
"@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
},
"engines": {
@@ -1138,11 +1202,12 @@
}
},
"node_modules/@babel/plugin-transform-member-expression-literals": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.1.tgz",
- "integrity": "sha512-4ojai0KysTWXzHseJKa1XPNXKRbuUrhkOPY4rEGeR+7ChlJVKxFa3H3Bz+7tWaGKgJAXUWKOGmltN+u9B3+CVg==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz",
+ "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1152,12 +1217,13 @@
}
},
"node_modules/@babel/plugin-transform-modules-amd": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.1.tgz",
- "integrity": "sha512-lAxNHi4HVtjnHd5Rxg3D5t99Xm6H7b04hUS7EHIXcUl2EV4yl1gWdqZrNzXnSrHveL9qMdbODlLF55mvgjAfaQ==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz",
+ "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-module-transforms": "^7.23.3",
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-module-transforms": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1167,13 +1233,14 @@
}
},
"node_modules/@babel/plugin-transform-modules-commonjs": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz",
- "integrity": "sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==",
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz",
+ "integrity": "sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-module-transforms": "^7.23.3",
- "@babel/helper-plugin-utils": "^7.24.0",
- "@babel/helper-simple-access": "^7.22.5"
+ "@babel/helper-module-transforms": "^7.24.8",
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/helper-simple-access": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1183,14 +1250,15 @@
}
},
"node_modules/@babel/plugin-transform-modules-systemjs": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.1.tgz",
- "integrity": "sha512-mqQ3Zh9vFO1Tpmlt8QPnbwGHzNz3lpNEMxQb1kAemn/erstyqw1r9KeOlOfo3y6xAnFEcOv2tSyrXfmMk+/YZA==",
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz",
+ "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-hoist-variables": "^7.22.5",
- "@babel/helper-module-transforms": "^7.23.3",
- "@babel/helper-plugin-utils": "^7.24.0",
- "@babel/helper-validator-identifier": "^7.22.20"
+ "@babel/helper-module-transforms": "^7.25.0",
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/helper-validator-identifier": "^7.24.7",
+ "@babel/traverse": "^7.25.0"
},
"engines": {
"node": ">=6.9.0"
@@ -1200,12 +1268,13 @@
}
},
"node_modules/@babel/plugin-transform-modules-umd": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.1.tgz",
- "integrity": "sha512-tuA3lpPj+5ITfcCluy6nWonSL7RvaG0AOTeAuvXqEKS34lnLzXpDb0dcP6K8jD0zWZFNDVly90AGFJPnm4fOYg==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz",
+ "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-module-transforms": "^7.23.3",
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-module-transforms": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1215,12 +1284,13 @@
}
},
"node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz",
- "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz",
+ "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.22.5",
- "@babel/helper-plugin-utils": "^7.22.5"
+ "@babel/helper-create-regexp-features-plugin": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1230,11 +1300,12 @@
}
},
"node_modules/@babel/plugin-transform-new-target": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.1.tgz",
- "integrity": "sha512-/rurytBM34hYy0HKZQyA0nHbQgQNFm4Q/BOc9Hflxi2X3twRof7NaE5W46j4kQitm7SvACVRXsa6N/tSZxvPug==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz",
+ "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1244,11 +1315,12 @@
}
},
"node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.1.tgz",
- "integrity": "sha512-iQ+caew8wRrhCikO5DrUYx0mrmdhkaELgFa+7baMcVuhxIkN7oxt06CZ51D65ugIb1UWRQ8oQe+HXAVM6qHFjw==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz",
+ "integrity": "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0",
+ "@babel/helper-plugin-utils": "^7.24.7",
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
},
"engines": {
@@ -1259,11 +1331,12 @@
}
},
"node_modules/@babel/plugin-transform-numeric-separator": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.1.tgz",
- "integrity": "sha512-7GAsGlK4cNL2OExJH1DzmDeKnRv/LXq0eLUSvudrehVA5Rgg4bIrqEUW29FbKMBRT0ztSqisv7kjP+XIC4ZMNw==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz",
+ "integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0",
+ "@babel/helper-plugin-utils": "^7.24.7",
"@babel/plugin-syntax-numeric-separator": "^7.10.4"
},
"engines": {
@@ -1274,11 +1347,12 @@
}
},
"node_modules/@babel/plugin-transform-object-assign": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.24.1.tgz",
- "integrity": "sha512-I1kctor9iKtupb7jv7FyjApHCuKLBKCblVAeHVK9PB6FW7GI0ac6RtobC3MwwJy8CZ1JxuhQmnbrsqI5G8hAIg==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.24.7.tgz",
+ "integrity": "sha512-DOzAi77P9jSyPijHS7Z8vH0wLRcZH6wWxuIZgLAiy8FWOkcKMJmnyHjy2JM94k6A0QxlA/hlLh+R9T3GEryjNQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1288,14 +1362,15 @@
}
},
"node_modules/@babel/plugin-transform-object-rest-spread": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.1.tgz",
- "integrity": "sha512-XjD5f0YqOtebto4HGISLNfiNMTTs6tbkFf2TOqJlYKYmbo+mN9Dnpl4SRoofiziuOWMIyq3sZEUqLo3hLITFEA==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz",
+ "integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-compilation-targets": "^7.23.6",
- "@babel/helper-plugin-utils": "^7.24.0",
+ "@babel/helper-compilation-targets": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7",
"@babel/plugin-syntax-object-rest-spread": "^7.8.3",
- "@babel/plugin-transform-parameters": "^7.24.1"
+ "@babel/plugin-transform-parameters": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1305,12 +1380,13 @@
}
},
"node_modules/@babel/plugin-transform-object-super": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.1.tgz",
- "integrity": "sha512-oKJqR3TeI5hSLRxudMjFQ9re9fBVUU0GICqM3J1mi8MqlhVr6hC/ZN4ttAyMuQR6EZZIY6h/exe5swqGNNIkWQ==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz",
+ "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0",
- "@babel/helper-replace-supers": "^7.24.1"
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/helper-replace-supers": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1320,11 +1396,12 @@
}
},
"node_modules/@babel/plugin-transform-optional-catch-binding": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.1.tgz",
- "integrity": "sha512-oBTH7oURV4Y+3EUrf6cWn1OHio3qG/PVwO5J03iSJmBg6m2EhKjkAu/xuaXaYwWW9miYtvbWv4LNf0AmR43LUA==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz",
+ "integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0",
+ "@babel/helper-plugin-utils": "^7.24.7",
"@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
},
"engines": {
@@ -1335,12 +1412,13 @@
}
},
"node_modules/@babel/plugin-transform-optional-chaining": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.1.tgz",
- "integrity": "sha512-n03wmDt+987qXwAgcBlnUUivrZBPZ8z1plL0YvgQalLm+ZE5BMhGm94jhxXtA1wzv1Cu2aaOv1BM9vbVttrzSg==",
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz",
+ "integrity": "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7",
"@babel/plugin-syntax-optional-chaining": "^7.8.3"
},
"engines": {
@@ -1351,11 +1429,12 @@
}
},
"node_modules/@babel/plugin-transform-parameters": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.1.tgz",
- "integrity": "sha512-8Jl6V24g+Uw5OGPeWNKrKqXPDw2YDjLc53ojwfMcKwlEoETKU9rU0mHUtcg9JntWI/QYzGAXNWEcVHZ+fR+XXg==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz",
+ "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1365,12 +1444,13 @@
}
},
"node_modules/@babel/plugin-transform-private-methods": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.1.tgz",
- "integrity": "sha512-tGvisebwBO5em4PaYNqt4fkw56K2VALsAbAakY0FjTYqJp7gfdrgr7YX76Or8/cpik0W6+tj3rZ0uHU9Oil4tw==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz",
+ "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.24.1",
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-create-class-features-plugin": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1380,13 +1460,14 @@
}
},
"node_modules/@babel/plugin-transform-private-property-in-object": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.1.tgz",
- "integrity": "sha512-pTHxDVa0BpUbvAgX3Gat+7cSciXqUcY9j2VZKTbSB6+VQGpNgNO9ailxTGHSXlqOnX1Hcx1Enme2+yv7VqP9bg==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz",
+ "integrity": "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-annotate-as-pure": "^7.22.5",
- "@babel/helper-create-class-features-plugin": "^7.24.1",
- "@babel/helper-plugin-utils": "^7.24.0",
+ "@babel/helper-annotate-as-pure": "^7.24.7",
+ "@babel/helper-create-class-features-plugin": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7",
"@babel/plugin-syntax-private-property-in-object": "^7.14.5"
},
"engines": {
@@ -1397,11 +1478,12 @@
}
},
"node_modules/@babel/plugin-transform-property-literals": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.1.tgz",
- "integrity": "sha512-LetvD7CrHmEx0G442gOomRr66d7q8HzzGGr4PMHGr+5YIm6++Yke+jxj246rpvsbyhJwCLxcTn6zW1P1BSenqA==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz",
+ "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1411,11 +1493,12 @@
}
},
"node_modules/@babel/plugin-transform-react-display-name": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.1.tgz",
- "integrity": "sha512-mvoQg2f9p2qlpDQRBC7M3c3XTr0k7cp/0+kFKKO/7Gtu0LSw16eKB+Fabe2bDT/UpsyasTBBkAnbdsLrkD5XMw==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz",
+ "integrity": "sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1425,15 +1508,16 @@
}
},
"node_modules/@babel/plugin-transform-react-jsx": {
- "version": "7.23.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz",
- "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==",
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.2.tgz",
+ "integrity": "sha512-KQsqEAVBpU82NM/B/N9j9WOdphom1SZH3R+2V7INrQUH+V9EBFwZsEJl8eBIVeQE62FxJCc70jzEZwqU7RcVqA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-annotate-as-pure": "^7.22.5",
- "@babel/helper-module-imports": "^7.22.15",
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/plugin-syntax-jsx": "^7.23.3",
- "@babel/types": "^7.23.4"
+ "@babel/helper-annotate-as-pure": "^7.24.7",
+ "@babel/helper-module-imports": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/plugin-syntax-jsx": "^7.24.7",
+ "@babel/types": "^7.25.2"
},
"engines": {
"node": ">=6.9.0"
@@ -1443,11 +1527,12 @@
}
},
"node_modules/@babel/plugin-transform-react-jsx-development": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz",
- "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.24.7.tgz",
+ "integrity": "sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/plugin-transform-react-jsx": "^7.22.5"
+ "@babel/plugin-transform-react-jsx": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1457,12 +1542,13 @@
}
},
"node_modules/@babel/plugin-transform-react-pure-annotations": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.1.tgz",
- "integrity": "sha512-+pWEAaDJvSm9aFvJNpLiM2+ktl2Sn2U5DdyiWdZBxmLc6+xGt88dvFqsHiAiDS+8WqUwbDfkKz9jRxK3M0k+kA==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.7.tgz",
+ "integrity": "sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-annotate-as-pure": "^7.22.5",
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-annotate-as-pure": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1472,11 +1558,12 @@
}
},
"node_modules/@babel/plugin-transform-regenerator": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.1.tgz",
- "integrity": "sha512-sJwZBCzIBE4t+5Q4IGLaaun5ExVMRY0lYwos/jNecjMrVCygCdph3IKv0tkP5Fc87e/1+bebAmEAGBfnRD+cnw==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz",
+ "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0",
+ "@babel/helper-plugin-utils": "^7.24.7",
"regenerator-transform": "^0.15.2"
},
"engines": {
@@ -1487,11 +1574,12 @@
}
},
"node_modules/@babel/plugin-transform-reserved-words": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.1.tgz",
- "integrity": "sha512-JAclqStUfIwKN15HrsQADFgeZt+wexNQ0uLhuqvqAUFoqPMjEcFCYZBhq0LUdz6dZK/mD+rErhW71fbx8RYElg==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz",
+ "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1501,11 +1589,12 @@
}
},
"node_modules/@babel/plugin-transform-shorthand-properties": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.1.tgz",
- "integrity": "sha512-LyjVB1nsJ6gTTUKRjRWx9C1s9hE7dLfP/knKdrfeH9UPtAGjYGgxIbFfx7xyLIEWs7Xe1Gnf8EWiUqfjLhInZA==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz",
+ "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1515,12 +1604,13 @@
}
},
"node_modules/@babel/plugin-transform-spread": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.1.tgz",
- "integrity": "sha512-KjmcIM+fxgY+KxPVbjelJC6hrH1CgtPmTvdXAfn3/a9CnWGSTY7nH4zm5+cjmWJybdcPSsD0++QssDsjcpe47g==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz",
+ "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5"
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1530,11 +1620,12 @@
}
},
"node_modules/@babel/plugin-transform-sticky-regex": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.1.tgz",
- "integrity": "sha512-9v0f1bRXgPVcPrngOQvLXeGNNVLc8UjMVfebo9ka0WF3/7+aVUHmaJVT3sa0XCzEFioPfPHZiOcYG9qOsH63cw==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz",
+ "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1544,11 +1635,12 @@
}
},
"node_modules/@babel/plugin-transform-template-literals": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.1.tgz",
- "integrity": "sha512-WRkhROsNzriarqECASCNu/nojeXCDTE/F2HmRgOzi7NGvyfYGq1NEjKBK3ckLfRgGc6/lPAqP0vDOSw3YtG34g==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz",
+ "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1558,11 +1650,12 @@
}
},
"node_modules/@babel/plugin-transform-typeof-symbol": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.1.tgz",
- "integrity": "sha512-CBfU4l/A+KruSUoW+vTQthwcAdwuqbpRNB8HQKlZABwHRhsdHZ9fezp4Sn18PeAlYxTNiLMlx4xUBV3AWfg1BA==",
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz",
+ "integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.8"
},
"engines": {
"node": ">=6.9.0"
@@ -1572,11 +1665,12 @@
}
},
"node_modules/@babel/plugin-transform-unicode-escapes": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.1.tgz",
- "integrity": "sha512-RlkVIcWT4TLI96zM660S877E7beKlQw7Ig+wqkKBiWfj0zH5Q4h50q6er4wzZKRNSYpfo6ILJ+hrJAGSX2qcNw==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz",
+ "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1586,12 +1680,13 @@
}
},
"node_modules/@babel/plugin-transform-unicode-property-regex": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.1.tgz",
- "integrity": "sha512-Ss4VvlfYV5huWApFsF8/Sq0oXnGO+jB+rijFEFugTd3cwSObUSnUi88djgR5528Csl0uKlrI331kRqe56Ov2Ng==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz",
+ "integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.22.15",
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-create-regexp-features-plugin": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1601,12 +1696,13 @@
}
},
"node_modules/@babel/plugin-transform-unicode-regex": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.1.tgz",
- "integrity": "sha512-2A/94wgZgxfTsiLaQ2E36XAOdcZmGAaEEgVmxQWwZXWkGhvoHbaqXcKnU8zny4ycpu3vNqg0L/PcCiYtHtA13g==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz",
+ "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.22.15",
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-create-regexp-features-plugin": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1616,12 +1712,13 @@
}
},
"node_modules/@babel/plugin-transform-unicode-sets-regex": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.1.tgz",
- "integrity": "sha512-fqj4WuzzS+ukpgerpAoOnMfQXwUHFxXUZUE84oL2Kao2N8uSlvcpnAidKASgsNgzZHBsHWvcm8s9FPWUhAb8fA==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz",
+ "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.22.15",
- "@babel/helper-plugin-utils": "^7.24.0"
+ "@babel/helper-create-regexp-features-plugin": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1641,25 +1738,28 @@
}
},
"node_modules/@babel/preset-env": {
- "version": "7.24.3",
- "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.3.tgz",
- "integrity": "sha512-fSk430k5c2ff8536JcPvPWK4tZDwehWLGlBp0wrsBUjZVdeQV6lePbwKWZaZfK2vnh/1kQX1PzAJWsnBmVgGJA==",
+ "version": "7.25.3",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.3.tgz",
+ "integrity": "sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==",
+ "license": "MIT",
"dependencies": {
- "@babel/compat-data": "^7.24.1",
- "@babel/helper-compilation-targets": "^7.23.6",
- "@babel/helper-plugin-utils": "^7.24.0",
- "@babel/helper-validator-option": "^7.23.5",
- "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.1",
- "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.1",
- "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.1",
+ "@babel/compat-data": "^7.25.2",
+ "@babel/helper-compilation-targets": "^7.25.2",
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/helper-validator-option": "^7.24.8",
+ "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3",
+ "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0",
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0",
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7",
+ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0",
"@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
"@babel/plugin-syntax-async-generators": "^7.8.4",
"@babel/plugin-syntax-class-properties": "^7.12.13",
"@babel/plugin-syntax-class-static-block": "^7.14.5",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-syntax-export-namespace-from": "^7.8.3",
- "@babel/plugin-syntax-import-assertions": "^7.24.1",
- "@babel/plugin-syntax-import-attributes": "^7.24.1",
+ "@babel/plugin-syntax-import-assertions": "^7.24.7",
+ "@babel/plugin-syntax-import-attributes": "^7.24.7",
"@babel/plugin-syntax-import-meta": "^7.10.4",
"@babel/plugin-syntax-json-strings": "^7.8.3",
"@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
@@ -1671,59 +1771,60 @@
"@babel/plugin-syntax-private-property-in-object": "^7.14.5",
"@babel/plugin-syntax-top-level-await": "^7.14.5",
"@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
- "@babel/plugin-transform-arrow-functions": "^7.24.1",
- "@babel/plugin-transform-async-generator-functions": "^7.24.3",
- "@babel/plugin-transform-async-to-generator": "^7.24.1",
- "@babel/plugin-transform-block-scoped-functions": "^7.24.1",
- "@babel/plugin-transform-block-scoping": "^7.24.1",
- "@babel/plugin-transform-class-properties": "^7.24.1",
- "@babel/plugin-transform-class-static-block": "^7.24.1",
- "@babel/plugin-transform-classes": "^7.24.1",
- "@babel/plugin-transform-computed-properties": "^7.24.1",
- "@babel/plugin-transform-destructuring": "^7.24.1",
- "@babel/plugin-transform-dotall-regex": "^7.24.1",
- "@babel/plugin-transform-duplicate-keys": "^7.24.1",
- "@babel/plugin-transform-dynamic-import": "^7.24.1",
- "@babel/plugin-transform-exponentiation-operator": "^7.24.1",
- "@babel/plugin-transform-export-namespace-from": "^7.24.1",
- "@babel/plugin-transform-for-of": "^7.24.1",
- "@babel/plugin-transform-function-name": "^7.24.1",
- "@babel/plugin-transform-json-strings": "^7.24.1",
- "@babel/plugin-transform-literals": "^7.24.1",
- "@babel/plugin-transform-logical-assignment-operators": "^7.24.1",
- "@babel/plugin-transform-member-expression-literals": "^7.24.1",
- "@babel/plugin-transform-modules-amd": "^7.24.1",
- "@babel/plugin-transform-modules-commonjs": "^7.24.1",
- "@babel/plugin-transform-modules-systemjs": "^7.24.1",
- "@babel/plugin-transform-modules-umd": "^7.24.1",
- "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5",
- "@babel/plugin-transform-new-target": "^7.24.1",
- "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1",
- "@babel/plugin-transform-numeric-separator": "^7.24.1",
- "@babel/plugin-transform-object-rest-spread": "^7.24.1",
- "@babel/plugin-transform-object-super": "^7.24.1",
- "@babel/plugin-transform-optional-catch-binding": "^7.24.1",
- "@babel/plugin-transform-optional-chaining": "^7.24.1",
- "@babel/plugin-transform-parameters": "^7.24.1",
- "@babel/plugin-transform-private-methods": "^7.24.1",
- "@babel/plugin-transform-private-property-in-object": "^7.24.1",
- "@babel/plugin-transform-property-literals": "^7.24.1",
- "@babel/plugin-transform-regenerator": "^7.24.1",
- "@babel/plugin-transform-reserved-words": "^7.24.1",
- "@babel/plugin-transform-shorthand-properties": "^7.24.1",
- "@babel/plugin-transform-spread": "^7.24.1",
- "@babel/plugin-transform-sticky-regex": "^7.24.1",
- "@babel/plugin-transform-template-literals": "^7.24.1",
- "@babel/plugin-transform-typeof-symbol": "^7.24.1",
- "@babel/plugin-transform-unicode-escapes": "^7.24.1",
- "@babel/plugin-transform-unicode-property-regex": "^7.24.1",
- "@babel/plugin-transform-unicode-regex": "^7.24.1",
- "@babel/plugin-transform-unicode-sets-regex": "^7.24.1",
+ "@babel/plugin-transform-arrow-functions": "^7.24.7",
+ "@babel/plugin-transform-async-generator-functions": "^7.25.0",
+ "@babel/plugin-transform-async-to-generator": "^7.24.7",
+ "@babel/plugin-transform-block-scoped-functions": "^7.24.7",
+ "@babel/plugin-transform-block-scoping": "^7.25.0",
+ "@babel/plugin-transform-class-properties": "^7.24.7",
+ "@babel/plugin-transform-class-static-block": "^7.24.7",
+ "@babel/plugin-transform-classes": "^7.25.0",
+ "@babel/plugin-transform-computed-properties": "^7.24.7",
+ "@babel/plugin-transform-destructuring": "^7.24.8",
+ "@babel/plugin-transform-dotall-regex": "^7.24.7",
+ "@babel/plugin-transform-duplicate-keys": "^7.24.7",
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0",
+ "@babel/plugin-transform-dynamic-import": "^7.24.7",
+ "@babel/plugin-transform-exponentiation-operator": "^7.24.7",
+ "@babel/plugin-transform-export-namespace-from": "^7.24.7",
+ "@babel/plugin-transform-for-of": "^7.24.7",
+ "@babel/plugin-transform-function-name": "^7.25.1",
+ "@babel/plugin-transform-json-strings": "^7.24.7",
+ "@babel/plugin-transform-literals": "^7.25.2",
+ "@babel/plugin-transform-logical-assignment-operators": "^7.24.7",
+ "@babel/plugin-transform-member-expression-literals": "^7.24.7",
+ "@babel/plugin-transform-modules-amd": "^7.24.7",
+ "@babel/plugin-transform-modules-commonjs": "^7.24.8",
+ "@babel/plugin-transform-modules-systemjs": "^7.25.0",
+ "@babel/plugin-transform-modules-umd": "^7.24.7",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7",
+ "@babel/plugin-transform-new-target": "^7.24.7",
+ "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7",
+ "@babel/plugin-transform-numeric-separator": "^7.24.7",
+ "@babel/plugin-transform-object-rest-spread": "^7.24.7",
+ "@babel/plugin-transform-object-super": "^7.24.7",
+ "@babel/plugin-transform-optional-catch-binding": "^7.24.7",
+ "@babel/plugin-transform-optional-chaining": "^7.24.8",
+ "@babel/plugin-transform-parameters": "^7.24.7",
+ "@babel/plugin-transform-private-methods": "^7.24.7",
+ "@babel/plugin-transform-private-property-in-object": "^7.24.7",
+ "@babel/plugin-transform-property-literals": "^7.24.7",
+ "@babel/plugin-transform-regenerator": "^7.24.7",
+ "@babel/plugin-transform-reserved-words": "^7.24.7",
+ "@babel/plugin-transform-shorthand-properties": "^7.24.7",
+ "@babel/plugin-transform-spread": "^7.24.7",
+ "@babel/plugin-transform-sticky-regex": "^7.24.7",
+ "@babel/plugin-transform-template-literals": "^7.24.7",
+ "@babel/plugin-transform-typeof-symbol": "^7.24.8",
+ "@babel/plugin-transform-unicode-escapes": "^7.24.7",
+ "@babel/plugin-transform-unicode-property-regex": "^7.24.7",
+ "@babel/plugin-transform-unicode-regex": "^7.24.7",
+ "@babel/plugin-transform-unicode-sets-regex": "^7.24.7",
"@babel/preset-modules": "0.1.6-no-external-plugins",
"babel-plugin-polyfill-corejs2": "^0.4.10",
"babel-plugin-polyfill-corejs3": "^0.10.4",
"babel-plugin-polyfill-regenerator": "^0.6.1",
- "core-js-compat": "^3.31.0",
+ "core-js-compat": "^3.37.1",
"semver": "^6.3.1"
},
"engines": {
@@ -1747,16 +1848,17 @@
}
},
"node_modules/@babel/preset-react": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz",
- "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==",
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.7.tgz",
+ "integrity": "sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.18.6",
- "@babel/helper-validator-option": "^7.18.6",
- "@babel/plugin-transform-react-display-name": "^7.18.6",
- "@babel/plugin-transform-react-jsx": "^7.18.6",
- "@babel/plugin-transform-react-jsx-development": "^7.18.6",
- "@babel/plugin-transform-react-pure-annotations": "^7.18.6"
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/helper-validator-option": "^7.24.7",
+ "@babel/plugin-transform-react-display-name": "^7.24.7",
+ "@babel/plugin-transform-react-jsx": "^7.24.7",
+ "@babel/plugin-transform-react-jsx-development": "^7.24.7",
+ "@babel/plugin-transform-react-pure-annotations": "^7.24.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1787,31 +1889,30 @@
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
"node_modules/@babel/template": {
- "version": "7.24.0",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz",
- "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==",
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz",
+ "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==",
+ "license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.23.5",
- "@babel/parser": "^7.24.0",
- "@babel/types": "^7.24.0"
+ "@babel/code-frame": "^7.24.7",
+ "@babel/parser": "^7.25.0",
+ "@babel/types": "^7.25.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.24.1",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz",
- "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==",
+ "version": "7.25.3",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz",
+ "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.24.1",
- "@babel/generator": "^7.24.1",
- "@babel/helper-environment-visitor": "^7.22.20",
- "@babel/helper-function-name": "^7.23.0",
- "@babel/helper-hoist-variables": "^7.22.5",
- "@babel/helper-split-export-declaration": "^7.22.6",
- "@babel/parser": "^7.24.1",
- "@babel/types": "^7.24.0",
+ "@babel/code-frame": "^7.24.7",
+ "@babel/generator": "^7.25.0",
+ "@babel/parser": "^7.25.3",
+ "@babel/template": "^7.25.0",
+ "@babel/types": "^7.25.2",
"debug": "^4.3.1",
"globals": "^11.1.0"
},
@@ -1820,12 +1921,13 @@
}
},
"node_modules/@babel/types": {
- "version": "7.24.0",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz",
- "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==",
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz",
+ "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-string-parser": "^7.23.4",
- "@babel/helper-validator-identifier": "^7.22.20",
+ "@babel/helper-string-parser": "^7.24.8",
+ "@babel/helper-validator-identifier": "^7.24.7",
"to-fast-properties": "^2.0.0"
},
"engines": {
@@ -6199,9 +6301,9 @@
"integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow=="
},
"node_modules/browserslist": {
- "version": "4.23.0",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
- "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
+ "version": "4.23.3",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz",
+ "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==",
"funding": [
{
"type": "opencollective",
@@ -6216,11 +6318,12 @@
"url": "https://github.com/sponsors/ai"
}
],
+ "license": "MIT",
"dependencies": {
- "caniuse-lite": "^1.0.30001587",
- "electron-to-chromium": "^1.4.668",
- "node-releases": "^2.0.14",
- "update-browserslist-db": "^1.0.13"
+ "caniuse-lite": "^1.0.30001646",
+ "electron-to-chromium": "^1.5.4",
+ "node-releases": "^2.0.18",
+ "update-browserslist-db": "^1.1.0"
},
"bin": {
"browserslist": "cli.js"
@@ -6499,9 +6602,9 @@
"integrity": "sha512-BS+RAD1DggiDlE2KaBUWKsMDuVmmh3hCM5LI0OW25mGlPttGLeOjDUa1DmZvJVFCXvtshY4BTyFgv31eFTLg8g=="
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001600",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz",
- "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==",
+ "version": "1.0.30001651",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz",
+ "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==",
"funding": [
{
"type": "opencollective",
@@ -6515,7 +6618,8 @@
"type": "github",
"url": "https://github.com/sponsors/ai"
}
- ]
+ ],
+ "license": "CC-BY-4.0"
},
"node_modules/capture-exit": {
"version": "2.0.0",
@@ -7288,7 +7392,8 @@
"node_modules/convert-source-map": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
- "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+ "dev": true
},
"node_modules/cookie": {
"version": "0.3.1",
@@ -7324,11 +7429,12 @@
"hasInstallScript": true
},
"node_modules/core-js-compat": {
- "version": "3.36.1",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz",
- "integrity": "sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA==",
+ "version": "3.38.0",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.0.tgz",
+ "integrity": "sha512-75LAicdLa4OJVwFxFbQR3NdnZjNgX6ILpVcVzcC4T2smerB5lELMrJQQQoWV6TiuC/vlaFqgU2tKQx9w5s0e0A==",
+ "license": "MIT",
"dependencies": {
- "browserslist": "^4.23.0"
+ "browserslist": "^4.23.3"
},
"funding": {
"type": "opencollective",
@@ -8204,9 +8310,10 @@
"dev": true
},
"node_modules/electron-to-chromium": {
- "version": "1.4.717",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.717.tgz",
- "integrity": "sha512-6Fmg8QkkumNOwuZ/5mIbMU9WI3H2fmn5ajcVya64I5Yr5CcNmO7vcLt0Y7c96DCiMO5/9G+4sI2r6eEvdg1F7A=="
+ "version": "1.5.6",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.6.tgz",
+ "integrity": "sha512-jwXWsM5RPf6j9dPYzaorcBSUg6AiqocPEyMpkchkvntaH9HGfOOMZwxMJjDY/XEs3T5dM7uyH1VhRMkqUU9qVw==",
+ "license": "ISC"
},
"node_modules/email-prop-type": {
"version": "1.1.7",
@@ -8861,6 +8968,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
@@ -17146,9 +17254,10 @@
"optional": true
},
"node_modules/node-releases": {
- "version": "2.0.14",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
- "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw=="
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
+ "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
+ "license": "MIT"
},
"node_modules/nopt": {
"version": "3.0.6",
@@ -17949,9 +18058,10 @@
"dev": true
},
"node_modules/picocolors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
+ "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
+ "license": "ISC"
},
"node_modules/picomatch": {
"version": "2.3.1",
@@ -24630,9 +24740,9 @@
}
},
"node_modules/update-browserslist-db": {
- "version": "1.0.13",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
- "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
+ "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==",
"funding": [
{
"type": "opencollective",
@@ -24647,9 +24757,10 @@
"url": "https://github.com/sponsors/ai"
}
],
+ "license": "MIT",
"dependencies": {
- "escalade": "^3.1.1",
- "picocolors": "^1.0.0"
+ "escalade": "^3.1.2",
+ "picocolors": "^1.0.1"
},
"bin": {
"update-browserslist-db": "cli.js"
diff --git a/package.json b/package.json
index 1ae38f189b..b2dc87c3a9 100644
--- a/package.json
+++ b/package.json
@@ -15,11 +15,11 @@
"watch-sass": "scripts/watch_sass.sh"
},
"dependencies": {
- "@babel/core": "7.19.0",
+ "@babel/core": "7.25.2",
"@babel/plugin-proposal-object-rest-spread": "^7.18.9",
"@babel/plugin-transform-object-assign": "^7.18.6",
"@babel/preset-env": "^7.19.0",
- "@babel/preset-react": "7.18.6",
+ "@babel/preset-react": "7.24.7",
"@edx/brand-edx.org": "^2.0.7",
"@edx/edx-bootstrap": "1.0.4",
"@edx/edx-proctoring": "^4.18.1",
From 5bd85597af92caf1ea7e6756c153700d93fd31db Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 12 Aug 2024 10:05:03 +0000
Subject: [PATCH 027/242] fix(deps): update dependency backbone to v1.6.0
---
package-lock.json | 18 +++++-------------
package.json | 2 +-
2 files changed, 6 insertions(+), 14 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index ac2919ce21..99094c25c9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,7 +23,7 @@
"babel-loader": "^9.1.3",
"babel-plugin-transform-class-properties": "6.24.1",
"babel-polyfill": "6.26.0",
- "backbone": "1.4.1",
+ "backbone": "1.6.0",
"backbone-associations": "0.6.2",
"backbone.paginator": "2.0.8",
"bootstrap": "4.0.0",
@@ -6058,9 +6058,10 @@
}
},
"node_modules/backbone": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.4.1.tgz",
- "integrity": "sha512-ADy1ztN074YkWbHi8ojJVFe3vAanO/lrzMGZWUClIP7oDD/Pjy2vrASraUP+2EVCfIiTtCW4FChVow01XneivA==",
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.6.0.tgz",
+ "integrity": "sha512-13PUjmsgw/49EowNcQvfG4gmczz1ximTMhUktj0Jfrjth0MVaTxehpU+qYYX4MxnuIuhmvBLC6/ayxuAGnOhbA==",
+ "license": "MIT",
"dependencies": {
"underscore": ">=1.8.3"
}
@@ -8236,15 +8237,6 @@
"urijs": "1.19.11"
}
},
- "node_modules/edx-ui-toolkit/node_modules/backbone": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.6.0.tgz",
- "integrity": "sha512-13PUjmsgw/49EowNcQvfG4gmczz1ximTMhUktj0Jfrjth0MVaTxehpU+qYYX4MxnuIuhmvBLC6/ayxuAGnOhbA==",
- "license": "MIT",
- "dependencies": {
- "underscore": ">=1.8.3"
- }
- },
"node_modules/edx-ui-toolkit/node_modules/formatio": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz",
diff --git a/package.json b/package.json
index b2dc87c3a9..06754da350 100644
--- a/package.json
+++ b/package.json
@@ -29,7 +29,7 @@
"babel-loader": "^9.1.3",
"babel-plugin-transform-class-properties": "6.24.1",
"babel-polyfill": "6.26.0",
- "backbone": "1.4.1",
+ "backbone": "1.6.0",
"backbone-associations": "0.6.2",
"backbone.paginator": "2.0.8",
"bootstrap": "4.0.0",
From 2ea6c25e44280aeaf7aaf7f60d4e142b0305c756 Mon Sep 17 00:00:00 2001
From: hamzawaleed01
Date: Mon, 5 Aug 2024 15:41:48 +0500
Subject: [PATCH 028/242] feat: add PA role
---
lms/envs/common.py | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 9f9004976e..c3bd1bd236 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -56,7 +56,10 @@ from enterprise.constants import (
ENTERPRISE_FULFILLMENT_OPERATOR_ROLE,
ENTERPRISE_REPORTING_CONFIG_ADMIN_ROLE,
ENTERPRISE_SSO_ORCHESTRATOR_OPERATOR_ROLE,
- ENTERPRISE_OPERATOR_ROLE
+ ENTERPRISE_OPERATOR_ROLE,
+ SYSTEM_ENTERPRISE_PROVISIONING_ADMIN_ROLE,
+ PROVISIONING_ENTERPRISE_CUSTOMER_ADMIN_ROLE,
+ PROVISIONING_PENDING_ENTERPRISE_CUSTOMER_ADMIN_USER_ROLE,
)
from openedx.core.constants import COURSE_KEY_REGEX, COURSE_KEY_PATTERN, COURSE_ID_PATTERN
@@ -3350,6 +3353,9 @@ INSTALLED_APPS = [
# Management of external user ids
'openedx.core.djangoapps.external_user_ids',
+ # Provides api for Demographics support
+ 'openedx.core.djangoapps.demographics',
+
# Management of per-user schedules
'openedx.core.djangoapps.schedules',
@@ -4292,10 +4298,6 @@ SOCIAL_PLATFORMS = {
}
}
-# Enable First Purchase Discount offer override
-FIRST_PURCHASE_DISCOUNT_OVERRIDE_CODE = ''
-FIRST_PURCHASE_DISCOUNT_OVERRIDE_PERCENTAGE = 15
-
# E-Commerce API Configuration
ECOMMERCE_PUBLIC_URL_ROOT = 'http://localhost:8002'
ECOMMERCE_API_URL = 'http://localhost:8002/api/v2'
@@ -4683,8 +4685,7 @@ ENTERPRISE_ALL_SERVICE_USERNAMES = [
]
# Setting for Open API key and prompts used by edx-enterprise.
-CHAT_COMPLETION_API = 'https://example.com/chat/completion'
-CHAT_COMPLETION_API_KEY = 'i am a key'
+OPENAI_API_KEY = ''
LEARNER_ENGAGEMENT_PROMPT_FOR_ACTIVE_CONTRACT = ''
LEARNER_ENGAGEMENT_PROMPT_FOR_NON_ACTIVE_CONTRACT = ''
LEARNER_PROGRESS_PROMPT_FOR_ACTIVE_CONTRACT = ''
@@ -4740,6 +4741,10 @@ SYSTEM_TO_FEATURE_ROLE_MAPPING = {
ENTERPRISE_FULFILLMENT_OPERATOR_ROLE,
ENTERPRISE_SSO_ORCHESTRATOR_OPERATOR_ROLE,
],
+ SYSTEM_ENTERPRISE_PROVISIONING_ADMIN_ROLE: [
+ PROVISIONING_ENTERPRISE_CUSTOMER_ADMIN_ROLE,
+ PROVISIONING_PENDING_ENTERPRISE_CUSTOMER_ADMIN_USER_ROLE,
+ ],
}
DATA_CONSENT_SHARE_CACHE_TIMEOUT = 8 * 60 * 60 # 8 hours
From 74af0e71e840af07f0085bb510dec6b4ef4616a1 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 12 Aug 2024 11:40:47 +0000
Subject: [PATCH 029/242] chore(deps): update jest monorepo to v26.6.3
---
package-lock.json | 58 ++++++++++++++++++-----------------------------
package.json | 4 ++--
2 files changed, 24 insertions(+), 38 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 99094c25c9..562f74c989 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -79,13 +79,13 @@
"@edx/eslint-config": "^3.1.1",
"@edx/mockprock": "github:openedx/mockprock#3ad18c6888e6521e9bf7a4df0db6f8579b928235",
"@edx/stylelint-config-edx": "2.3.3",
- "babel-jest": "26.0.0",
+ "babel-jest": "26.6.3",
"enzyme": "3.11.0",
"enzyme-adapter-react-16": "1.15.8",
"eslint-import-resolver-webpack": "0.13.8",
"jasmine-core": "2.6.4",
"jasmine-jquery": "git+https://git@github.com/velesin/jasmine-jquery.git#ebad463d592d3fea00c69f26ea18a930e09c7b58",
- "jest": "26.0.0",
+ "jest": "26.6.3",
"jest-enzyme": "6.1.2",
"karma": "0.13.22",
"karma-chrome-launcher": "0.2.3",
@@ -5541,16 +5541,17 @@
"integrity": "sha512-m2CvfDW4+1qfDdsrtf4dwOslQC3yhbgyBFptncp4wvtdrDHqueW7slsYv4gArie056phvQFhT2nRcGS4bnm6mA=="
},
"node_modules/babel-jest": {
- "version": "26.0.0",
- "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.0.0.tgz",
- "integrity": "sha512-2AtcYOP4xhFn6TkvGmbEArNBcYLm/cTOIXB1a5j2juPOIC2U0nHEouMqYzgnPXgWC+CBK5RmYoGnwRt6eV4E8A==",
+ "version": "26.6.3",
+ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz",
+ "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@jest/transform": "^26.0.0",
- "@jest/types": "^26.0.0",
+ "@jest/transform": "^26.6.2",
+ "@jest/types": "^26.6.2",
"@types/babel__core": "^7.1.7",
"babel-plugin-istanbul": "^6.0.0",
- "babel-preset-jest": "^26.0.0",
+ "babel-preset-jest": "^26.6.2",
"chalk": "^4.0.0",
"graceful-fs": "^4.2.4",
"slash": "^3.0.0"
@@ -5567,6 +5568,7 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -5582,6 +5584,7 @@
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -5598,6 +5601,7 @@
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -5609,13 +5613,15 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/babel-jest/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
@@ -5625,6 +5631,7 @@
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -12846,14 +12853,15 @@
"license": "MIT"
},
"node_modules/jest": {
- "version": "26.0.0",
- "resolved": "https://registry.npmjs.org/jest/-/jest-26.0.0.tgz",
- "integrity": "sha512-OtoG+cpcP+UXx+pQ7rzoQ11Pfb5+OUkrsNn5YPc0GU2HeBktgTANonUZEgT6cCgUHX7jUiuDIusDNTL4iNcWGQ==",
+ "version": "26.6.3",
+ "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz",
+ "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@jest/core": "^26.0.0",
+ "@jest/core": "^26.6.3",
"import-local": "^3.0.2",
- "jest-cli": "^26.0.0"
+ "jest-cli": "^26.6.3"
},
"bin": {
"jest": "bin/jest.js"
@@ -13025,28 +13033,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/jest-config/node_modules/babel-jest": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz",
- "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==",
- "dev": true,
- "dependencies": {
- "@jest/transform": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/babel__core": "^7.1.7",
- "babel-plugin-istanbul": "^6.0.0",
- "babel-preset-jest": "^26.6.2",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.4",
- "slash": "^3.0.0"
- },
- "engines": {
- "node": ">= 10.14.2"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
"node_modules/jest-config/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
diff --git a/package.json b/package.json
index 06754da350..5b8de12976 100644
--- a/package.json
+++ b/package.json
@@ -85,13 +85,13 @@
"@edx/eslint-config": "^3.1.1",
"@edx/mockprock": "github:openedx/mockprock#3ad18c6888e6521e9bf7a4df0db6f8579b928235",
"@edx/stylelint-config-edx": "2.3.3",
- "babel-jest": "26.0.0",
+ "babel-jest": "26.6.3",
"enzyme": "3.11.0",
"enzyme-adapter-react-16": "1.15.8",
"eslint-import-resolver-webpack": "0.13.8",
"jasmine-core": "2.6.4",
"jasmine-jquery": "git+https://git@github.com/velesin/jasmine-jquery.git#ebad463d592d3fea00c69f26ea18a930e09c7b58",
- "jest": "26.0.0",
+ "jest": "26.6.3",
"jest-enzyme": "6.1.2",
"karma": "0.13.22",
"karma-chrome-launcher": "0.2.3",
From 04e507635c798fcb62dbe7597e11ded3763634b7 Mon Sep 17 00:00:00 2001
From: MueezKhan246 <93375917+MueezKhan246@users.noreply.github.com>
Date: Mon, 12 Aug 2024 12:46:28 +0000
Subject: [PATCH 030/242] feat: Upgrade Python dependency edx-enterprise
added logs for data dog inspection for permission check in handler function.
Commit generated by workflow `openedx/edx-platform/.github/workflows/upgrade-one-python-dependency.yml@refs/heads/master`
---
requirements/common_constraints.txt | 9 ---------
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 6 ++----
requirements/edx/development.txt | 3 +--
requirements/edx/doc.txt | 6 ++----
requirements/edx/testing.txt | 6 ++----
6 files changed, 8 insertions(+), 24 deletions(-)
diff --git a/requirements/common_constraints.txt b/requirements/common_constraints.txt
index 9405a605c5..f2ef0216ca 100644
--- a/requirements/common_constraints.txt
+++ b/requirements/common_constraints.txt
@@ -26,15 +26,6 @@ elasticsearch<7.14.0
# django-simple-history>3.0.0 adds indexing and causes a lot of migrations to be affected
-# opentelemetry requires version 6.x at the moment:
-# https://github.com/open-telemetry/opentelemetry-python/issues/3570
-# Normally this could be added as a constraint in edx-django-utils, where we're
-# adding the opentelemetry dependency. However, when we compile pip-tools.txt,
-# that uses version 7.x, and then there's no undoing that when compiling base.txt.
-# So we need to pin it globally, for now.
-# Ticket for unpinning: https://github.com/openedx/edx-lint/issues/407
-importlib-metadata<7
-
# Cause: https://github.com/openedx/event-tracking/pull/290
# event-tracking 2.4.1 upgrades to pymongo 4.4.0 which is not supported on edx-platform.
# We will pin event-tracking to do not break existing installations
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 02cd4b01ab..378a4ca2b2 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -20,7 +20,7 @@ celery>=5.2.2,<6.0.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==4.23.3
+edx-enterprise==4.23.4
# Stay on LTS version, remove once this is added to common constraint
Django<5.0
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 66923e2501..65b1d6e20c 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -465,7 +465,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.3
+edx-enterprise==4.23.4
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
@@ -647,9 +647,7 @@ idna==3.7
# snowflake-connector-python
# yarl
importlib-metadata==6.11.0
- # via
- # -c requirements/edx/../common_constraints.txt
- # -r requirements/edx/kernel.in
+ # via -r requirements/edx/kernel.in
inflection==0.5.1
# via
# drf-spectacular
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index 7adede611a..d27eae3311 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -740,7 +740,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.3
+edx-enterprise==4.23.4
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
@@ -1065,7 +1065,6 @@ import-linter==2.0
# via -r requirements/edx/testing.txt
importlib-metadata==6.11.0
# via
- # -c requirements/edx/../common_constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
inflection==0.5.1
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index d61a022ecf..e7ffc24394 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -545,7 +545,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.3
+edx-enterprise==4.23.4
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
@@ -767,9 +767,7 @@ idna==3.7
imagesize==1.4.1
# via sphinx
importlib-metadata==6.11.0
- # via
- # -c requirements/edx/../common_constraints.txt
- # -r requirements/edx/base.txt
+ # via -r requirements/edx/base.txt
inflection==0.5.1
# via
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index b95e1f9f0a..5d881f6933 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -569,7 +569,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.3
+edx-enterprise==4.23.4
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
@@ -808,9 +808,7 @@ idna==3.7
import-linter==2.0
# via -r requirements/edx/testing.in
importlib-metadata==6.11.0
- # via
- # -c requirements/edx/../common_constraints.txt
- # -r requirements/edx/base.txt
+ # via -r requirements/edx/base.txt
inflection==0.5.1
# via
# -r requirements/edx/base.txt
From 200ee1a804f8fc46e6f91f5c91aa39e36905667c Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 12 Aug 2024 12:54:39 +0000
Subject: [PATCH 031/242] chore(deps): update dependency selenium-webdriver to
v3.6.0
---
package-lock.json | 99 ++++++++++++++++++++++++++++++++++++++++-------
package.json | 2 +-
2 files changed, 86 insertions(+), 15 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 562f74c989..c2a83bf7a6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -101,7 +101,7 @@
"karma-webpack": "^5.0.1",
"plato": "1.7.0",
"react-test-renderer": "16.14.0",
- "selenium-webdriver": "3.4.0",
+ "selenium-webdriver": "3.6.0",
"sinon": "2.4.1",
"squirejs": "0.1.0",
"string-replace-loader": "^3.1.0",
@@ -4654,15 +4654,6 @@
"node": ">=0.4.0"
}
},
- "node_modules/adm-zip": {
- "version": "0.4.16",
- "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
- "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
- "dev": true,
- "engines": {
- "node": ">=0.3.0"
- }
- },
"node_modules/after": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
@@ -11526,6 +11517,13 @@
"node": ">= 4"
}
},
+ "node_modules/immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/immutable": {
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz",
@@ -15338,6 +15336,59 @@
"node": ">=4.0"
}
},
+ "node_modules/jszip": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
+ "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+ "dev": true,
+ "license": "(MIT OR GPL-3.0-or-later)",
+ "dependencies": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/jszip/node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/jszip/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/jszip/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/jszip/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
"node_modules/karma": {
"version": "0.13.22",
"resolved": "https://registry.npmjs.org/karma/-/karma-0.13.22.tgz",
@@ -15879,6 +15930,16 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "immediate": "~3.0.5"
+ }
+ },
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@@ -17803,6 +17864,13 @@
"node": ">=6"
}
},
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "dev": true,
+ "license": "(MIT AND Zlib)"
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -22073,12 +22141,13 @@
"integrity": "sha512-qGVDoreyYiP1pkQnbnFAUIS5AjenNwwQBdl7zeos9etl+hYKWahjRTfzAZZYBv5xNHx7vNKCmaLDQZ6Fr2AEXg=="
},
"node_modules/selenium-webdriver": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.4.0.tgz",
- "integrity": "sha512-Bfq9FX33fFSj0F26BaaZllQBZl3d9FykVaH4CEJfwtN43+V8jrTpuhfiR5Zm9nkXjMbcEQz2EKWAjNzV8Hkqbw==",
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz",
+ "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==",
"dev": true,
+ "license": "Apache-2.0",
"dependencies": {
- "adm-zip": "^0.4.7",
+ "jszip": "^3.1.3",
"rimraf": "^2.5.4",
"tmp": "0.0.30",
"xml2js": "^0.4.17"
@@ -22091,7 +22160,9 @@
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
+ "license": "ISC",
"dependencies": {
"glob": "^7.1.3"
},
diff --git a/package.json b/package.json
index 5b8de12976..3bd526c3b4 100644
--- a/package.json
+++ b/package.json
@@ -107,7 +107,7 @@
"karma-webpack": "^5.0.1",
"plato": "1.7.0",
"react-test-renderer": "16.14.0",
- "selenium-webdriver": "3.4.0",
+ "selenium-webdriver": "3.6.0",
"sinon": "2.4.1",
"squirejs": "0.1.0",
"string-replace-loader": "^3.1.0",
From 30f1c754b4ab23e9a9f85a5e31d5761f68458c59 Mon Sep 17 00:00:00 2001
From: hamzawaleed01 <83753341+hamzawaleed01@users.noreply.github.com>
Date: Tue, 13 Aug 2024 15:15:01 +0000
Subject: [PATCH 032/242] feat: Upgrade Python dependency edx-enterprise
feat: added migration for model updateroleassignmentswithcustomersconfig
Commit generated by workflow `openedx/edx-platform/.github/workflows/upgrade-one-python-dependency.yml@refs/heads/master`
---
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 378a4ca2b2..ac4f4ed6c7 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -20,7 +20,7 @@ celery>=5.2.2,<6.0.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==4.23.4
+edx-enterprise==4.23.7
# Stay on LTS version, remove once this is added to common constraint
Django<5.0
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 65b1d6e20c..c0e0600ae1 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -465,7 +465,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.4
+edx-enterprise==4.23.7
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index d27eae3311..d77e85773b 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -740,7 +740,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.4
+edx-enterprise==4.23.7
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index e7ffc24394..475039444b 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -545,7 +545,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.4
+edx-enterprise==4.23.7
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 5d881f6933..6adb94b2a4 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -569,7 +569,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.4
+edx-enterprise==4.23.7
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
From 018ba9d42d2f042091b55bcd6ac23c277d615b8a Mon Sep 17 00:00:00 2001
From: Prashant Makwana
Date: Tue, 13 Aug 2024 12:52:04 -0400
Subject: [PATCH 033/242] chore: bumping version num to 4.23.8 (#35310)
---
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index ac4f4ed6c7..0a95e5c8a4 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -20,7 +20,7 @@ celery>=5.2.2,<6.0.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==4.23.7
+edx-enterprise==4.23.8
# Stay on LTS version, remove once this is added to common constraint
Django<5.0
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index c0e0600ae1..6a15216943 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -465,7 +465,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.7
+edx-enterprise==4.23.8
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index d77e85773b..8155169e33 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -740,7 +740,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.7
+edx-enterprise==4.23.8
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 475039444b..bb81b7b71a 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -545,7 +545,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.7
+edx-enterprise==4.23.8
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 6adb94b2a4..18c988dfb6 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -569,7 +569,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.7
+edx-enterprise==4.23.8
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
From c24c87499f75893fbb6b35b2573f361c84a142ab Mon Sep 17 00:00:00 2001
From: Jade Olivier
Date: Tue, 30 Jul 2024 11:03:03 +0200
Subject: [PATCH 034/242] feat: Commerce Coordinator step in retirement
pipeline
---
.../tests/utils/test_edx_api.py | 39 +++++++++++++++++++
scripts/user_retirement/utils/edx_api.py | 14 +++++++
scripts/user_retirement/utils/helpers.py | 13 ++++++-
3 files changed, 65 insertions(+), 1 deletion(-)
diff --git a/scripts/user_retirement/tests/utils/test_edx_api.py b/scripts/user_retirement/tests/utils/test_edx_api.py
index eb826cd1a4..037ea68628 100644
--- a/scripts/user_retirement/tests/utils/test_edx_api.py
+++ b/scripts/user_retirement/tests/utils/test_edx_api.py
@@ -542,3 +542,42 @@ class TestLicenseManagerApi(OAuth2Mixin, unittest.TestCase):
original_username=FAKE_ORIGINAL_USERNAME
)
)
+
+
+class TestCommerceCoordinatorApi(OAuth2Mixin, unittest.TestCase):
+ """
+ Test the edX Commerce-Coordinator API client.
+ """
+
+ @responses.activate(registry=OrderedRegistry)
+ def setUp(self):
+ super().setUp()
+ self.mock_access_token_response()
+ self.lms_base_url = 'http://localhost:18000/'
+ self.commerce_coordinator_base_url = 'http://localhost:8140/'
+ self.commerce_coordinator_api = edx_api.CommerceCoordinatorApi(
+ self.lms_base_url,
+ self.commerce_coordinator_base_url,
+ 'the_client_id',
+ 'the_client_secret'
+ )
+
+ @patch.object(edx_api.CommerceCoordinatorApi, '_request')
+ def test_retire_learner(self, mock_request):
+ learner_data = get_fake_user_retirement()
+ json_data = {
+ 'edx_lms_user_id': learner_data['user']['id']
+ }
+ responses.add(
+ POST,
+ urljoin(self.commerce_coordinator_base_url, 'lms/user_retirement'),
+ match=[matchers.json_params_matcher(json_data)]
+ )
+
+ self.commerce_coordinator_api.retire_learner(learner=learner_data)
+
+ mock_request.assert_called_once_with(
+ 'POST',
+ urljoin(self.commerce_coordinator_base_url, 'lms/user_retirement/'),
+ json=json_data
+ )
diff --git a/scripts/user_retirement/utils/edx_api.py b/scripts/user_retirement/utils/edx_api.py
index e891f04019..f77675fcbc 100644
--- a/scripts/user_retirement/utils/edx_api.py
+++ b/scripts/user_retirement/utils/edx_api.py
@@ -490,3 +490,17 @@ class LicenseManagerApi(BaseApiClient):
except HttpDoesNotExistException:
LOG.info("No license manager data found for user")
return True
+
+class CommerceCoordinatorApi(BaseApiClient):
+ """
+ Commerce-Coordinator API client.
+ """
+ @_retry_lms_api()
+ def retire_learner(self, learner):
+ """
+ Performs the learner retirement step for Commerce-Coordinator.
+ Passes the learner's LMS User Id instead of username.
+ """
+ data = {'edx_lms_user_id': learner['user']['id']}
+ api_url = self.get_api_url('lms/user_retirement')
+ return self._request('POST', api_url, json=data)
diff --git a/scripts/user_retirement/utils/helpers.py b/scripts/user_retirement/utils/helpers.py
index 1bcbadb4b3..42723b0999 100644
--- a/scripts/user_retirement/utils/helpers.py
+++ b/scripts/user_retirement/utils/helpers.py
@@ -18,7 +18,8 @@ import yaml
from six import text_type
from scripts.user_retirement.utils.edx_api import LmsApi # pylint: disable=wrong-import-position
-from scripts.user_retirement.utils.edx_api import CredentialsApi, EcommerceApi, LicenseManagerApi
+from scripts.user_retirement.utils.edx_api import CommerceCoordinatorApi, CredentialsApi, EcommerceApi, \
+ LicenseManagerApi
from scripts.user_retirement.utils.thirdparty_apis.amplitude_api import \
AmplitudeApi # pylint: disable=wrong-import-position
from scripts.user_retirement.utils.thirdparty_apis.braze_api import BrazeApi # pylint: disable=wrong-import-position
@@ -154,6 +155,7 @@ def _setup_all_apis_or_exit(fail_func, fail_code, config):
credentials_base_url = config['base_urls'].get('credentials', None)
segment_base_url = config['base_urls'].get('segment', None)
license_manager_base_url = config['base_urls'].get('license_manager', None)
+ commerce_coordinator_base_url = config['base_urls'].get('commerce_coordinator', None)
client_id = config['client_id']
client_secret = config['client_secret']
braze_api_key = config.get('braze_api_key', None)
@@ -180,6 +182,7 @@ def _setup_all_apis_or_exit(fail_func, fail_code, config):
('CREDENTIALS', credentials_base_url),
('SEGMENT', segment_base_url),
('HUBSPOT', hubspot_api_key),
+ ('COMMERCE_COORDINATOR', commerce_coordinator_base_url),
):
if state[2] == service and service_url is None:
fail_func(fail_code, 'Service URL is not configured, but required for state {}'.format(state))
@@ -235,5 +238,13 @@ def _setup_all_apis_or_exit(fail_func, fail_code, config):
segment_auth_token,
segment_workspace_slug
)
+
+ if commerce_coordinator_base_url:
+ config['COMMERCE_COORDINATOR'] = CommerceCoordinatorApi(
+ lms_base_url,
+ commerce_coordinator_base_url,
+ client_id,
+ client_secret,
+ )
except Exception as exc: # pylint: disable=broad-except
fail_func(fail_code, 'Unexpected error occurred!', exc)
From 77ca0f754af322b918434960f74948665d0847e1 Mon Sep 17 00:00:00 2001
From: Jade Olivier
Date: Tue, 30 Jul 2024 11:11:42 +0200
Subject: [PATCH 035/242] fix: quality
---
scripts/user_retirement/utils/edx_api.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/scripts/user_retirement/utils/edx_api.py b/scripts/user_retirement/utils/edx_api.py
index f77675fcbc..d259e9d2eb 100644
--- a/scripts/user_retirement/utils/edx_api.py
+++ b/scripts/user_retirement/utils/edx_api.py
@@ -491,6 +491,7 @@ class LicenseManagerApi(BaseApiClient):
LOG.info("No license manager data found for user")
return True
+
class CommerceCoordinatorApi(BaseApiClient):
"""
Commerce-Coordinator API client.
From e8f9db428d6bd41bd17b3ae4bc4ff18fa2fff152 Mon Sep 17 00:00:00 2001
From: Jade Olivier
Date: Tue, 6 Aug 2024 11:18:52 +0200
Subject: [PATCH 036/242] fix: generalize internal services
---
lms/envs/common.py | 2 ++
lms/envs/test.py | 2 ++
.../tests/test_retire_one_learner.py | 9 +++++
.../tests/utils/test_edx_api.py | 22 +++++++------
scripts/user_retirement/utils/edx_api.py | 13 +++++---
scripts/user_retirement/utils/helpers.py | 33 +++++++++++--------
6 files changed, 52 insertions(+), 29 deletions(-)
diff --git a/lms/envs/common.py b/lms/envs/common.py
index d0d2eeae76..1057c798f6 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -4996,6 +4996,8 @@ RETIREMENT_STATES = [
'COMPLETE',
]
+EXTRA_SERVICES_TO_RETIRE_FROM = {}
+
USERNAME_REPLACEMENT_WORKER = "REPLACE WITH VALID USERNAME"
############## Settings for Microfrontends #########################
diff --git a/lms/envs/test.py b/lms/envs/test.py
index 3c4bb95649..13799a3c40 100644
--- a/lms/envs/test.py
+++ b/lms/envs/test.py
@@ -662,6 +662,8 @@ SUBSCRIPTIONS_TRIAL_LENGTH = 7
CSRF_TRUSTED_ORIGINS = ['.example.com']
CSRF_TRUSTED_ORIGINS_WITH_SCHEME = ['https://*.example.com']
+EXTRA_SERVICES_TO_RETIRE_FROM = {}
+
# values are already updated above with default CSRF_TRUSTED_ORIGINS values but in
# case of new django version these values will override.
if django.VERSION[0] >= 4: # for greater than django 3.2 use with schemes.
diff --git a/scripts/user_retirement/tests/test_retire_one_learner.py b/scripts/user_retirement/tests/test_retire_one_learner.py
index 844325ea3e..e620d850ee 100644
--- a/scripts/user_retirement/tests/test_retire_one_learner.py
+++ b/scripts/user_retirement/tests/test_retire_one_learner.py
@@ -4,6 +4,8 @@ Test the retire_one_learner.py script
from unittest.mock import DEFAULT, patch
from click.testing import CliRunner
+from django.conf import settings
+from django.test.utils import override_settings
from scripts.user_retirement.retire_one_learner import (
END_STATES,
@@ -19,6 +21,13 @@ from scripts.user_retirement.tests.retirement_helpers import fake_config_file, g
from scripts.user_retirement.utils.exception import HttpDoesNotExistException
+@override_settings(EXTRA_SERVICES_TO_RETIRE_FROM=[
+ {
+ 'name': 'MOCK_SERVICE',
+ 'service_base_url': 'http://fake_service_base_url',
+ 'retirement_url_path': 'fake_retirement_url_path'
+ }
+])
def _call_script(username, fetch_ecom_segment_id=False):
"""
Call the retired learner script with the given username and a generic, temporary config file.
diff --git a/scripts/user_retirement/tests/utils/test_edx_api.py b/scripts/user_retirement/tests/utils/test_edx_api.py
index 037ea68628..2c86c2cb97 100644
--- a/scripts/user_retirement/tests/utils/test_edx_api.py
+++ b/scripts/user_retirement/tests/utils/test_edx_api.py
@@ -544,9 +544,9 @@ class TestLicenseManagerApi(OAuth2Mixin, unittest.TestCase):
)
-class TestCommerceCoordinatorApi(OAuth2Mixin, unittest.TestCase):
+class TestGenericApi(OAuth2Mixin, unittest.TestCase):
"""
- Test the edX Commerce-Coordinator API client.
+ Test the Generic API client.
"""
@responses.activate(registry=OrderedRegistry)
@@ -554,15 +554,17 @@ class TestCommerceCoordinatorApi(OAuth2Mixin, unittest.TestCase):
super().setUp()
self.mock_access_token_response()
self.lms_base_url = 'http://localhost:18000/'
- self.commerce_coordinator_base_url = 'http://localhost:8140/'
- self.commerce_coordinator_api = edx_api.CommerceCoordinatorApi(
+ self.service_api_base_url = 'http://mock_service_url/'
+ self.retirement_url = 'mock/retirement_url'
+ self.generic_api = edx_api.GenericRetirementApi(
self.lms_base_url,
- self.commerce_coordinator_base_url,
+ self.service_api_base_url,
'the_client_id',
- 'the_client_secret'
+ 'the_client_secret',
+ self.retirement_url
)
- @patch.object(edx_api.CommerceCoordinatorApi, '_request')
+ @patch.object(edx_api.GenericRetirementApi, '_request')
def test_retire_learner(self, mock_request):
learner_data = get_fake_user_retirement()
json_data = {
@@ -570,14 +572,14 @@ class TestCommerceCoordinatorApi(OAuth2Mixin, unittest.TestCase):
}
responses.add(
POST,
- urljoin(self.commerce_coordinator_base_url, 'lms/user_retirement'),
+ urljoin(self.service_api_base_url, 'mock/retirement_url'),
match=[matchers.json_params_matcher(json_data)]
)
- self.commerce_coordinator_api.retire_learner(learner=learner_data)
+ self.generic_api.retire_learner(learner=learner_data)
mock_request.assert_called_once_with(
'POST',
- urljoin(self.commerce_coordinator_base_url, 'lms/user_retirement/'),
+ urljoin(self.service_api_base_url, 'mock/retirement_url/'),
json=json_data
)
diff --git a/scripts/user_retirement/utils/edx_api.py b/scripts/user_retirement/utils/edx_api.py
index d259e9d2eb..b0c5ffae46 100644
--- a/scripts/user_retirement/utils/edx_api.py
+++ b/scripts/user_retirement/utils/edx_api.py
@@ -492,16 +492,19 @@ class LicenseManagerApi(BaseApiClient):
return True
-class CommerceCoordinatorApi(BaseApiClient):
+class GenericRetirementApi(BaseApiClient):
"""
- Commerce-Coordinator API client.
+ Generic API client.
"""
+ def __init__(self, lms_base_url, api_base_url, client_id, client_secret, retirement_url_path):
+ super().__init__(lms_base_url, api_base_url, client_id, client_secret)
+ self.retirement_url_path = retirement_url_path
+
@_retry_lms_api()
def retire_learner(self, learner):
"""
- Performs the learner retirement step for Commerce-Coordinator.
- Passes the learner's LMS User Id instead of username.
+ Performs the learner retirement step for additonal services.
"""
data = {'edx_lms_user_id': learner['user']['id']}
- api_url = self.get_api_url('lms/user_retirement')
+ api_url = self.get_api_url(self.retirement_url_path)
return self._request('POST', api_url, json=data)
diff --git a/scripts/user_retirement/utils/helpers.py b/scripts/user_retirement/utils/helpers.py
index 42723b0999..0c3af6006a 100644
--- a/scripts/user_retirement/utils/helpers.py
+++ b/scripts/user_retirement/utils/helpers.py
@@ -16,9 +16,10 @@ import unicodedata
import yaml
from six import text_type
+from django.conf import settings
from scripts.user_retirement.utils.edx_api import LmsApi # pylint: disable=wrong-import-position
-from scripts.user_retirement.utils.edx_api import CommerceCoordinatorApi, CredentialsApi, EcommerceApi, \
+from scripts.user_retirement.utils.edx_api import CredentialsApi, EcommerceApi, GenericRetirementApi, \
LicenseManagerApi
from scripts.user_retirement.utils.thirdparty_apis.amplitude_api import \
AmplitudeApi # pylint: disable=wrong-import-position
@@ -155,7 +156,6 @@ def _setup_all_apis_or_exit(fail_func, fail_code, config):
credentials_base_url = config['base_urls'].get('credentials', None)
segment_base_url = config['base_urls'].get('segment', None)
license_manager_base_url = config['base_urls'].get('license_manager', None)
- commerce_coordinator_base_url = config['base_urls'].get('commerce_coordinator', None)
client_id = config['client_id']
client_secret = config['client_secret']
braze_api_key = config.get('braze_api_key', None)
@@ -174,16 +174,19 @@ def _setup_all_apis_or_exit(fail_func, fail_code, config):
hubspot_from_address = config.get('hubspot_from_address', None)
hubspot_alert_email = config.get('hubspot_alert_email', None)
+ required_services = [
+ ('BRAZE', braze_api_key),
+ ('AMPLITUDE', amplitude_api_key),
+ ('ECOMMERCE', ecommerce_base_url),
+ ('CREDENTIALS', credentials_base_url),
+ ('SEGMENT', segment_base_url),
+ ('HUBSPOT', hubspot_api_key),
+ ]
+ extra_services = [(service['name'], service['service_base_url']) for service in settings.EXTRA_SERVICES_TO_RETIRE_FROM]
+ all_services = required_services + extra_services
+
for state in config['retirement_pipeline']:
- for service, service_url in (
- ('BRAZE', braze_api_key),
- ('AMPLITUDE', amplitude_api_key),
- ('ECOMMERCE', ecommerce_base_url),
- ('CREDENTIALS', credentials_base_url),
- ('SEGMENT', segment_base_url),
- ('HUBSPOT', hubspot_api_key),
- ('COMMERCE_COORDINATOR', commerce_coordinator_base_url),
- ):
+ for service, service_url in (all_services):
if state[2] == service and service_url is None:
fail_func(fail_code, 'Service URL is not configured, but required for state {}'.format(state))
@@ -239,12 +242,14 @@ def _setup_all_apis_or_exit(fail_func, fail_code, config):
segment_workspace_slug
)
- if commerce_coordinator_base_url:
- config['COMMERCE_COORDINATOR'] = CommerceCoordinatorApi(
+ for service_config in extra_services:
+ service_name = service_config['name']
+ config[service_name] = GenericRetirementApi(
lms_base_url,
- commerce_coordinator_base_url,
+ service_config['service_base_url'],
client_id,
client_secret,
+ service_config['retirement_url_path']
)
except Exception as exc: # pylint: disable=broad-except
fail_func(fail_code, 'Unexpected error occurred!', exc)
From 5e636dea169cbb6aeafd6a3f4a9f9f976810f320 Mon Sep 17 00:00:00 2001
From: Jade Olivier
Date: Mon, 12 Aug 2024 15:09:24 +0200
Subject: [PATCH 037/242] fix: tests
---
lms/envs/test.py | 8 ++++++-
scripts/user_retirement/pytest.ini | 3 +++
.../tests/test_retire_one_learner.py | 23 +++++++------------
scripts/user_retirement/utils/helpers.py | 2 +-
4 files changed, 19 insertions(+), 17 deletions(-)
diff --git a/lms/envs/test.py b/lms/envs/test.py
index 13799a3c40..9d94175980 100644
--- a/lms/envs/test.py
+++ b/lms/envs/test.py
@@ -662,7 +662,13 @@ SUBSCRIPTIONS_TRIAL_LENGTH = 7
CSRF_TRUSTED_ORIGINS = ['.example.com']
CSRF_TRUSTED_ORIGINS_WITH_SCHEME = ['https://*.example.com']
-EXTRA_SERVICES_TO_RETIRE_FROM = {}
+EXTRA_SERVICES_TO_RETIRE_FROM = [
+ {
+ 'name': 'MOCK_SERVICE',
+ 'service_base_url': 'http://fake_service_base_url',
+ 'retirement_url_path': 'fake_retirement_url_path'
+ }
+]
# values are already updated above with default CSRF_TRUSTED_ORIGINS values but in
# case of new django version these values will override.
diff --git a/scripts/user_retirement/pytest.ini b/scripts/user_retirement/pytest.ini
index e69de29bb2..7a127196d0 100644
--- a/scripts/user_retirement/pytest.ini
+++ b/scripts/user_retirement/pytest.ini
@@ -0,0 +1,3 @@
+[pytest]
+
+DJANGO_SETTINGS_MODULE = lms.envs.test
diff --git a/scripts/user_retirement/tests/test_retire_one_learner.py b/scripts/user_retirement/tests/test_retire_one_learner.py
index e620d850ee..a30186cf6e 100644
--- a/scripts/user_retirement/tests/test_retire_one_learner.py
+++ b/scripts/user_retirement/tests/test_retire_one_learner.py
@@ -21,13 +21,6 @@ from scripts.user_retirement.tests.retirement_helpers import fake_config_file, g
from scripts.user_retirement.utils.exception import HttpDoesNotExistException
-@override_settings(EXTRA_SERVICES_TO_RETIRE_FROM=[
- {
- 'name': 'MOCK_SERVICE',
- 'service_base_url': 'http://fake_service_base_url',
- 'retirement_url_path': 'fake_retirement_url_path'
- }
-])
def _call_script(username, fetch_ecom_segment_id=False):
"""
Call the retired learner script with the given username and a generic, temporary config file.
@@ -70,8 +63,8 @@ def test_successful_retirement(*args, **kwargs):
result = _call_script(username, fetch_ecom_segment_id=True)
- # Called once per API we instantiate (LMS, ECommerce, Credentials)
- assert mock_get_access_token.call_count == 3
+ # Called once per API we instantiate (LMS, ECommerce, Credentials, Additional Services)
+ assert mock_get_access_token.call_count == 3 + len(settings.EXTRA_SERVICES_TO_RETIRE_FROM)
mock_get_retirement_state.assert_called_once_with(username)
assert mock_update_learner_state.call_count == 9
@@ -106,7 +99,7 @@ def test_user_does_not_exist(*args, **kwargs):
result = _call_script(username)
- assert mock_get_access_token.call_count == 3
+ assert mock_get_access_token.call_count == 3 + len(settings.EXTRA_SERVICES_TO_RETIRE_FROM)
mock_get_retirement_state.assert_called_once_with(username)
mock_update_learner_state.assert_not_called()
@@ -141,7 +134,7 @@ def test_bad_learner(*args, **kwargs):
mock_get_retirement_state.side_effect = HttpDoesNotExistException
result = _call_script(username)
- assert mock_get_access_token.call_count == 3
+ assert mock_get_access_token.call_count == 3 + len(settings.EXTRA_SERVICES_TO_RETIRE_FROM)
mock_get_retirement_state.assert_called_once_with(username)
mock_update_learner_state.assert_not_called()
@@ -169,7 +162,7 @@ def test_user_in_working_state(*args, **kwargs):
result = _call_script(username)
- assert mock_get_access_token.call_count == 3
+ assert mock_get_access_token.call_count == 3 + len(settings.EXTRA_SERVICES_TO_RETIRE_FROM)
mock_get_retirement_state.assert_called_once_with(username)
mock_update_learner_state.assert_not_called()
@@ -197,7 +190,7 @@ def test_user_in_bad_state(*args, **kwargs):
)
result = _call_script(username)
- assert mock_get_access_token.call_count == 3
+ assert mock_get_access_token.call_count == 3 + len(settings.EXTRA_SERVICES_TO_RETIRE_FROM)
mock_get_retirement_state.assert_called_once_with(username)
mock_update_learner_state.assert_not_called()
@@ -232,7 +225,7 @@ def test_user_in_end_state(*args, **kwargs):
result = _call_script(username)
- assert mock_get_access_token.call_count == 3
+ assert mock_get_access_token.call_count == 3 + len(settings.EXTRA_SERVICES_TO_RETIRE_FROM)
mock_get_retirement_state.assert_called_once_with(username)
mock_update_learner_state.assert_not_called()
@@ -274,7 +267,7 @@ def test_skipping_states(*args, **kwargs):
result = _call_script(username)
# Called once per API we instantiate (LMS, ECommerce, Credentials)
- assert mock_get_access_token.call_count == 3
+ assert mock_get_access_token.call_count == 3 + len(settings.EXTRA_SERVICES_TO_RETIRE_FROM)
mock_get_retirement_state.assert_called_once_with(username)
assert mock_update_learner_state.call_count == 5
diff --git a/scripts/user_retirement/utils/helpers.py b/scripts/user_retirement/utils/helpers.py
index 0c3af6006a..7704a25621 100644
--- a/scripts/user_retirement/utils/helpers.py
+++ b/scripts/user_retirement/utils/helpers.py
@@ -242,7 +242,7 @@ def _setup_all_apis_or_exit(fail_func, fail_code, config):
segment_workspace_slug
)
- for service_config in extra_services:
+ for service_config in settings.EXTRA_SERVICES_TO_RETIRE_FROM:
service_name = service_config['name']
config[service_name] = GenericRetirementApi(
lms_base_url,
From 64098b6dabb04f2788840b1e388299bb2a62c943 Mon Sep 17 00:00:00 2001
From: Jade Olivier
Date: Mon, 12 Aug 2024 15:35:45 +0200
Subject: [PATCH 038/242] fix: Add CI update for tests
---
.github/workflows/units-test-scripts-user-retirement.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/units-test-scripts-user-retirement.yml b/.github/workflows/units-test-scripts-user-retirement.yml
index f1b2b2c539..2e86650675 100644
--- a/.github/workflows/units-test-scripts-user-retirement.yml
+++ b/.github/workflows/units-test-scripts-user-retirement.yml
@@ -31,4 +31,5 @@ jobs:
- name: Run pytest
run: |
+ export DJANGO_SETTINGS_MODULE=lms.envs.test
pytest scripts/user_retirement
From 8a1c30ebc517457ce274167d32bdde34440e210f Mon Sep 17 00:00:00 2001
From: Jade Olivier
Date: Mon, 12 Aug 2024 16:32:37 +0200
Subject: [PATCH 039/242] fix: dependencies
---
scripts/user_retirement/requirements/base.in | 3 +
scripts/user_retirement/requirements/base.txt | 41 ++-
.../user_retirement/requirements/testing.txt | 252 ++++++------------
3 files changed, 128 insertions(+), 168 deletions(-)
diff --git a/scripts/user_retirement/requirements/base.in b/scripts/user_retirement/requirements/base.in
index d0bdab9082..a6bba5e983 100644
--- a/scripts/user_retirement/requirements/base.in
+++ b/scripts/user_retirement/requirements/base.in
@@ -1,12 +1,15 @@
-c ../../../requirements/constraints.txt
boto3
+celery # required because we're using the LMS's settings file for config options
click
+defusedxml # required because we're using the LMS's settings file for config options
pyyaml
backoff
requests
edx-rest-api-client
jenkinsapi
+kombu # required because we're using the LMS's settings file for config options
unicodecsv
simplejson
simple-salesforce
diff --git a/scripts/user_retirement/requirements/base.txt b/scripts/user_retirement/requirements/base.txt
index 47e6e79c22..6213c57b2b 100644
--- a/scripts/user_retirement/requirements/base.txt
+++ b/scripts/user_retirement/requirements/base.txt
@@ -2,14 +2,18 @@
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
-# make upgrade
+# pip-compile --output-file=scripts/user_retirement/requirements/base.txt scripts/user_retirement/requirements/base.in
#
+amqp==5.2.0
+ # via kombu
asgiref==3.8.1
# via django
attrs==24.2.0
# via zeep
backoff==2.2.1
# via -r scripts/user_retirement/requirements/base.in
+billiard==4.2.0
+ # via celery
boto3==1.34.154
# via -r scripts/user_retirement/requirements/base.in
botocore==1.34.154
@@ -18,6 +22,10 @@ botocore==1.34.154
# s3transfer
cachetools==5.4.0
# via google-auth
+celery==5.4.0
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/base.in
certifi==2024.7.4
# via requests
cffi==1.17.0
@@ -32,9 +40,21 @@ click==8.1.6
# via
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
# -r scripts/user_retirement/requirements/base.in
+ # celery
+ # click-didyoumean
+ # click-plugins
+ # click-repl
# edx-django-utils
+click-didyoumean==0.3.1
+ # via celery
+click-plugins==1.1.1
+ # via celery
+click-repl==0.3.0
+ # via celery
cryptography==43.0.0
# via pyjwt
+defusedxml==0.7.1
+ # via -r scripts/user_retirement/requirements/base.in
django==4.2.15
# via
# -c scripts/user_retirement/requirements/../../../requirements/common_constraints.txt
@@ -77,6 +97,10 @@ jmespath==1.0.1
# via
# boto3
# botocore
+kombu==5.4.0
+ # via
+ # -r scripts/user_retirement/requirements/base.in
+ # celery
lxml==4.9.4
# via
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
@@ -89,6 +113,8 @@ pbr==6.0.0
# via stevedore
platformdirs==4.2.2
# via zeep
+prompt-toolkit==3.0.47
+ # via click-repl
proto-plus==1.24.0
# via google-api-core
protobuf==5.27.3
@@ -115,7 +141,9 @@ pynacl==1.5.0
pyparsing==3.1.2
# via httplib2
python-dateutil==2.9.0.post0
- # via botocore
+ # via
+ # botocore
+ # celery
pytz==2024.1
# via
# jenkinsapi
@@ -158,6 +186,8 @@ stevedore==5.2.0
# via edx-django-utils
typing-extensions==4.12.2
# via simple-salesforce
+tzdata==2024.1
+ # via celery
unicodecsv==0.14.1
# via -r scripts/user_retirement/requirements/base.in
uritemplate==4.1.1
@@ -167,5 +197,12 @@ urllib3==1.26.19
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
# botocore
# requests
+vine==5.1.0
+ # via
+ # amqp
+ # celery
+ # kombu
+wcwidth==0.2.13
+ # via prompt-toolkit
zeep==4.2.1
# via simple-salesforce
diff --git a/scripts/user_retirement/requirements/testing.txt b/scripts/user_retirement/requirements/testing.txt
index 006eabeef4..ee55090496 100644
--- a/scripts/user_retirement/requirements/testing.txt
+++ b/scripts/user_retirement/requirements/testing.txt
@@ -2,287 +2,207 @@
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
-# make upgrade
+# pip-compile --output-file=scripts/user_retirement/requirements/testing.txt scripts/user_retirement/requirements/base.in
#
+amqp==5.2.0
+ # via kombu
asgiref==3.8.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # django
+ # via django
attrs==24.2.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # zeep
+ # via zeep
backoff==2.2.1
- # via -r scripts/user_retirement/requirements/base.txt
+ # via -r scripts/user_retirement/requirements/base.in
+billiard==4.2.0
+ # via celery
boto3==1.34.154
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # moto
+ # via -r scripts/user_retirement/requirements/base.in
botocore==1.34.154
# via
- # -r scripts/user_retirement/requirements/base.txt
# boto3
- # moto
# s3transfer
cachetools==5.4.0
+ # via google-auth
+celery==5.4.0
# via
- # -r scripts/user_retirement/requirements/base.txt
- # google-auth
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/base.in
certifi==2024.7.4
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # requests
+ # via requests
cffi==1.17.0
# via
- # -r scripts/user_retirement/requirements/base.txt
# cryptography
# pynacl
charset-normalizer==2.0.12
# via
- # -r scripts/user_retirement/requirements/base.txt
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
# requests
click==8.1.6
# via
- # -r scripts/user_retirement/requirements/base.txt
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/base.in
+ # celery
+ # click-didyoumean
+ # click-plugins
+ # click-repl
# edx-django-utils
+click-didyoumean==0.3.1
+ # via celery
+click-plugins==1.1.1
+ # via celery
+click-repl==0.3.0
+ # via celery
cryptography==43.0.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # moto
- # pyjwt
-ddt==1.7.2
- # via -r scripts/user_retirement/requirements/testing.in
+ # via pyjwt
+defusedxml==0.7.1
+ # via -r scripts/user_retirement/requirements/base.in
django==4.2.15
# via
- # -r scripts/user_retirement/requirements/base.txt
+ # -c scripts/user_retirement/requirements/../../../requirements/common_constraints.txt
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
# django-crum
# django-waffle
# edx-django-utils
django-crum==0.7.9
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-django-utils
+ # via edx-django-utils
django-waffle==4.1.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-django-utils
+ # via edx-django-utils
edx-django-utils==5.15.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-rest-api-client
+ # via edx-rest-api-client
edx-rest-api-client==5.7.1
- # via -r scripts/user_retirement/requirements/base.txt
+ # via -r scripts/user_retirement/requirements/base.in
google-api-core==2.19.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # google-api-python-client
+ # via google-api-python-client
google-api-python-client==2.139.0
- # via -r scripts/user_retirement/requirements/base.txt
+ # via -r scripts/user_retirement/requirements/base.in
google-auth==2.32.0
# via
- # -r scripts/user_retirement/requirements/base.txt
# google-api-core
# google-api-python-client
# google-auth-httplib2
google-auth-httplib2==0.2.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # google-api-python-client
+ # via google-api-python-client
googleapis-common-protos==1.63.2
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # google-api-core
+ # via google-api-core
httplib2==0.22.0
# via
- # -r scripts/user_retirement/requirements/base.txt
# google-api-python-client
# google-auth-httplib2
idna==3.7
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # requests
-iniconfig==2.0.0
- # via pytest
+ # via requests
isodate==0.6.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # zeep
+ # via zeep
jenkinsapi==0.3.13
- # via -r scripts/user_retirement/requirements/base.txt
-jinja2==3.1.4
- # via moto
+ # via -r scripts/user_retirement/requirements/base.in
jmespath==1.0.1
# via
- # -r scripts/user_retirement/requirements/base.txt
# boto3
# botocore
+kombu==5.4.0
+ # via
+ # -r scripts/user_retirement/requirements/base.in
+ # celery
lxml==4.9.4
# via
- # -r scripts/user_retirement/requirements/base.txt
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
# zeep
-markupsafe==2.1.5
- # via
- # jinja2
- # werkzeug
-mock==5.1.0
- # via -r scripts/user_retirement/requirements/testing.in
more-itertools==10.3.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # simple-salesforce
-moto==4.2.14
- # via -r scripts/user_retirement/requirements/testing.in
+ # via simple-salesforce
newrelic==9.12.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-django-utils
-packaging==24.1
- # via pytest
+ # via edx-django-utils
pbr==6.0.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # stevedore
+ # via stevedore
platformdirs==4.2.2
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # zeep
-pluggy==1.5.0
- # via pytest
+ # via zeep
+prompt-toolkit==3.0.47
+ # via click-repl
proto-plus==1.24.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # google-api-core
+ # via google-api-core
protobuf==5.27.3
# via
- # -r scripts/user_retirement/requirements/base.txt
# google-api-core
# googleapis-common-protos
# proto-plus
psutil==6.0.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-django-utils
+ # via edx-django-utils
pyasn1==0.6.0
# via
- # -r scripts/user_retirement/requirements/base.txt
# pyasn1-modules
# rsa
pyasn1-modules==0.4.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # google-auth
+ # via google-auth
pycparser==2.22
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # cffi
+ # via cffi
pyjwt[crypto]==2.9.0
# via
- # -r scripts/user_retirement/requirements/base.txt
# edx-rest-api-client
# simple-salesforce
pynacl==1.5.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-django-utils
+ # via edx-django-utils
pyparsing==3.1.2
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # httplib2
-pytest==8.3.2
- # via -r scripts/user_retirement/requirements/testing.in
+ # via httplib2
python-dateutil==2.9.0.post0
# via
- # -r scripts/user_retirement/requirements/base.txt
# botocore
- # moto
+ # celery
pytz==2024.1
# via
- # -r scripts/user_retirement/requirements/base.txt
# jenkinsapi
# zeep
pyyaml==6.0.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # responses
+ # via -r scripts/user_retirement/requirements/base.in
requests==2.32.3
# via
- # -r scripts/user_retirement/requirements/base.txt
+ # -r scripts/user_retirement/requirements/base.in
# edx-rest-api-client
# google-api-core
# jenkinsapi
- # moto
# requests-file
- # requests-mock
# requests-toolbelt
- # responses
# simple-salesforce
# slumber
# zeep
requests-file==2.1.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # zeep
-requests-mock==1.12.1
- # via -r scripts/user_retirement/requirements/testing.in
+ # via zeep
requests-toolbelt==1.0.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # zeep
-responses==0.25.3
- # via
- # -r scripts/user_retirement/requirements/testing.in
- # moto
+ # via zeep
rsa==4.9
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # google-auth
+ # via google-auth
s3transfer==0.10.2
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # boto3
+ # via boto3
simple-salesforce==1.12.6
- # via -r scripts/user_retirement/requirements/base.txt
+ # via -r scripts/user_retirement/requirements/base.in
simplejson==3.19.2
- # via -r scripts/user_retirement/requirements/base.txt
+ # via -r scripts/user_retirement/requirements/base.in
six==1.16.0
# via
- # -r scripts/user_retirement/requirements/base.txt
# isodate
# jenkinsapi
# python-dateutil
slumber==0.7.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-rest-api-client
+ # via edx-rest-api-client
sqlparse==0.5.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # django
+ # via django
stevedore==5.2.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-django-utils
+ # via edx-django-utils
typing-extensions==4.12.2
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # simple-salesforce
+ # via simple-salesforce
+tzdata==2024.1
+ # via celery
unicodecsv==0.14.1
- # via -r scripts/user_retirement/requirements/base.txt
+ # via -r scripts/user_retirement/requirements/base.in
uritemplate==4.1.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # google-api-python-client
+ # via google-api-python-client
urllib3==1.26.19
# via
- # -r scripts/user_retirement/requirements/base.txt
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
# botocore
# requests
- # responses
-werkzeug==3.0.3
- # via moto
-xmltodict==0.13.0
- # via moto
-zeep==4.2.1
+vine==5.1.0
# via
- # -r scripts/user_retirement/requirements/base.txt
- # simple-salesforce
+ # amqp
+ # celery
+ # kombu
+wcwidth==0.2.13
+ # via prompt-toolkit
+zeep==4.2.1
+ # via simple-salesforce
From 4cde950007b022718123f54a2685d74e956f527d Mon Sep 17 00:00:00 2001
From: Jade Olivier
Date: Mon, 12 Aug 2024 17:00:17 +0200
Subject: [PATCH 040/242] Revert "fix: dependencies"
This reverts commit 2659ae9fff3ec9a85298cae623097177bd869659.
---
scripts/user_retirement/requirements/base.in | 3 -
scripts/user_retirement/requirements/base.txt | 41 +--
.../user_retirement/requirements/testing.txt | 252 ++++++++++++------
3 files changed, 168 insertions(+), 128 deletions(-)
diff --git a/scripts/user_retirement/requirements/base.in b/scripts/user_retirement/requirements/base.in
index a6bba5e983..d0bdab9082 100644
--- a/scripts/user_retirement/requirements/base.in
+++ b/scripts/user_retirement/requirements/base.in
@@ -1,15 +1,12 @@
-c ../../../requirements/constraints.txt
boto3
-celery # required because we're using the LMS's settings file for config options
click
-defusedxml # required because we're using the LMS's settings file for config options
pyyaml
backoff
requests
edx-rest-api-client
jenkinsapi
-kombu # required because we're using the LMS's settings file for config options
unicodecsv
simplejson
simple-salesforce
diff --git a/scripts/user_retirement/requirements/base.txt b/scripts/user_retirement/requirements/base.txt
index 6213c57b2b..47e6e79c22 100644
--- a/scripts/user_retirement/requirements/base.txt
+++ b/scripts/user_retirement/requirements/base.txt
@@ -2,18 +2,14 @@
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
-# pip-compile --output-file=scripts/user_retirement/requirements/base.txt scripts/user_retirement/requirements/base.in
+# make upgrade
#
-amqp==5.2.0
- # via kombu
asgiref==3.8.1
# via django
attrs==24.2.0
# via zeep
backoff==2.2.1
# via -r scripts/user_retirement/requirements/base.in
-billiard==4.2.0
- # via celery
boto3==1.34.154
# via -r scripts/user_retirement/requirements/base.in
botocore==1.34.154
@@ -22,10 +18,6 @@ botocore==1.34.154
# s3transfer
cachetools==5.4.0
# via google-auth
-celery==5.4.0
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/base.in
certifi==2024.7.4
# via requests
cffi==1.17.0
@@ -40,21 +32,9 @@ click==8.1.6
# via
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
# -r scripts/user_retirement/requirements/base.in
- # celery
- # click-didyoumean
- # click-plugins
- # click-repl
# edx-django-utils
-click-didyoumean==0.3.1
- # via celery
-click-plugins==1.1.1
- # via celery
-click-repl==0.3.0
- # via celery
cryptography==43.0.0
# via pyjwt
-defusedxml==0.7.1
- # via -r scripts/user_retirement/requirements/base.in
django==4.2.15
# via
# -c scripts/user_retirement/requirements/../../../requirements/common_constraints.txt
@@ -97,10 +77,6 @@ jmespath==1.0.1
# via
# boto3
# botocore
-kombu==5.4.0
- # via
- # -r scripts/user_retirement/requirements/base.in
- # celery
lxml==4.9.4
# via
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
@@ -113,8 +89,6 @@ pbr==6.0.0
# via stevedore
platformdirs==4.2.2
# via zeep
-prompt-toolkit==3.0.47
- # via click-repl
proto-plus==1.24.0
# via google-api-core
protobuf==5.27.3
@@ -141,9 +115,7 @@ pynacl==1.5.0
pyparsing==3.1.2
# via httplib2
python-dateutil==2.9.0.post0
- # via
- # botocore
- # celery
+ # via botocore
pytz==2024.1
# via
# jenkinsapi
@@ -186,8 +158,6 @@ stevedore==5.2.0
# via edx-django-utils
typing-extensions==4.12.2
# via simple-salesforce
-tzdata==2024.1
- # via celery
unicodecsv==0.14.1
# via -r scripts/user_retirement/requirements/base.in
uritemplate==4.1.1
@@ -197,12 +167,5 @@ urllib3==1.26.19
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
# botocore
# requests
-vine==5.1.0
- # via
- # amqp
- # celery
- # kombu
-wcwidth==0.2.13
- # via prompt-toolkit
zeep==4.2.1
# via simple-salesforce
diff --git a/scripts/user_retirement/requirements/testing.txt b/scripts/user_retirement/requirements/testing.txt
index ee55090496..006eabeef4 100644
--- a/scripts/user_retirement/requirements/testing.txt
+++ b/scripts/user_retirement/requirements/testing.txt
@@ -2,207 +2,287 @@
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
-# pip-compile --output-file=scripts/user_retirement/requirements/testing.txt scripts/user_retirement/requirements/base.in
+# make upgrade
#
-amqp==5.2.0
- # via kombu
asgiref==3.8.1
- # via django
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # django
attrs==24.2.0
- # via zeep
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # zeep
backoff==2.2.1
- # via -r scripts/user_retirement/requirements/base.in
-billiard==4.2.0
- # via celery
+ # via -r scripts/user_retirement/requirements/base.txt
boto3==1.34.154
- # via -r scripts/user_retirement/requirements/base.in
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # moto
botocore==1.34.154
# via
+ # -r scripts/user_retirement/requirements/base.txt
# boto3
+ # moto
# s3transfer
cachetools==5.4.0
- # via google-auth
-celery==5.4.0
# via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/base.in
+ # -r scripts/user_retirement/requirements/base.txt
+ # google-auth
certifi==2024.7.4
- # via requests
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # requests
cffi==1.17.0
# via
+ # -r scripts/user_retirement/requirements/base.txt
# cryptography
# pynacl
charset-normalizer==2.0.12
# via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/base.txt
# requests
click==8.1.6
# via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/base.in
- # celery
- # click-didyoumean
- # click-plugins
- # click-repl
+ # -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
-click-didyoumean==0.3.1
- # via celery
-click-plugins==1.1.1
- # via celery
-click-repl==0.3.0
- # via celery
cryptography==43.0.0
- # via pyjwt
-defusedxml==0.7.1
- # via -r scripts/user_retirement/requirements/base.in
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # moto
+ # pyjwt
+ddt==1.7.2
+ # via -r scripts/user_retirement/requirements/testing.in
django==4.2.15
# via
- # -c scripts/user_retirement/requirements/../../../requirements/common_constraints.txt
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/base.txt
# django-crum
# django-waffle
# edx-django-utils
django-crum==0.7.9
- # via edx-django-utils
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-django-utils
django-waffle==4.1.0
- # via edx-django-utils
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-django-utils
edx-django-utils==5.15.0
- # via edx-rest-api-client
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-rest-api-client
edx-rest-api-client==5.7.1
- # via -r scripts/user_retirement/requirements/base.in
+ # via -r scripts/user_retirement/requirements/base.txt
google-api-core==2.19.1
- # via google-api-python-client
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # google-api-python-client
google-api-python-client==2.139.0
- # via -r scripts/user_retirement/requirements/base.in
+ # via -r scripts/user_retirement/requirements/base.txt
google-auth==2.32.0
# via
+ # -r scripts/user_retirement/requirements/base.txt
# google-api-core
# google-api-python-client
# google-auth-httplib2
google-auth-httplib2==0.2.0
- # via google-api-python-client
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # google-api-python-client
googleapis-common-protos==1.63.2
- # via google-api-core
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # google-api-core
httplib2==0.22.0
# via
+ # -r scripts/user_retirement/requirements/base.txt
# google-api-python-client
# google-auth-httplib2
idna==3.7
- # via requests
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # requests
+iniconfig==2.0.0
+ # via pytest
isodate==0.6.1
- # via zeep
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # zeep
jenkinsapi==0.3.13
- # via -r scripts/user_retirement/requirements/base.in
+ # via -r scripts/user_retirement/requirements/base.txt
+jinja2==3.1.4
+ # via moto
jmespath==1.0.1
# via
+ # -r scripts/user_retirement/requirements/base.txt
# boto3
# botocore
-kombu==5.4.0
- # via
- # -r scripts/user_retirement/requirements/base.in
- # celery
lxml==4.9.4
# via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/base.txt
# zeep
+markupsafe==2.1.5
+ # via
+ # jinja2
+ # werkzeug
+mock==5.1.0
+ # via -r scripts/user_retirement/requirements/testing.in
more-itertools==10.3.0
- # via simple-salesforce
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # simple-salesforce
+moto==4.2.14
+ # via -r scripts/user_retirement/requirements/testing.in
newrelic==9.12.0
- # via edx-django-utils
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-django-utils
+packaging==24.1
+ # via pytest
pbr==6.0.0
- # via stevedore
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # stevedore
platformdirs==4.2.2
- # via zeep
-prompt-toolkit==3.0.47
- # via click-repl
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # zeep
+pluggy==1.5.0
+ # via pytest
proto-plus==1.24.0
- # via google-api-core
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # google-api-core
protobuf==5.27.3
# via
+ # -r scripts/user_retirement/requirements/base.txt
# google-api-core
# googleapis-common-protos
# proto-plus
psutil==6.0.0
- # via edx-django-utils
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-django-utils
pyasn1==0.6.0
# via
+ # -r scripts/user_retirement/requirements/base.txt
# pyasn1-modules
# rsa
pyasn1-modules==0.4.0
- # via google-auth
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # google-auth
pycparser==2.22
- # via cffi
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # cffi
pyjwt[crypto]==2.9.0
# via
+ # -r scripts/user_retirement/requirements/base.txt
# edx-rest-api-client
# simple-salesforce
pynacl==1.5.0
- # via edx-django-utils
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-django-utils
pyparsing==3.1.2
- # via httplib2
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # httplib2
+pytest==8.3.2
+ # via -r scripts/user_retirement/requirements/testing.in
python-dateutil==2.9.0.post0
# via
+ # -r scripts/user_retirement/requirements/base.txt
# botocore
- # celery
+ # moto
pytz==2024.1
# via
+ # -r scripts/user_retirement/requirements/base.txt
# jenkinsapi
# zeep
pyyaml==6.0.1
- # via -r scripts/user_retirement/requirements/base.in
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # responses
requests==2.32.3
# via
- # -r scripts/user_retirement/requirements/base.in
+ # -r scripts/user_retirement/requirements/base.txt
# edx-rest-api-client
# google-api-core
# jenkinsapi
+ # moto
# requests-file
+ # requests-mock
# requests-toolbelt
+ # responses
# simple-salesforce
# slumber
# zeep
requests-file==2.1.0
- # via zeep
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # zeep
+requests-mock==1.12.1
+ # via -r scripts/user_retirement/requirements/testing.in
requests-toolbelt==1.0.0
- # via zeep
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # zeep
+responses==0.25.3
+ # via
+ # -r scripts/user_retirement/requirements/testing.in
+ # moto
rsa==4.9
- # via google-auth
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # google-auth
s3transfer==0.10.2
- # via boto3
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # boto3
simple-salesforce==1.12.6
- # via -r scripts/user_retirement/requirements/base.in
+ # via -r scripts/user_retirement/requirements/base.txt
simplejson==3.19.2
- # via -r scripts/user_retirement/requirements/base.in
+ # via -r scripts/user_retirement/requirements/base.txt
six==1.16.0
# via
+ # -r scripts/user_retirement/requirements/base.txt
# isodate
# jenkinsapi
# python-dateutil
slumber==0.7.1
- # via edx-rest-api-client
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-rest-api-client
sqlparse==0.5.1
- # via django
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # django
stevedore==5.2.0
- # via edx-django-utils
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-django-utils
typing-extensions==4.12.2
- # via simple-salesforce
-tzdata==2024.1
- # via celery
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # simple-salesforce
unicodecsv==0.14.1
- # via -r scripts/user_retirement/requirements/base.in
+ # via -r scripts/user_retirement/requirements/base.txt
uritemplate==4.1.1
- # via google-api-python-client
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # google-api-python-client
urllib3==1.26.19
# via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/base.txt
# botocore
# requests
-vine==5.1.0
- # via
- # amqp
- # celery
- # kombu
-wcwidth==0.2.13
- # via prompt-toolkit
+ # responses
+werkzeug==3.0.3
+ # via moto
+xmltodict==0.13.0
+ # via moto
zeep==4.2.1
- # via simple-salesforce
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # simple-salesforce
From a5442b2409b0e896fd0bb0d2765a5cdcf593d161 Mon Sep 17 00:00:00 2001
From: Jade Olivier
Date: Tue, 13 Aug 2024 11:34:16 +0200
Subject: [PATCH 041/242] fix: install dependencies
---
.../units-test-scripts-user-retirement.yml | 1 +
scripts/user_retirement/requirements/base.in | 1 +
scripts/user_retirement/requirements/base.txt | 1471 ++++++++++++++++-
.../user_retirement/requirements/testing.txt | 1284 +++++++++++++-
4 files changed, 2697 insertions(+), 60 deletions(-)
diff --git a/.github/workflows/units-test-scripts-user-retirement.yml b/.github/workflows/units-test-scripts-user-retirement.yml
index 2e86650675..e9b7c84c8a 100644
--- a/.github/workflows/units-test-scripts-user-retirement.yml
+++ b/.github/workflows/units-test-scripts-user-retirement.yml
@@ -26,6 +26,7 @@ jobs:
- name: Install dependencies
run: |
+ sudo apt-get update && sudo apt-get install libxmlsec1-dev
python -m pip install --upgrade pip
pip install -r scripts/user_retirement/requirements/testing.txt
diff --git a/scripts/user_retirement/requirements/base.in b/scripts/user_retirement/requirements/base.in
index d0bdab9082..ee037a58b5 100644
--- a/scripts/user_retirement/requirements/base.in
+++ b/scripts/user_retirement/requirements/base.in
@@ -1,4 +1,5 @@
-c ../../../requirements/constraints.txt
+-r ../../../requirements/edx/base.txt
boto3
click
diff --git a/scripts/user_retirement/requirements/base.txt b/scripts/user_retirement/requirements/base.txt
index 47e6e79c22..b0a649c675 100644
--- a/scripts/user_retirement/requirements/base.txt
+++ b/scripts/user_retirement/requirements/base.txt
@@ -4,168 +4,1545 @@
#
# make upgrade
#
+-e git+https://github.com/anupdhabarde/edx-proctoring-proctortrack.git@31c6c9923a51c903ae83760ecbbac191363aa2a2#egg=edx_proctoring_proctortrack
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+acid-xblock==0.3.1
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+aiohappyeyeballs==2.3.4
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # aiohttp
+aiohttp==3.10.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # geoip2
+ # openai
+aiosignal==1.3.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # aiohttp
+algoliasearch==3.0.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+amqp==5.2.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # kombu
+analytics-python==1.4.post1
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+aniso8601==9.0.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-tincan-py35
+annotated-types==0.7.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # pydantic
+appdirs==1.4.4
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # fs
asgiref==3.8.1
- # via django
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # django
+ # django-cors-headers
+ # django-countries
+asn1crypto==1.5.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # snowflake-connector-python
attrs==24.2.0
- # via zeep
-backoff==2.2.1
- # via -r scripts/user_retirement/requirements/base.in
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # aiohttp
+ # edx-ace
+ # jsonschema
+ # lti-consumer-xblock
+ # openedx-events
+ # openedx-learning
+ # referencing
+ # zeep
+babel==2.15.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # enmerkar
+ # enmerkar-underscore
+backoff==1.10.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # -r scripts/user_retirement/requirements/base.in
+ # analytics-python
+bcrypt==4.2.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # paramiko
+beautifulsoup4==4.12.3
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # pynliner
+billiard==4.2.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # celery
+bleach[css]==6.1.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+ # lti-consumer-xblock
+ # openedx-django-wiki
+ # ora2
+ # xblock-drag-and-drop-v2
+ # xblock-poll
+boto==2.49.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
boto3==1.34.154
- # via -r scripts/user_retirement/requirements/base.in
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # -r scripts/user_retirement/requirements/base.in
+ # django-ses
+ # fs-s3fs
+ # ora2
botocore==1.34.154
# via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# boto3
# s3transfer
+bridgekeeper==0.9
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+cachecontrol==0.14.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # firebase-admin
cachetools==5.4.0
- # via google-auth
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # google-auth
+camel-converter[pydantic]==3.1.2
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # meilisearch
+celery==5.4.0
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # django-celery-results
+ # django-user-tasks
+ # edx-celeryutils
+ # edx-enterprise
+ # event-tracking
+ # openedx-learning
certifi==2024.7.4
- # via requests
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # elasticsearch
+ # py2neo
+ # requests
+ # snowflake-connector-python
cffi==1.17.0
# via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# cryptography
# pynacl
+ # snowflake-connector-python
+chardet==5.2.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # pysrt
charset-normalizer==2.0.12
# via
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# requests
+ # snowflake-connector-python
+chem==1.3.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
click==8.1.6
# via
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# -r scripts/user_retirement/requirements/base.in
+ # celery
+ # click-didyoumean
+ # click-plugins
+ # click-repl
+ # code-annotations
# edx-django-utils
-cryptography==43.0.0
- # via pyjwt
+ # nltk
+ # user-util
+click-didyoumean==0.3.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # celery
+click-plugins==1.1.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # celery
+click-repl==0.3.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # celery
+code-annotations==1.8.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+ # edx-toggles
+codejail-includes==1.0.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+crowdsourcehinter-xblock==0.7
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+cryptography==42.0.8
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # django-fernet-fields-v2
+ # edx-enterprise
+ # jwcrypto
+ # optimizely-sdk
+ # paramiko
+ # pgpy
+ # pyjwt
+ # pyopenssl
+ # snowflake-connector-python
+ # social-auth-core
+cssutils==2.11.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # pynliner
+defusedxml==0.7.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # djangorestframework-xml
+ # ora2
+ # python3-openid
+ # social-auth-core
django==4.2.15
# via
# -c scripts/user_retirement/requirements/../../../requirements/common_constraints.txt
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # django-appconf
+ # django-celery-results
+ # django-classy-tags
+ # django-config-models
+ # django-cors-headers
# django-crum
+ # django-fernet-fields-v2
+ # django-filter
+ # django-js-asset
+ # django-method-override
+ # django-model-utils
+ # django-multi-email-field
+ # django-mysql
+ # django-oauth-toolkit
+ # django-push-notifications
+ # django-sekizai
+ # django-ses
+ # django-statici18n
+ # django-storages
+ # django-user-tasks
# django-waffle
+ # djangorestframework
+ # done-xblock
+ # drf-jwt
+ # drf-spectacular
+ # drf-yasg
+ # edx-ace
+ # edx-api-doc-tools
+ # edx-auth-backends
+ # edx-bulk-grades
+ # edx-celeryutils
+ # edx-completion
+ # edx-django-release-util
+ # edx-django-sites-extensions
# edx-django-utils
+ # edx-drf-extensions
+ # edx-enterprise
+ # edx-event-bus-kafka
+ # edx-event-bus-redis
+ # edx-i18n-tools
+ # edx-milestones
+ # edx-name-affirmation
+ # edx-organizations
+ # edx-proctoring
+ # edx-rbac
+ # edx-search
+ # edx-submissions
+ # edx-toggles
+ # edx-token-utils
+ # edx-when
+ # edxval
+ # enmerkar
+ # enmerkar-underscore
+ # event-tracking
+ # help-tokens
+ # jsonfield
+ # lti-consumer-xblock
+ # openedx-django-pyfs
+ # openedx-django-wiki
+ # openedx-events
+ # openedx-filters
+ # openedx-learning
+ # ora2
+ # social-auth-app-django
+ # super-csv
+ # xblock-google-drive
+ # xss-utils
+django-appconf==1.0.6
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # django-statici18n
+django-cache-memoize==0.2.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+django-celery-results==2.5.1
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+django-classy-tags==4.1.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # django-sekizai
+django-config-models==2.7.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+ # edx-name-affirmation
+ # lti-consumer-xblock
+django-cors-headers==4.4.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+django-countries==7.6.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
django-crum==0.7.9
- # via edx-django-utils
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-django-utils
+ # edx-enterprise
+ # edx-proctoring
+ # edx-rbac
+ # edx-toggles
+ # super-csv
+django-fernet-fields-v2==0.9
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+django-filter==24.3
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+ # lti-consumer-xblock
+django-ipware==7.0.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+ # edx-proctoring
+django-js-asset==2.2.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # django-mptt
+django-method-override==1.0.4
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+django-model-utils==4.5.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # django-user-tasks
+ # edx-bulk-grades
+ # edx-celeryutils
+ # edx-completion
+ # edx-enterprise
+ # edx-milestones
+ # edx-name-affirmation
+ # edx-organizations
+ # edx-proctoring
+ # edx-rbac
+ # edx-submissions
+ # edx-when
+ # edxval
+ # ora2
+ # super-csv
+django-mptt==0.16.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # openedx-django-wiki
+django-multi-email-field==0.7.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+django-mysql==4.14.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+django-oauth-toolkit==1.7.1
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+django-object-actions==4.2.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+django-pipeline==3.1.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+django-push-notifications==3.1.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-ace
+django-ratelimit==4.1.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+django-sekizai==4.1.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # openedx-django-wiki
+django-ses==4.1.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+django-simple-history==3.4.0
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+ # edx-name-affirmation
+ # edx-organizations
+ # edx-proctoring
+ # ora2
+django-statici18n==2.5.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # lti-consumer-xblock
+ # xblock-drag-and-drop-v2
+ # xblock-poll
+django-storages==1.14.3
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edxval
+django-user-tasks==3.2.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
django-waffle==4.1.0
- # via edx-django-utils
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-django-utils
+ # edx-drf-extensions
+ # edx-enterprise
+ # edx-proctoring
+ # edx-toggles
+django-webpack-loader==0.7.0
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-proctoring
+djangorestframework==3.14.0
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # django-config-models
+ # django-user-tasks
+ # drf-jwt
+ # drf-spectacular
+ # drf-yasg
+ # edx-api-doc-tools
+ # edx-completion
+ # edx-drf-extensions
+ # edx-enterprise
+ # edx-name-affirmation
+ # edx-organizations
+ # edx-proctoring
+ # edx-submissions
+ # openedx-learning
+ # ora2
+ # super-csv
+djangorestframework-xml==2.0.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+dnspython==2.6.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # pymongo
+done-xblock==2.3.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+drf-jwt==1.19.2
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-drf-extensions
+drf-spectacular==0.27.2
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+drf-yasg==1.21.7
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # django-user-tasks
+ # edx-api-doc-tools
+edx-ace==1.11.1
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+edx-api-doc-tools==1.8.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-name-affirmation
+edx-auth-backends==4.3.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+edx-braze-client==0.2.5
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+edx-bulk-grades==1.1.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # staff-graded-xblock
+edx-ccx-keys==1.3.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # lti-consumer-xblock
+ # openedx-events
+edx-celeryutils==1.3.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-name-affirmation
+ # super-csv
+edx-codejail==3.4.1
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+edx-completion==4.6.7
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+edx-django-release-util==1.4.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-submissions
+ # edxval
+edx-django-sites-extensions==4.2.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
edx-django-utils==5.15.0
- # via edx-rest-api-client
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # django-config-models
+ # edx-ace
+ # edx-drf-extensions
+ # edx-enterprise
+ # edx-event-bus-kafka
+ # edx-event-bus-redis
+ # edx-name-affirmation
+ # edx-rest-api-client
+ # edx-toggles
+ # edx-when
+ # event-tracking
+ # openedx-events
+ # ora2
+ # super-csv
+edx-drf-extensions==10.3.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-completion
+ # edx-enterprise
+ # edx-name-affirmation
+ # edx-organizations
+ # edx-proctoring
+ # edx-rbac
+ # edx-when
+ # edxval
+ # openedx-learning
+edx-enterprise==4.23.4
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+edx-event-bus-kafka==5.8.1
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+edx-event-bus-redis==0.5.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+edx-i18n-tools==1.5.0
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # ora2
+edx-milestones==0.6.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+edx-name-affirmation==2.3.7
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+edx-opaque-keys[django]==2.10.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-bulk-grades
+ # edx-ccx-keys
+ # edx-completion
+ # edx-drf-extensions
+ # edx-enterprise
+ # edx-milestones
+ # edx-organizations
+ # edx-proctoring
+ # edx-when
+ # lti-consumer-xblock
+ # openedx-events
+ # ora2
+edx-organizations==6.13.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+edx-proctoring==4.18.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-proctoring-proctortrack
+edx-rbac==1.9.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
edx-rest-api-client==5.7.1
- # via -r scripts/user_retirement/requirements/base.in
-google-api-core==2.19.1
- # via google-api-python-client
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # -r scripts/user_retirement/requirements/base.in
+ # edx-enterprise
+ # edx-proctoring
+edx-search==4.0.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+edx-sga==0.25.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+edx-submissions==3.7.6
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # ora2
+edx-tincan-py35==1.0.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+edx-toggles==5.2.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-completion
+ # edx-enterprise
+ # edx-event-bus-kafka
+ # edx-event-bus-redis
+ # edx-name-affirmation
+ # edx-search
+ # edxval
+ # event-tracking
+ # ora2
+edx-token-utils==0.2.1
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+edx-when==2.5.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-proctoring
+edxval==2.5.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+elasticsearch==7.13.4
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/common_constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-search
+enmerkar==0.7.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # enmerkar-underscore
+enmerkar-underscore==2.3.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+event-tracking==3.0.0
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-completion
+ # edx-proctoring
+ # edx-search
+fastavro==1.9.5
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # openedx-events
+filelock==3.15.4
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # snowflake-connector-python
+firebase-admin==6.5.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-ace
+frozenlist==1.4.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # aiohttp
+ # aiosignal
+fs==2.0.27
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # fs-s3fs
+ # openedx-django-pyfs
+ # xblock
+fs-s3fs==0.1.8
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # openedx-django-pyfs
+future==1.0.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # pyjwkest
+geoip2==4.8.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+glob2==0.7
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+google-api-core[grpc]==2.19.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # firebase-admin
+ # google-api-python-client
+ # google-cloud-core
+ # google-cloud-firestore
+ # google-cloud-storage
google-api-python-client==2.139.0
- # via -r scripts/user_retirement/requirements/base.in
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # -r scripts/user_retirement/requirements/base.in
+ # firebase-admin
google-auth==2.32.0
# via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# google-api-core
# google-api-python-client
# google-auth-httplib2
+ # google-cloud-core
+ # google-cloud-firestore
+ # google-cloud-storage
google-auth-httplib2==0.2.0
- # via google-api-python-client
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # google-api-python-client
+google-cloud-core==2.4.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # google-cloud-firestore
+ # google-cloud-storage
+google-cloud-firestore==2.17.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # firebase-admin
+google-cloud-storage==2.18.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # firebase-admin
+google-crc32c==1.5.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # google-cloud-storage
+ # google-resumable-media
+google-resumable-media==2.7.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # google-cloud-storage
googleapis-common-protos==1.63.2
- # via google-api-core
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # google-api-core
+ # grpcio-status
+grpcio==1.65.4
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # google-api-core
+ # grpcio-status
+grpcio-status==1.62.3
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # google-api-core
+gunicorn==22.0.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+help-tokens==2.4.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+html5lib==1.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # ora2
httplib2==0.22.0
# via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# google-api-python-client
# google-auth-httplib2
+icalendar==5.0.13
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
idna==3.7
- # via requests
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # optimizely-sdk
+ # requests
+ # snowflake-connector-python
+ # yarl
+importlib-metadata==6.11.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+inflection==0.5.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # drf-spectacular
+ # drf-yasg
+interchange==2021.0.4
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # py2neo
+ipaddress==1.0.23
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
isodate==0.6.1
- # via zeep
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # python3-saml
+ # zeep
jenkinsapi==0.3.13
# via -r scripts/user_retirement/requirements/base.in
+jinja2==3.1.4
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # code-annotations
jmespath==1.0.1
# via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# boto3
# botocore
+joblib==1.4.2
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # nltk
+jsondiff==2.2.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+jsonfield==3.1.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-celeryutils
+ # edx-enterprise
+ # edx-proctoring
+ # edx-submissions
+ # lti-consumer-xblock
+ # ora2
+jsonschema==4.23.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # drf-spectacular
+ # optimizely-sdk
+jsonschema-specifications==2023.12.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # jsonschema
+jwcrypto==1.5.6
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # django-oauth-toolkit
+ # pylti1p3
+kombu==5.4.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # celery
+laboratory==1.0.2
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+lazy==1.6
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # acid-xblock
+ # lti-consumer-xblock
+ # ora2
+ # xblock
+libsass==0.10.0
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+loremipsum==1.0.5
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # ora2
+lti-consumer-xblock==9.11.3
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
lxml==4.9.4
# via
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-i18n-tools
+ # edxval
+ # lti-consumer-xblock
+ # olxcleaner
+ # openedx-calc
+ # ora2
+ # python3-saml
+ # xblock
+ # xmlsec
# zeep
-more-itertools==10.3.0
- # via simple-salesforce
-newrelic==9.12.0
- # via edx-django-utils
-pbr==6.0.0
- # via stevedore
-platformdirs==4.2.2
- # via zeep
-proto-plus==1.24.0
- # via google-api-core
-protobuf==5.27.3
+mailsnake==1.6.4
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+mako==1.3.5
# via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # acid-xblock
+ # lti-consumer-xblock
+ # xblock
+ # xblock-utils
+markdown==3.3.7
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # openedx-django-wiki
+ # staff-graded-xblock
+ # xblock-poll
+markupsafe==2.1.5
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # chem
+ # jinja2
+ # mako
+ # openedx-calc
+ # xblock
+maxminddb==2.6.2
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # geoip2
+meilisearch==0.31.4
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+mock==5.1.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+mongoengine==0.28.2
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+monotonic==1.6
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # analytics-python
+ # py2neo
+more-itertools==10.3.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # cssutils
+ # simple-salesforce
+mpmath==1.3.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # sympy
+msgpack==1.0.8
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # cachecontrol
+multidict==6.0.5
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # aiohttp
+ # yarl
+mysqlclient==2.2.4
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+newrelic==9.12.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-django-utils
+nh3==0.2.18
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+nltk==3.8.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # chem
+nodeenv==1.9.1
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+numpy==1.26.4
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # chem
+ # openedx-calc
+ # scipy
+ # shapely
+oauthlib==3.2.2
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # django-oauth-toolkit
+ # lti-consumer-xblock
+ # requests-oauthlib
+ # social-auth-core
+olxcleaner==0.3.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+openai==0.28.1
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+openedx-atlas==0.6.1
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+openedx-calc==3.1.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+openedx-django-pyfs==3.6.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # lti-consumer-xblock
+ # xblock
+openedx-django-require==2.1.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+openedx-django-wiki==2.1.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+openedx-events==9.11.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-event-bus-kafka
+ # edx-event-bus-redis
+ # event-tracking
+ # ora2
+openedx-filters==1.9.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # lti-consumer-xblock
+ # ora2
+openedx-learning==0.10.1
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+openedx-mongodbproxy==0.2.1
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+optimizely-sdk==4.1.1
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ora2==6.11.2
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+packaging==24.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # drf-yasg
+ # gunicorn
+ # py2neo
+ # snowflake-connector-python
+pansi==2020.7.3
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # py2neo
+paramiko==3.4.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+path==16.11.0
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-i18n-tools
+ # path-py
+path-py==12.5.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+ # ora2
+ # staff-graded-xblock
+paver==1.3.4
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+pbr==6.0.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # stevedore
+pgpy==0.6.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+piexif==1.1.3
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+pillow==10.4.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+ # edx-organizations
+ # edxval
+platformdirs==4.2.2
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # snowflake-connector-python
+ # zeep
+polib==1.2.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-i18n-tools
+prompt-toolkit==3.0.47
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # click-repl
+proto-plus==1.24.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# google-api-core
+ # google-cloud-firestore
+protobuf==4.25.4
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # google-api-core
+ # google-cloud-firestore
# googleapis-common-protos
+ # grpcio-status
# proto-plus
psutil==6.0.0
- # via edx-django-utils
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-django-utils
+py2neo @ https://github.com/overhangio/py2neo/releases/download/2021.2.3/py2neo-2021.2.3.tar.gz
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
pyasn1==0.6.0
# via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # pgpy
# pyasn1-modules
# rsa
pyasn1-modules==0.4.0
- # via google-auth
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # google-auth
+pycountry==24.6.1
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
pycparser==2.22
- # via cffi
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # cffi
+pycryptodomex==3.20.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-proctoring
+ # lti-consumer-xblock
+ # pyjwkest
+pydantic==2.8.2
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # camel-converter
+pydantic-core==2.20.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # pydantic
+pygments==2.18.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # py2neo
+pyjwkest==1.4.2
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-token-utils
+ # lti-consumer-xblock
pyjwt[crypto]==2.9.0
# via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # drf-jwt
+ # edx-auth-backends
+ # edx-drf-extensions
+ # edx-proctoring
# edx-rest-api-client
+ # firebase-admin
+ # pylti1p3
# simple-salesforce
+ # snowflake-connector-python
+ # social-auth-core
+pylatexenc==2.10
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # olxcleaner
+pylti1p3==2.0.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+pymemcache==4.0.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+pymongo==4.4.0
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-opaque-keys
+ # event-tracking
+ # mongoengine
+ # openedx-mongodbproxy
pynacl==1.5.0
- # via edx-django-utils
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-django-utils
+ # paramiko
+pynliner==0.8.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+pyopenssl==24.2.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # optimizely-sdk
+ # snowflake-connector-python
pyparsing==3.1.2
- # via httplib2
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # chem
+ # httplib2
+ # openedx-calc
+pyrsistent==0.20.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # optimizely-sdk
+pysrt==1.1.2
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edxval
python-dateutil==2.9.0.post0
- # via botocore
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # analytics-python
+ # botocore
+ # celery
+ # edx-ace
+ # edx-enterprise
+ # edx-proctoring
+ # icalendar
+ # olxcleaner
+ # ora2
+ # xblock
+python-ipware==3.0.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # django-ipware
+python-memcached==1.62
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+python-slugify==8.0.4
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # code-annotations
+python-swiftclient==4.6.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # ora2
+python3-openid==3.2.0 ; python_version >= "3"
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # social-auth-core
+python3-saml==1.16.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
pytz==2024.1
# via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # django-ses
+ # djangorestframework
+ # drf-yasg
+ # edx-completion
+ # edx-enterprise
+ # edx-proctoring
+ # edx-submissions
+ # edx-tincan-py35
+ # event-tracking
+ # fs
+ # icalendar
+ # interchange
# jenkinsapi
+ # olxcleaner
+ # ora2
+ # snowflake-connector-python
+ # xblock
# zeep
+pyuca==1.2
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
pyyaml==6.0.1
- # via -r scripts/user_retirement/requirements/base.in
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # -r scripts/user_retirement/requirements/base.in
+ # code-annotations
+ # drf-spectacular
+ # drf-yasg
+ # edx-django-release-util
+ # edx-i18n-tools
+ # jsondiff
+ # xblock
+random2==1.0.2
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+recommender-xblock==2.2.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+redis==5.0.8
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # walrus
+referencing==0.35.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # jsonschema
+ # jsonschema-specifications
+regex==2024.7.24
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # nltk
requests==2.32.3
# via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# -r scripts/user_retirement/requirements/base.in
+ # algoliasearch
+ # analytics-python
+ # cachecontrol
+ # django-oauth-toolkit
+ # edx-bulk-grades
+ # edx-drf-extensions
+ # edx-enterprise
# edx-rest-api-client
+ # geoip2
# google-api-core
+ # google-cloud-storage
# jenkinsapi
+ # mailsnake
+ # meilisearch
+ # openai
+ # optimizely-sdk
+ # pyjwkest
+ # pylti1p3
+ # python-swiftclient
# requests-file
+ # requests-oauthlib
# requests-toolbelt
+ # sailthru-client
# simple-salesforce
# slumber
+ # snowflake-connector-python
+ # social-auth-core
+ # xblock-google-drive
# zeep
requests-file==2.1.0
# via zeep
+requests-oauthlib==2.0.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # social-auth-core
requests-toolbelt==1.0.0
# via zeep
+rpds-py==0.20.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # jsonschema
+ # referencing
rsa==4.9
- # via google-auth
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # google-auth
+rules==3.4
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+ # edx-proctoring
+ # openedx-learning
s3transfer==0.10.2
- # via boto3
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # boto3
+sailthru-client==2.2.3
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-ace
+scipy==1.14.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # chem
+ # openedx-calc
+semantic-version==2.10.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-drf-extensions
+shapely==2.0.5
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
simple-salesforce==1.12.6
# via -r scripts/user_retirement/requirements/base.in
simplejson==3.19.2
- # via -r scripts/user_retirement/requirements/base.in
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # -r scripts/user_retirement/requirements/base.in
+ # sailthru-client
+ # super-csv
+ # xblock
+ # xblock-utils
six==1.16.0
# via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # analytics-python
+ # bleach
+ # codejail-includes
+ # crowdsourcehinter-xblock
+ # edx-ace
+ # edx-auth-backends
+ # edx-ccx-keys
+ # edx-codejail
+ # edx-django-release-util
+ # edx-milestones
+ # edx-rbac
+ # event-tracking
+ # fs
+ # fs-s3fs
+ # html5lib
+ # interchange
# isodate
# jenkinsapi
+ # libsass
+ # optimizely-sdk
+ # pansi
+ # paver
+ # py2neo
+ # pyjwkest
# python-dateutil
slumber==0.7.1
- # via edx-rest-api-client
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-bulk-grades
+ # edx-enterprise
+ # edx-rest-api-client
+snowflake-connector-python==3.12.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+social-auth-app-django==5.4.1
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-auth-backends
+social-auth-core==4.5.4
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-auth-backends
+ # social-auth-app-django
+sorl-thumbnail==12.10.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # openedx-django-wiki
+sortedcontainers==2.4.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # snowflake-connector-python
+soupsieve==2.5
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # beautifulsoup4
sqlparse==0.5.1
- # via django
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # django
+staff-graded-xblock==2.3.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
stevedore==5.2.0
- # via edx-django-utils
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # code-annotations
+ # edx-ace
+ # edx-django-utils
+ # edx-enterprise
+ # edx-opaque-keys
+super-csv==3.2.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-bulk-grades
+sympy==1.13.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # openedx-calc
+testfixtures==8.3.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-enterprise
+text-unidecode==1.3
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # python-slugify
+tinycss2==1.2.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # bleach
+tomlkit==0.13.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # snowflake-connector-python
+tqdm==4.66.5
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # nltk
+ # openai
typing-extensions==4.12.2
- # via simple-salesforce
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # django-countries
+ # edx-opaque-keys
+ # jwcrypto
+ # pydantic
+ # pydantic-core
+ # pylti1p3
+ # simple-salesforce
+ # snowflake-connector-python
+tzdata==2024.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # celery
unicodecsv==0.14.1
- # via -r scripts/user_retirement/requirements/base.in
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # -r scripts/user_retirement/requirements/base.in
+ # edx-enterprise
uritemplate==4.1.1
- # via google-api-python-client
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # drf-spectacular
+ # drf-yasg
+ # google-api-python-client
urllib3==1.26.19
# via
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# botocore
+ # elasticsearch
+ # py2neo
# requests
+user-util==1.1.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+vine==5.1.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # amqp
+ # celery
+ # kombu
+voluptuous==0.15.2
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # ora2
+walrus==0.9.4
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-event-bus-redis
+watchdog==4.0.1
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+wcwidth==0.2.13
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # prompt-toolkit
+web-fragments==2.2.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # crowdsourcehinter-xblock
+ # edx-sga
+ # staff-graded-xblock
+ # xblock
+ # xblock-utils
+webencodings==0.5.1
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # bleach
+ # html5lib
+ # tinycss2
+webob==1.8.7
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # xblock
+wrapt==1.16.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+xblock[django]==4.0.1
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # acid-xblock
+ # crowdsourcehinter-xblock
+ # done-xblock
+ # edx-completion
+ # edx-sga
+ # edx-when
+ # lti-consumer-xblock
+ # ora2
+ # staff-graded-xblock
+ # xblock-drag-and-drop-v2
+ # xblock-google-drive
+ # xblock-utils
+xblock-drag-and-drop-v2==4.0.3
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+xblock-google-drive==0.7.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+xblock-poll==1.14.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+xblock-utils==4.0.0
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # edx-sga
+ # xblock-poll
+xmlsec==1.3.13
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # python3-saml
+xss-utils==0.6.0
+ # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+yarl==1.9.4
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # aiohttp
zeep==4.2.1
# via simple-salesforce
+zipp==3.19.2
+ # via
+ # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # importlib-metadata
+
+# The following packages are considered to be unsafe in a requirements file:
+# setuptools
diff --git a/scripts/user_retirement/requirements/testing.txt b/scripts/user_retirement/requirements/testing.txt
index 006eabeef4..3fb96783ca 100644
--- a/scripts/user_retirement/requirements/testing.txt
+++ b/scripts/user_retirement/requirements/testing.txt
@@ -4,219 +4,1256 @@
#
# make upgrade
#
+-e git+https://github.com/anupdhabarde/edx-proctoring-proctortrack.git@31c6c9923a51c903ae83760ecbbac191363aa2a2#egg=edx_proctoring_proctortrack
+ # via -r scripts/user_retirement/requirements/base.txt
+acid-xblock==0.3.1
+ # via -r scripts/user_retirement/requirements/base.txt
+aiohappyeyeballs==2.3.4
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # aiohttp
+aiohttp==3.10.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # geoip2
+ # openai
+aiosignal==1.3.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # aiohttp
+algoliasearch==3.0.0
+ # via -r scripts/user_retirement/requirements/base.txt
+amqp==5.2.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # kombu
+analytics-python==1.4.post1
+ # via -r scripts/user_retirement/requirements/base.txt
+aniso8601==9.0.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-tincan-py35
+annotated-types==0.7.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # pydantic
+appdirs==1.4.4
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # fs
asgiref==3.8.1
# via
# -r scripts/user_retirement/requirements/base.txt
# django
+ # django-cors-headers
+ # django-countries
+asn1crypto==1.5.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # snowflake-connector-python
attrs==24.2.0
# via
# -r scripts/user_retirement/requirements/base.txt
+ # aiohttp
+ # edx-ace
+ # jsonschema
+ # lti-consumer-xblock
+ # openedx-events
+ # openedx-learning
+ # referencing
# zeep
-backoff==2.2.1
+babel==2.15.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # enmerkar
+ # enmerkar-underscore
+backoff==1.10.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # analytics-python
+bcrypt==4.2.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # paramiko
+beautifulsoup4==4.12.3
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # pynliner
+billiard==4.2.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # celery
+bleach[css]==6.1.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+ # lti-consumer-xblock
+ # openedx-django-wiki
+ # ora2
+ # xblock-drag-and-drop-v2
+ # xblock-poll
+boto==2.49.0
# via -r scripts/user_retirement/requirements/base.txt
boto3==1.34.154
# via
# -r scripts/user_retirement/requirements/base.txt
+ # django-ses
+ # fs-s3fs
# moto
+ # ora2
botocore==1.34.154
# via
# -r scripts/user_retirement/requirements/base.txt
# boto3
# moto
# s3transfer
+bridgekeeper==0.9
+ # via -r scripts/user_retirement/requirements/base.txt
+cachecontrol==0.14.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # firebase-admin
cachetools==5.4.0
# via
# -r scripts/user_retirement/requirements/base.txt
# google-auth
+camel-converter[pydantic]==3.1.2
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # meilisearch
+celery==5.4.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # django-celery-results
+ # django-user-tasks
+ # edx-celeryutils
+ # edx-enterprise
+ # event-tracking
+ # openedx-learning
certifi==2024.7.4
# via
# -r scripts/user_retirement/requirements/base.txt
+ # elasticsearch
+ # py2neo
# requests
+ # snowflake-connector-python
cffi==1.17.0
# via
# -r scripts/user_retirement/requirements/base.txt
# cryptography
# pynacl
+ # snowflake-connector-python
+chardet==5.2.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # pysrt
charset-normalizer==2.0.12
# via
# -r scripts/user_retirement/requirements/base.txt
# requests
+ # snowflake-connector-python
+chem==1.3.0
+ # via -r scripts/user_retirement/requirements/base.txt
click==8.1.6
# via
# -r scripts/user_retirement/requirements/base.txt
+ # celery
+ # click-didyoumean
+ # click-plugins
+ # click-repl
+ # code-annotations
# edx-django-utils
-cryptography==43.0.0
+ # nltk
+ # user-util
+click-didyoumean==0.3.1
# via
# -r scripts/user_retirement/requirements/base.txt
+ # celery
+click-plugins==1.1.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # celery
+click-repl==0.3.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # celery
+code-annotations==1.8.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+ # edx-toggles
+codejail-includes==1.0.0
+ # via -r scripts/user_retirement/requirements/base.txt
+crowdsourcehinter-xblock==0.7
+ # via -r scripts/user_retirement/requirements/base.txt
+cryptography==42.0.8
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # django-fernet-fields-v2
+ # edx-enterprise
+ # jwcrypto
# moto
+ # optimizely-sdk
+ # paramiko
+ # pgpy
# pyjwt
+ # pyopenssl
+ # snowflake-connector-python
+ # social-auth-core
+cssutils==2.11.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # pynliner
ddt==1.7.2
# via -r scripts/user_retirement/requirements/testing.in
+defusedxml==0.7.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # djangorestframework-xml
+ # ora2
+ # python3-openid
+ # social-auth-core
django==4.2.15
# via
# -r scripts/user_retirement/requirements/base.txt
+ # django-appconf
+ # django-celery-results
+ # django-classy-tags
+ # django-config-models
+ # django-cors-headers
# django-crum
+ # django-fernet-fields-v2
+ # django-filter
+ # django-js-asset
+ # django-method-override
+ # django-model-utils
+ # django-multi-email-field
+ # django-mysql
+ # django-oauth-toolkit
+ # django-push-notifications
+ # django-sekizai
+ # django-ses
+ # django-statici18n
+ # django-storages
+ # django-user-tasks
# django-waffle
+ # djangorestframework
+ # done-xblock
+ # drf-jwt
+ # drf-spectacular
+ # drf-yasg
+ # edx-ace
+ # edx-api-doc-tools
+ # edx-auth-backends
+ # edx-bulk-grades
+ # edx-celeryutils
+ # edx-completion
+ # edx-django-release-util
+ # edx-django-sites-extensions
# edx-django-utils
+ # edx-drf-extensions
+ # edx-enterprise
+ # edx-event-bus-kafka
+ # edx-event-bus-redis
+ # edx-i18n-tools
+ # edx-milestones
+ # edx-name-affirmation
+ # edx-organizations
+ # edx-proctoring
+ # edx-rbac
+ # edx-search
+ # edx-submissions
+ # edx-toggles
+ # edx-token-utils
+ # edx-when
+ # edxval
+ # enmerkar
+ # enmerkar-underscore
+ # event-tracking
+ # help-tokens
+ # jsonfield
+ # lti-consumer-xblock
+ # openedx-django-pyfs
+ # openedx-django-wiki
+ # openedx-events
+ # openedx-filters
+ # openedx-learning
+ # ora2
+ # social-auth-app-django
+ # super-csv
+ # xblock-google-drive
+ # xss-utils
+django-appconf==1.0.6
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # django-statici18n
+django-cache-memoize==0.2.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+django-celery-results==2.5.1
+ # via -r scripts/user_retirement/requirements/base.txt
+django-classy-tags==4.1.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # django-sekizai
+django-config-models==2.7.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+ # edx-name-affirmation
+ # lti-consumer-xblock
+django-cors-headers==4.4.0
+ # via -r scripts/user_retirement/requirements/base.txt
+django-countries==7.6.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
django-crum==0.7.9
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
+ # edx-enterprise
+ # edx-proctoring
+ # edx-rbac
+ # edx-toggles
+ # super-csv
+django-fernet-fields-v2==0.9
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+django-filter==24.3
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+ # lti-consumer-xblock
+django-ipware==7.0.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+ # edx-proctoring
+django-js-asset==2.2.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # django-mptt
+django-method-override==1.0.4
+ # via -r scripts/user_retirement/requirements/base.txt
+django-model-utils==4.5.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # django-user-tasks
+ # edx-bulk-grades
+ # edx-celeryutils
+ # edx-completion
+ # edx-enterprise
+ # edx-milestones
+ # edx-name-affirmation
+ # edx-organizations
+ # edx-proctoring
+ # edx-rbac
+ # edx-submissions
+ # edx-when
+ # edxval
+ # ora2
+ # super-csv
+django-mptt==0.16.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # openedx-django-wiki
+django-multi-email-field==0.7.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+django-mysql==4.14.0
+ # via -r scripts/user_retirement/requirements/base.txt
+django-oauth-toolkit==1.7.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+django-object-actions==4.2.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+django-pipeline==3.1.0
+ # via -r scripts/user_retirement/requirements/base.txt
+django-push-notifications==3.1.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-ace
+django-ratelimit==4.1.0
+ # via -r scripts/user_retirement/requirements/base.txt
+django-sekizai==4.1.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # openedx-django-wiki
+django-ses==4.1.0
+ # via -r scripts/user_retirement/requirements/base.txt
+django-simple-history==3.4.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+ # edx-name-affirmation
+ # edx-organizations
+ # edx-proctoring
+ # ora2
+django-statici18n==2.5.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # lti-consumer-xblock
+ # xblock-drag-and-drop-v2
+ # xblock-poll
+django-storages==1.14.3
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edxval
+django-user-tasks==3.2.0
+ # via -r scripts/user_retirement/requirements/base.txt
django-waffle==4.1.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
+ # edx-drf-extensions
+ # edx-enterprise
+ # edx-proctoring
+ # edx-toggles
+django-webpack-loader==0.7.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-proctoring
+djangorestframework==3.14.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # django-config-models
+ # django-user-tasks
+ # drf-jwt
+ # drf-spectacular
+ # drf-yasg
+ # edx-api-doc-tools
+ # edx-completion
+ # edx-drf-extensions
+ # edx-enterprise
+ # edx-name-affirmation
+ # edx-organizations
+ # edx-proctoring
+ # edx-submissions
+ # openedx-learning
+ # ora2
+ # super-csv
+djangorestframework-xml==2.0.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+dnspython==2.6.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # pymongo
+done-xblock==2.3.0
+ # via -r scripts/user_retirement/requirements/base.txt
+drf-jwt==1.19.2
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-drf-extensions
+drf-spectacular==0.27.2
+ # via -r scripts/user_retirement/requirements/base.txt
+drf-yasg==1.21.7
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # django-user-tasks
+ # edx-api-doc-tools
+edx-ace==1.11.1
+ # via -r scripts/user_retirement/requirements/base.txt
+edx-api-doc-tools==1.8.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-name-affirmation
+edx-auth-backends==4.3.0
+ # via -r scripts/user_retirement/requirements/base.txt
+edx-braze-client==0.2.5
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+edx-bulk-grades==1.1.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # staff-graded-xblock
+edx-ccx-keys==1.3.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # lti-consumer-xblock
+ # openedx-events
+edx-celeryutils==1.3.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-name-affirmation
+ # super-csv
+edx-codejail==3.4.1
+ # via -r scripts/user_retirement/requirements/base.txt
+edx-completion==4.6.7
+ # via -r scripts/user_retirement/requirements/base.txt
+edx-django-release-util==1.4.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-submissions
+ # edxval
+edx-django-sites-extensions==4.2.0
+ # via -r scripts/user_retirement/requirements/base.txt
edx-django-utils==5.15.0
# via
# -r scripts/user_retirement/requirements/base.txt
+ # django-config-models
+ # edx-ace
+ # edx-drf-extensions
+ # edx-enterprise
+ # edx-event-bus-kafka
+ # edx-event-bus-redis
+ # edx-name-affirmation
# edx-rest-api-client
-edx-rest-api-client==5.7.1
- # via -r scripts/user_retirement/requirements/base.txt
-google-api-core==2.19.1
+ # edx-toggles
+ # edx-when
+ # event-tracking
+ # openedx-events
+ # ora2
+ # super-csv
+edx-drf-extensions==10.3.0
# via
# -r scripts/user_retirement/requirements/base.txt
- # google-api-python-client
-google-api-python-client==2.139.0
+ # edx-completion
+ # edx-enterprise
+ # edx-name-affirmation
+ # edx-organizations
+ # edx-proctoring
+ # edx-rbac
+ # edx-when
+ # edxval
+ # openedx-learning
+edx-enterprise==4.23.4
# via -r scripts/user_retirement/requirements/base.txt
+edx-event-bus-kafka==5.8.1
+ # via -r scripts/user_retirement/requirements/base.txt
+edx-event-bus-redis==0.5.0
+ # via -r scripts/user_retirement/requirements/base.txt
+edx-i18n-tools==1.5.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # ora2
+edx-milestones==0.6.0
+ # via -r scripts/user_retirement/requirements/base.txt
+edx-name-affirmation==2.3.7
+ # via -r scripts/user_retirement/requirements/base.txt
+edx-opaque-keys[django]==2.10.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-bulk-grades
+ # edx-ccx-keys
+ # edx-completion
+ # edx-drf-extensions
+ # edx-enterprise
+ # edx-milestones
+ # edx-organizations
+ # edx-proctoring
+ # edx-when
+ # lti-consumer-xblock
+ # openedx-events
+ # ora2
+edx-organizations==6.13.0
+ # via -r scripts/user_retirement/requirements/base.txt
+edx-proctoring==4.18.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-proctoring-proctortrack
+edx-rbac==1.9.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+edx-rest-api-client==5.7.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+ # edx-proctoring
+edx-search==4.0.0
+ # via -r scripts/user_retirement/requirements/base.txt
+edx-sga==0.25.0
+ # via -r scripts/user_retirement/requirements/base.txt
+edx-submissions==3.7.6
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # ora2
+edx-tincan-py35==1.0.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+edx-toggles==5.2.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-completion
+ # edx-enterprise
+ # edx-event-bus-kafka
+ # edx-event-bus-redis
+ # edx-name-affirmation
+ # edx-search
+ # edxval
+ # event-tracking
+ # ora2
+edx-token-utils==0.2.1
+ # via -r scripts/user_retirement/requirements/base.txt
+edx-when==2.5.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-proctoring
+edxval==2.5.0
+ # via -r scripts/user_retirement/requirements/base.txt
+elasticsearch==7.13.4
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-search
+enmerkar==0.7.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # enmerkar-underscore
+enmerkar-underscore==2.3.0
+ # via -r scripts/user_retirement/requirements/base.txt
+event-tracking==3.0.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-completion
+ # edx-proctoring
+ # edx-search
+fastavro==1.9.5
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # openedx-events
+filelock==3.15.4
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # snowflake-connector-python
+firebase-admin==6.5.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-ace
+frozenlist==1.4.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # aiohttp
+ # aiosignal
+fs==2.0.27
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # fs-s3fs
+ # openedx-django-pyfs
+ # xblock
+fs-s3fs==0.1.8
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # openedx-django-pyfs
+future==1.0.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # pyjwkest
+geoip2==4.8.0
+ # via -r scripts/user_retirement/requirements/base.txt
+glob2==0.7
+ # via -r scripts/user_retirement/requirements/base.txt
+google-api-core[grpc]==2.19.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # firebase-admin
+ # google-api-python-client
+ # google-cloud-core
+ # google-cloud-firestore
+ # google-cloud-storage
+google-api-python-client==2.139.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # firebase-admin
google-auth==2.32.0
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-core
# google-api-python-client
# google-auth-httplib2
+ # google-cloud-core
+ # google-cloud-firestore
+ # google-cloud-storage
google-auth-httplib2==0.2.0
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-python-client
+google-cloud-core==2.4.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # google-cloud-firestore
+ # google-cloud-storage
+google-cloud-firestore==2.17.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # firebase-admin
+google-cloud-storage==2.18.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # firebase-admin
+google-crc32c==1.5.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # google-cloud-storage
+ # google-resumable-media
+google-resumable-media==2.7.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # google-cloud-storage
googleapis-common-protos==1.63.2
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-core
+ # grpcio-status
+grpcio==1.65.4
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # google-api-core
+ # grpcio-status
+grpcio-status==1.62.3
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # google-api-core
+gunicorn==22.0.0
+ # via -r scripts/user_retirement/requirements/base.txt
+help-tokens==2.4.0
+ # via -r scripts/user_retirement/requirements/base.txt
+html5lib==1.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # ora2
httplib2==0.22.0
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-python-client
# google-auth-httplib2
+icalendar==5.0.13
+ # via -r scripts/user_retirement/requirements/base.txt
idna==3.7
# via
# -r scripts/user_retirement/requirements/base.txt
+ # optimizely-sdk
# requests
+ # snowflake-connector-python
+ # yarl
+importlib-metadata==6.11.0
+ # via -r scripts/user_retirement/requirements/base.txt
+inflection==0.5.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # drf-spectacular
+ # drf-yasg
iniconfig==2.0.0
# via pytest
+interchange==2021.0.4
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # py2neo
+ipaddress==1.0.23
+ # via -r scripts/user_retirement/requirements/base.txt
isodate==0.6.1
# via
# -r scripts/user_retirement/requirements/base.txt
+ # python3-saml
# zeep
jenkinsapi==0.3.13
# via -r scripts/user_retirement/requirements/base.txt
jinja2==3.1.4
- # via moto
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # code-annotations
+ # moto
jmespath==1.0.1
# via
# -r scripts/user_retirement/requirements/base.txt
# boto3
# botocore
+joblib==1.4.2
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # nltk
+jsondiff==2.2.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+jsonfield==3.1.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-celeryutils
+ # edx-enterprise
+ # edx-proctoring
+ # edx-submissions
+ # lti-consumer-xblock
+ # ora2
+jsonschema==4.23.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # drf-spectacular
+ # optimizely-sdk
+jsonschema-specifications==2023.12.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # jsonschema
+jwcrypto==1.5.6
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # django-oauth-toolkit
+ # pylti1p3
+kombu==5.4.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # celery
+laboratory==1.0.2
+ # via -r scripts/user_retirement/requirements/base.txt
+lazy==1.6
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # acid-xblock
+ # lti-consumer-xblock
+ # ora2
+ # xblock
+libsass==0.10.0
+ # via -r scripts/user_retirement/requirements/base.txt
+loremipsum==1.0.5
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # ora2
+lti-consumer-xblock==9.11.3
+ # via -r scripts/user_retirement/requirements/base.txt
lxml==4.9.4
# via
# -r scripts/user_retirement/requirements/base.txt
+ # edx-i18n-tools
+ # edxval
+ # lti-consumer-xblock
+ # olxcleaner
+ # openedx-calc
+ # ora2
+ # python3-saml
+ # xblock
+ # xmlsec
# zeep
+mailsnake==1.6.4
+ # via -r scripts/user_retirement/requirements/base.txt
+mako==1.3.5
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # acid-xblock
+ # lti-consumer-xblock
+ # xblock
+ # xblock-utils
+markdown==3.3.7
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # openedx-django-wiki
+ # staff-graded-xblock
+ # xblock-poll
markupsafe==2.1.5
# via
+ # -r scripts/user_retirement/requirements/base.txt
+ # chem
# jinja2
+ # mako
+ # openedx-calc
# werkzeug
+ # xblock
+maxminddb==2.6.2
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # geoip2
+meilisearch==0.31.4
+ # via -r scripts/user_retirement/requirements/base.txt
mock==5.1.0
- # via -r scripts/user_retirement/requirements/testing.in
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # -r scripts/user_retirement/requirements/testing.in
+mongoengine==0.28.2
+ # via -r scripts/user_retirement/requirements/base.txt
+monotonic==1.6
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # analytics-python
+ # py2neo
more-itertools==10.3.0
# via
# -r scripts/user_retirement/requirements/base.txt
+ # cssutils
# simple-salesforce
moto==4.2.14
# via -r scripts/user_retirement/requirements/testing.in
+mpmath==1.3.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # sympy
+msgpack==1.0.8
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # cachecontrol
+multidict==6.0.5
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # aiohttp
+ # yarl
+mysqlclient==2.2.4
+ # via -r scripts/user_retirement/requirements/base.txt
newrelic==9.12.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
+nh3==0.2.18
+ # via -r scripts/user_retirement/requirements/base.txt
+nltk==3.8.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # chem
+nodeenv==1.9.1
+ # via -r scripts/user_retirement/requirements/base.txt
+numpy==1.26.4
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # chem
+ # openedx-calc
+ # scipy
+ # shapely
+oauthlib==3.2.2
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # django-oauth-toolkit
+ # lti-consumer-xblock
+ # requests-oauthlib
+ # social-auth-core
+olxcleaner==0.3.0
+ # via -r scripts/user_retirement/requirements/base.txt
+openai==0.28.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+openedx-atlas==0.6.1
+ # via -r scripts/user_retirement/requirements/base.txt
+openedx-calc==3.1.0
+ # via -r scripts/user_retirement/requirements/base.txt
+openedx-django-pyfs==3.6.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # lti-consumer-xblock
+ # xblock
+openedx-django-require==2.1.0
+ # via -r scripts/user_retirement/requirements/base.txt
+openedx-django-wiki==2.1.0
+ # via -r scripts/user_retirement/requirements/base.txt
+openedx-events==9.11.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-event-bus-kafka
+ # edx-event-bus-redis
+ # event-tracking
+ # ora2
+openedx-filters==1.9.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # lti-consumer-xblock
+ # ora2
+openedx-learning==0.10.1
+ # via -r scripts/user_retirement/requirements/base.txt
+openedx-mongodbproxy==0.2.1
+ # via -r scripts/user_retirement/requirements/base.txt
+optimizely-sdk==4.1.1
+ # via -r scripts/user_retirement/requirements/base.txt
+ora2==6.11.2
+ # via -r scripts/user_retirement/requirements/base.txt
packaging==24.1
- # via pytest
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # drf-yasg
+ # gunicorn
+ # py2neo
+ # pytest
+ # snowflake-connector-python
+pansi==2020.7.3
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # py2neo
+paramiko==3.4.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+path==16.11.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-i18n-tools
+ # path-py
+path-py==12.5.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+ # ora2
+ # staff-graded-xblock
+paver==1.3.4
+ # via -r scripts/user_retirement/requirements/base.txt
pbr==6.0.0
# via
# -r scripts/user_retirement/requirements/base.txt
# stevedore
+pgpy==0.6.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+piexif==1.1.3
+ # via -r scripts/user_retirement/requirements/base.txt
+pillow==10.4.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+ # edx-organizations
+ # edxval
platformdirs==4.2.2
# via
# -r scripts/user_retirement/requirements/base.txt
+ # snowflake-connector-python
# zeep
pluggy==1.5.0
# via pytest
+polib==1.2.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-i18n-tools
+prompt-toolkit==3.0.47
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # click-repl
proto-plus==1.24.0
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-core
-protobuf==5.27.3
+ # google-cloud-firestore
+protobuf==4.25.4
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-core
+ # google-cloud-firestore
# googleapis-common-protos
+ # grpcio-status
# proto-plus
psutil==6.0.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
+py2neo @ https://github.com/overhangio/py2neo/releases/download/2021.2.3/py2neo-2021.2.3.tar.gz
+ # via -r scripts/user_retirement/requirements/base.txt
pyasn1==0.6.0
# via
# -r scripts/user_retirement/requirements/base.txt
+ # pgpy
# pyasn1-modules
# rsa
pyasn1-modules==0.4.0
# via
# -r scripts/user_retirement/requirements/base.txt
# google-auth
+pycountry==24.6.1
+ # via -r scripts/user_retirement/requirements/base.txt
pycparser==2.22
# via
# -r scripts/user_retirement/requirements/base.txt
# cffi
+pycryptodomex==3.20.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-proctoring
+ # lti-consumer-xblock
+ # pyjwkest
+pydantic==2.8.2
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # camel-converter
+pydantic-core==2.20.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # pydantic
+pygments==2.18.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # py2neo
+pyjwkest==1.4.2
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-token-utils
+ # lti-consumer-xblock
pyjwt[crypto]==2.9.0
# via
# -r scripts/user_retirement/requirements/base.txt
+ # drf-jwt
+ # edx-auth-backends
+ # edx-drf-extensions
+ # edx-proctoring
# edx-rest-api-client
+ # firebase-admin
+ # pylti1p3
# simple-salesforce
+ # snowflake-connector-python
+ # social-auth-core
+pylatexenc==2.10
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # olxcleaner
+pylti1p3==2.0.0
+ # via -r scripts/user_retirement/requirements/base.txt
+pymemcache==4.0.0
+ # via -r scripts/user_retirement/requirements/base.txt
+pymongo==4.4.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-opaque-keys
+ # event-tracking
+ # mongoengine
+ # openedx-mongodbproxy
pynacl==1.5.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
+ # paramiko
+pynliner==0.8.0
+ # via -r scripts/user_retirement/requirements/base.txt
+pyopenssl==24.2.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # optimizely-sdk
+ # snowflake-connector-python
pyparsing==3.1.2
# via
# -r scripts/user_retirement/requirements/base.txt
+ # chem
# httplib2
+ # openedx-calc
+pyrsistent==0.20.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # optimizely-sdk
+pysrt==1.1.2
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edxval
pytest==8.3.2
# via -r scripts/user_retirement/requirements/testing.in
python-dateutil==2.9.0.post0
# via
# -r scripts/user_retirement/requirements/base.txt
+ # analytics-python
# botocore
+ # celery
+ # edx-ace
+ # edx-enterprise
+ # edx-proctoring
+ # icalendar
# moto
+ # olxcleaner
+ # ora2
+ # xblock
+python-ipware==3.0.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # django-ipware
+python-memcached==1.62
+ # via -r scripts/user_retirement/requirements/base.txt
+python-slugify==8.0.4
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # code-annotations
+python-swiftclient==4.6.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # ora2
+python3-openid==3.2.0 ; python_version >= "3"
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # social-auth-core
+python3-saml==1.16.0
+ # via -r scripts/user_retirement/requirements/base.txt
pytz==2024.1
# via
# -r scripts/user_retirement/requirements/base.txt
+ # django-ses
+ # djangorestframework
+ # drf-yasg
+ # edx-completion
+ # edx-enterprise
+ # edx-proctoring
+ # edx-submissions
+ # edx-tincan-py35
+ # event-tracking
+ # fs
+ # icalendar
+ # interchange
# jenkinsapi
+ # olxcleaner
+ # ora2
+ # snowflake-connector-python
+ # xblock
# zeep
+pyuca==1.2
+ # via -r scripts/user_retirement/requirements/base.txt
pyyaml==6.0.1
# via
# -r scripts/user_retirement/requirements/base.txt
+ # code-annotations
+ # drf-spectacular
+ # drf-yasg
+ # edx-django-release-util
+ # edx-i18n-tools
+ # jsondiff
# responses
+ # xblock
+random2==1.0.2
+ # via -r scripts/user_retirement/requirements/base.txt
+recommender-xblock==2.2.0
+ # via -r scripts/user_retirement/requirements/base.txt
+redis==5.0.8
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # walrus
+referencing==0.35.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # jsonschema
+ # jsonschema-specifications
+regex==2024.7.24
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # nltk
requests==2.32.3
# via
# -r scripts/user_retirement/requirements/base.txt
+ # algoliasearch
+ # analytics-python
+ # cachecontrol
+ # django-oauth-toolkit
+ # edx-bulk-grades
+ # edx-drf-extensions
+ # edx-enterprise
# edx-rest-api-client
+ # geoip2
# google-api-core
+ # google-cloud-storage
# jenkinsapi
+ # mailsnake
+ # meilisearch
# moto
+ # openai
+ # optimizely-sdk
+ # pyjwkest
+ # pylti1p3
+ # python-swiftclient
# requests-file
# requests-mock
+ # requests-oauthlib
# requests-toolbelt
# responses
+ # sailthru-client
# simple-salesforce
# slumber
+ # snowflake-connector-python
+ # social-auth-core
+ # xblock-google-drive
# zeep
requests-file==2.1.0
# via
@@ -224,6 +1261,10 @@ requests-file==2.1.0
# zeep
requests-mock==1.12.1
# via -r scripts/user_retirement/requirements/testing.in
+requests-oauthlib==2.0.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # social-auth-core
requests-toolbelt==1.0.0
# via
# -r scripts/user_retirement/requirements/base.txt
@@ -232,57 +1273,274 @@ responses==0.25.3
# via
# -r scripts/user_retirement/requirements/testing.in
# moto
+rpds-py==0.20.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # jsonschema
+ # referencing
rsa==4.9
# via
# -r scripts/user_retirement/requirements/base.txt
# google-auth
+rules==3.4
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+ # edx-proctoring
+ # openedx-learning
s3transfer==0.10.2
# via
# -r scripts/user_retirement/requirements/base.txt
# boto3
+sailthru-client==2.2.3
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-ace
+scipy==1.14.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # chem
+ # openedx-calc
+semantic-version==2.10.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-drf-extensions
+shapely==2.0.5
+ # via -r scripts/user_retirement/requirements/base.txt
simple-salesforce==1.12.6
# via -r scripts/user_retirement/requirements/base.txt
simplejson==3.19.2
- # via -r scripts/user_retirement/requirements/base.txt
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # sailthru-client
+ # super-csv
+ # xblock
+ # xblock-utils
six==1.16.0
# via
# -r scripts/user_retirement/requirements/base.txt
+ # analytics-python
+ # bleach
+ # codejail-includes
+ # crowdsourcehinter-xblock
+ # edx-ace
+ # edx-auth-backends
+ # edx-ccx-keys
+ # edx-codejail
+ # edx-django-release-util
+ # edx-milestones
+ # edx-rbac
+ # event-tracking
+ # fs
+ # fs-s3fs
+ # html5lib
+ # interchange
# isodate
# jenkinsapi
+ # libsass
+ # optimizely-sdk
+ # pansi
+ # paver
+ # py2neo
+ # pyjwkest
# python-dateutil
slumber==0.7.1
# via
# -r scripts/user_retirement/requirements/base.txt
+ # edx-bulk-grades
+ # edx-enterprise
# edx-rest-api-client
+snowflake-connector-python==3.12.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+social-auth-app-django==5.4.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-auth-backends
+social-auth-core==4.5.4
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-auth-backends
+ # social-auth-app-django
+sorl-thumbnail==12.10.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # openedx-django-wiki
+sortedcontainers==2.4.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # snowflake-connector-python
+soupsieve==2.5
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # beautifulsoup4
sqlparse==0.5.1
# via
# -r scripts/user_retirement/requirements/base.txt
# django
+staff-graded-xblock==2.3.0
+ # via -r scripts/user_retirement/requirements/base.txt
stevedore==5.2.0
# via
# -r scripts/user_retirement/requirements/base.txt
+ # code-annotations
+ # edx-ace
# edx-django-utils
+ # edx-enterprise
+ # edx-opaque-keys
+super-csv==3.2.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-bulk-grades
+sympy==1.13.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # openedx-calc
+testfixtures==8.3.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
+text-unidecode==1.3
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # python-slugify
+tinycss2==1.2.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # bleach
+tomlkit==0.13.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # snowflake-connector-python
+tqdm==4.66.5
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # nltk
+ # openai
typing-extensions==4.12.2
# via
# -r scripts/user_retirement/requirements/base.txt
+ # django-countries
+ # edx-opaque-keys
+ # jwcrypto
+ # pydantic
+ # pydantic-core
+ # pylti1p3
# simple-salesforce
+ # snowflake-connector-python
+tzdata==2024.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # celery
unicodecsv==0.14.1
- # via -r scripts/user_retirement/requirements/base.txt
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-enterprise
uritemplate==4.1.1
# via
# -r scripts/user_retirement/requirements/base.txt
+ # drf-spectacular
+ # drf-yasg
# google-api-python-client
urllib3==1.26.19
# via
# -r scripts/user_retirement/requirements/base.txt
# botocore
+ # elasticsearch
+ # py2neo
# requests
# responses
+user-util==1.1.0
+ # via -r scripts/user_retirement/requirements/base.txt
+vine==5.1.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # amqp
+ # celery
+ # kombu
+voluptuous==0.15.2
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # ora2
+walrus==0.9.4
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-event-bus-redis
+watchdog==4.0.1
+ # via -r scripts/user_retirement/requirements/base.txt
+wcwidth==0.2.13
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # prompt-toolkit
+web-fragments==2.2.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # crowdsourcehinter-xblock
+ # edx-sga
+ # staff-graded-xblock
+ # xblock
+ # xblock-utils
+webencodings==0.5.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # bleach
+ # html5lib
+ # tinycss2
+webob==1.8.7
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # xblock
werkzeug==3.0.3
# via moto
+wrapt==1.16.0
+ # via -r scripts/user_retirement/requirements/base.txt
+xblock[django]==4.0.1
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # acid-xblock
+ # crowdsourcehinter-xblock
+ # done-xblock
+ # edx-completion
+ # edx-sga
+ # edx-when
+ # lti-consumer-xblock
+ # ora2
+ # staff-graded-xblock
+ # xblock-drag-and-drop-v2
+ # xblock-google-drive
+ # xblock-utils
+xblock-drag-and-drop-v2==4.0.3
+ # via -r scripts/user_retirement/requirements/base.txt
+xblock-google-drive==0.7.0
+ # via -r scripts/user_retirement/requirements/base.txt
+xblock-poll==1.14.0
+ # via -r scripts/user_retirement/requirements/base.txt
+xblock-utils==4.0.0
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # edx-sga
+ # xblock-poll
+xmlsec==1.3.13
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # python3-saml
xmltodict==0.13.0
# via moto
+xss-utils==0.6.0
+ # via -r scripts/user_retirement/requirements/base.txt
+yarl==1.9.4
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # aiohttp
zeep==4.2.1
# via
# -r scripts/user_retirement/requirements/base.txt
# simple-salesforce
+zipp==3.19.2
+ # via
+ # -r scripts/user_retirement/requirements/base.txt
+ # importlib-metadata
+
+# The following packages are considered to be unsafe in a requirements file:
+# setuptools
From c9641b35d4a4260383c0da04929fbbdf3b8b13b7 Mon Sep 17 00:00:00 2001
From: Jade Olivier
Date: Wed, 14 Aug 2024 10:20:51 +0200
Subject: [PATCH 042/242] feat: updated user retirement docs
---
.../docs/implementation_overview.rst | 22 +++++++++++--
.../user_retirement/docs/service_setup.rst | 33 ++++++++++++++-----
2 files changed, 45 insertions(+), 10 deletions(-)
diff --git a/scripts/user_retirement/docs/implementation_overview.rst b/scripts/user_retirement/docs/implementation_overview.rst
index 37a814c1d5..b6cf1a5130 100644
--- a/scripts/user_retirement/docs/implementation_overview.rst
+++ b/scripts/user_retirement/docs/implementation_overview.rst
@@ -11,8 +11,8 @@ these services. As a consequence, to remove a user's PII, you must be able
to request each service containing PII to remove, delete, or unlink the
data for that user in that service.
-In the user retirement feature, a centralized process (the *driver* scripts)
-orchestrates all of these requests. For information about how to configure the
+In the user retirement feature, a centralized process (the *driver* scripts)
+orchestrates all of these requests. For information about how to configure the
driver scripts, see :ref:`driver-setup`.
****************************
@@ -46,6 +46,24 @@ table of the states themselves (the ``RetirementState`` model), rather than
hard-coding the states. This was done because we cannot predict all the
possible states required by all members of the Open edX community.
+The workflow also allows the inclusion of additional services that may need to be
+retired alongside the core services. These extra services can now be configured within
+the Django settings under the ``EXTRA_SERVICES_TO_RETIRE_FROM`` setting. This setting allows
+the platform to handle the retirement of PII in custom or internal services that are not
+part of the standard Open edX installation but are crucial for specific deployments.
+
+Here is an example of how the ``EXTRA_SERVICES_TO_RETIRE_FROM`` setting can be configured:
+.. code-block:: python
+ EXTRA_SERVICES_TO_RETIRE_FROM = [
+ {
+ 'name': 'MOCK_SERVICE',
+ 'service_base_url': 'http://fake_service_base_url',
+ 'retirement_url_path': 'fake_retirement_url_path'
+ }
+ ]
+This setting defines a list of services, each with a name, service_base_url, and retirement_url_path,
+allowing the user retirement process to include these additional services.
+
This example state diagram outlines the pathways users follow throughout the
workflow:
diff --git a/scripts/user_retirement/docs/service_setup.rst b/scripts/user_retirement/docs/service_setup.rst
index 4fd59fcfd3..366541a33a 100644
--- a/scripts/user_retirement/docs/service_setup.rst
+++ b/scripts/user_retirement/docs/service_setup.rst
@@ -36,13 +36,13 @@ defining *derived* settings specific to Open edX. Read more about it in
- ``'retired.invalid'``
- The domain part of hashed emails. Used in ``RETIRED_EMAIL_FMT``.
* - RETIRED_USERNAME_FMT
- - ``lambda settings:
+ - ``lambda settings:
settings.RETIRED_USERNAME_PREFIX + '{}'``
- The username field for a retired user gets transformed into this format,
where ``{}`` is replaced with the hash of their username.
* - RETIRED_EMAIL_FMT
- - ``lambda settings:
- settings.RETIRED_EMAIL_PREFIX + '{}@' +
+ - ``lambda settings:
+ settings.RETIRED_EMAIL_PREFIX + '{}@' +
settings.RETIRED_EMAIL_DOMAIN``
- The email field for a retired user gets transformed into this format, where
``{}`` is replaced with the hash of their email.
@@ -60,6 +60,23 @@ defining *derived* settings specific to Open edX. Read more about it in
* - FEATURES['ENABLE_ACCOUNT_DELETION']
- True
- Whether to display the "Delete My Account" section the account settings page.
+ * - EXTRA_SERVICES_TO_RETIRE_FROM
+ - None
+ - A list of additional services from which user data should be retired. Each entry in the list should be a dictionary with the following keys:
+ - ``name``: The name of the service.
+ - ``service_base_url``: The base URL of the service's API.
+ - ``retirement_url_path``: The API path for the user retirement endpoint.
+ This setting allows the retirement process to interact with external services not covered by default. For example:
+ .. code-block:: python
+ EXTRA_SERVICES_TO_RETIRE_FROM = [
+ {
+ 'name': 'MOCK_SERVICE',
+ 'service_base_url': 'http://fake_service_base_url',
+ 'retirement_url_path': 'fake_retirement_url_path'
+ },
+ # Add more services as needed
+ ]
+ By default, this setting is empty, meaning no additional services are included. This can be overridden in `https://github.com/openedx/edx-platform/blob/master/lms/envs/common.py.
=================
@@ -79,10 +96,10 @@ state at the beginning, and ``COMPLETED``, ``ERRORED``, and ``ABORTED`` states
at the end of the list. Also, for every ``RETIRING_foo`` state, there must be
a corresponding ``foo_COMPLETE`` state.
-Override these states if you need to add any states. Typically, these
+Override these states if you need to add any states. Typically, these
settings are set in ``lms.yml``.
-After you have defined any custom states, populate the states table with the
+After you have defined any custom states, populate the states table with the
following management command:
.. code-block:: bash
@@ -120,7 +137,7 @@ Retirement Service User
The user retirement driver scripts authenticate with the LMS and IDAs as the
retirement service user with oauth client credentials. Therefore, to use the
-driver scripts, you must create a retirement service user, and generate a DOT
+driver scripts, you must create a retirement service user, and generate a DOT
application and client credentials, as in the following command.
.. code-block:: bash
@@ -162,7 +179,7 @@ that relate to user retirement.
- ``/admin/user_api/retirementstate/``
- Represents the table of states defined in ``RETIREMENT_STATES`` and
populated with ``populate_retirement_states``.
- * - User Retirement Requests
+ * - User Retirement Requests
- ``/admin/user_api/userretirementrequest/``
- Represents the table that tracks the user IDs of every learner who
has ever requested account deletion. This table is primarily used for
@@ -173,7 +190,7 @@ that relate to user retirement.
In special cases where you may need to manually intervene with the pipeline,
you can use the User Retirement Statuses management page to change the
-state for an individual user. For more information about how to handle these
+state for an individual user. For more information about how to handle these
cases, see :ref:`handling-special-cases`.
.. include:: ../../../../links/links.rst
From c06416bed78c8607a83fbe35ea26cc020db70231 Mon Sep 17 00:00:00 2001
From: Jade Olivier
Date: Wed, 14 Aug 2024 10:32:31 +0200
Subject: [PATCH 043/242] fix: dependencies again
---
.../docs/implementation_overview.rst | 9 ++++---
.../user_retirement/docs/service_setup.rst | 26 +++++++++++--------
scripts/user_retirement/requirements/base.txt | 2 +-
.../user_retirement/requirements/testing.txt | 2 +-
4 files changed, 23 insertions(+), 16 deletions(-)
diff --git a/scripts/user_retirement/docs/implementation_overview.rst b/scripts/user_retirement/docs/implementation_overview.rst
index b6cf1a5130..902732a441 100644
--- a/scripts/user_retirement/docs/implementation_overview.rst
+++ b/scripts/user_retirement/docs/implementation_overview.rst
@@ -53,15 +53,18 @@ the platform to handle the retirement of PII in custom or internal services that
part of the standard Open edX installation but are crucial for specific deployments.
Here is an example of how the ``EXTRA_SERVICES_TO_RETIRE_FROM`` setting can be configured:
+
.. code-block:: python
- EXTRA_SERVICES_TO_RETIRE_FROM = [
+
+ EXTRA_SERVICES_TO_RETIRE_FROM = [
{
'name': 'MOCK_SERVICE',
'service_base_url': 'http://fake_service_base_url',
'retirement_url_path': 'fake_retirement_url_path'
}
- ]
-This setting defines a list of services, each with a name, service_base_url, and retirement_url_path,
+ ]
+
+This setting defines a list of services, each with a ``name``, ``service_base_url``, and ``retirement_url_path``,
allowing the user retirement process to include these additional services.
This example state diagram outlines the pathways users follow throughout the
diff --git a/scripts/user_retirement/docs/service_setup.rst b/scripts/user_retirement/docs/service_setup.rst
index 366541a33a..58a0f41001 100644
--- a/scripts/user_retirement/docs/service_setup.rst
+++ b/scripts/user_retirement/docs/service_setup.rst
@@ -63,20 +63,24 @@ defining *derived* settings specific to Open edX. Read more about it in
* - EXTRA_SERVICES_TO_RETIRE_FROM
- None
- A list of additional services from which user data should be retired. Each entry in the list should be a dictionary with the following keys:
+
- ``name``: The name of the service.
- ``service_base_url``: The base URL of the service's API.
- ``retirement_url_path``: The API path for the user retirement endpoint.
- This setting allows the retirement process to interact with external services not covered by default. For example:
- .. code-block:: python
- EXTRA_SERVICES_TO_RETIRE_FROM = [
- {
- 'name': 'MOCK_SERVICE',
- 'service_base_url': 'http://fake_service_base_url',
- 'retirement_url_path': 'fake_retirement_url_path'
- },
- # Add more services as needed
- ]
- By default, this setting is empty, meaning no additional services are included. This can be overridden in `https://github.com/openedx/edx-platform/blob/master/lms/envs/common.py.
+
+ This setting allows the retirement process to interact with external services not covered by default. For example:
+
+ .. code-block:: python
+
+ EXTRA_SERVICES_TO_RETIRE_FROM = [
+ {
+ 'name': 'MOCK_SERVICE',
+ 'service_base_url': 'http://fake_service_base_url',
+ 'retirement_url_path': 'fake_retirement_url_path'
+ }
+ ]
+
+ By default, this setting is empty, meaning no additional services are included. This can be overridden in `common.py `_.
=================
diff --git a/scripts/user_retirement/requirements/base.txt b/scripts/user_retirement/requirements/base.txt
index b0a649c675..f57f1dad43 100644
--- a/scripts/user_retirement/requirements/base.txt
+++ b/scripts/user_retirement/requirements/base.txt
@@ -534,7 +534,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.4
+edx-enterprise==4.23.8
# via
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
# -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
diff --git a/scripts/user_retirement/requirements/testing.txt b/scripts/user_retirement/requirements/testing.txt
index 3fb96783ca..6231b3bb36 100644
--- a/scripts/user_retirement/requirements/testing.txt
+++ b/scripts/user_retirement/requirements/testing.txt
@@ -526,7 +526,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.4
+edx-enterprise==4.23.8
# via -r scripts/user_retirement/requirements/base.txt
edx-event-bus-kafka==5.8.1
# via -r scripts/user_retirement/requirements/base.txt
From b65550c79629b572bd2fab783f05f517c02f8f1b Mon Sep 17 00:00:00 2001
From: Jade Olivier
Date: Wed, 14 Aug 2024 13:48:54 +0200
Subject: [PATCH 044/242] fix: change settings config to empty list not dict
---
lms/envs/common.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 1057c798f6..980aba1e83 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -4996,7 +4996,7 @@ RETIREMENT_STATES = [
'COMPLETE',
]
-EXTRA_SERVICES_TO_RETIRE_FROM = {}
+EXTRA_SERVICES_TO_RETIRE_FROM = []
USERNAME_REPLACEMENT_WORKER = "REPLACE WITH VALID USERNAME"
From e4855536fd828095c39af0f215cc3b7436b68b04 Mon Sep 17 00:00:00 2001
From: Justin Hynes
Date: Thu, 15 Aug 2024 12:28:52 -0400
Subject: [PATCH 045/242] Revert: revert generic retirement update (#35317)
* Revert "fix: change settings config to empty list not dict"
This reverts commit b65550c79629b572bd2fab783f05f517c02f8f1b.
* Revert "fix: dependencies again"
This reverts commit c06416bed78c8607a83fbe35ea26cc020db70231.
* Revert "feat: updated user retirement docs"
This reverts commit c9641b35d4a4260383c0da04929fbbdf3b8b13b7.
* Revert "fix: install dependencies"
This reverts commit a5442b2409b0e896fd0bb0d2765a5cdcf593d161.
* Revert "Revert "fix: dependencies""
This reverts commit 4cde950007b022718123f54a2685d74e956f527d.
* Revert "fix: dependencies"
This reverts commit 8a1c30ebc517457ce274167d32bdde34440e210f.
* Revert "fix: Add CI update for tests"
This reverts commit 64098b6dabb04f2788840b1e388299bb2a62c943.
* Revert "fix: tests"
This reverts commit 5e636dea169cbb6aeafd6a3f4a9f9f976810f320.
* Revert "fix: generalize internal services"
This reverts commit e8f9db428d6bd41bd17b3ae4bc4ff18fa2fff152.
* Revert "fix: quality"
This reverts commit 77ca0f754af322b918434960f74948665d0847e1.
* Revert "feat: Commerce Coordinator step in retirement pipeline"
This reverts commit c24c87499f75893fbb6b35b2573f361c84a142ab.
---
.../units-test-scripts-user-retirement.yml | 2 -
lms/envs/common.py | 2 -
lms/envs/test.py | 8 -
.../docs/implementation_overview.rst | 25 +-
.../user_retirement/docs/service_setup.rst | 37 +-
scripts/user_retirement/pytest.ini | 3 -
scripts/user_retirement/requirements/base.in | 1 -
scripts/user_retirement/requirements/base.txt | 1461 +----------------
.../user_retirement/requirements/testing.txt | 1278 +-------------
.../tests/test_retire_one_learner.py | 18 +-
.../tests/utils/test_edx_api.py | 41 -
scripts/user_retirement/utils/edx_api.py | 18 -
scripts/user_retirement/utils/helpers.py | 34 +-
13 files changed, 79 insertions(+), 2849 deletions(-)
diff --git a/.github/workflows/units-test-scripts-user-retirement.yml b/.github/workflows/units-test-scripts-user-retirement.yml
index e9b7c84c8a..f1b2b2c539 100644
--- a/.github/workflows/units-test-scripts-user-retirement.yml
+++ b/.github/workflows/units-test-scripts-user-retirement.yml
@@ -26,11 +26,9 @@ jobs:
- name: Install dependencies
run: |
- sudo apt-get update && sudo apt-get install libxmlsec1-dev
python -m pip install --upgrade pip
pip install -r scripts/user_retirement/requirements/testing.txt
- name: Run pytest
run: |
- export DJANGO_SETTINGS_MODULE=lms.envs.test
pytest scripts/user_retirement
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 280aaf37bf..18d07afd71 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -5003,8 +5003,6 @@ RETIREMENT_STATES = [
'COMPLETE',
]
-EXTRA_SERVICES_TO_RETIRE_FROM = []
-
USERNAME_REPLACEMENT_WORKER = "REPLACE WITH VALID USERNAME"
############## Settings for Microfrontends #########################
diff --git a/lms/envs/test.py b/lms/envs/test.py
index 9d94175980..3c4bb95649 100644
--- a/lms/envs/test.py
+++ b/lms/envs/test.py
@@ -662,14 +662,6 @@ SUBSCRIPTIONS_TRIAL_LENGTH = 7
CSRF_TRUSTED_ORIGINS = ['.example.com']
CSRF_TRUSTED_ORIGINS_WITH_SCHEME = ['https://*.example.com']
-EXTRA_SERVICES_TO_RETIRE_FROM = [
- {
- 'name': 'MOCK_SERVICE',
- 'service_base_url': 'http://fake_service_base_url',
- 'retirement_url_path': 'fake_retirement_url_path'
- }
-]
-
# values are already updated above with default CSRF_TRUSTED_ORIGINS values but in
# case of new django version these values will override.
if django.VERSION[0] >= 4: # for greater than django 3.2 use with schemes.
diff --git a/scripts/user_retirement/docs/implementation_overview.rst b/scripts/user_retirement/docs/implementation_overview.rst
index 902732a441..37a814c1d5 100644
--- a/scripts/user_retirement/docs/implementation_overview.rst
+++ b/scripts/user_retirement/docs/implementation_overview.rst
@@ -11,8 +11,8 @@ these services. As a consequence, to remove a user's PII, you must be able
to request each service containing PII to remove, delete, or unlink the
data for that user in that service.
-In the user retirement feature, a centralized process (the *driver* scripts)
-orchestrates all of these requests. For information about how to configure the
+In the user retirement feature, a centralized process (the *driver* scripts)
+orchestrates all of these requests. For information about how to configure the
driver scripts, see :ref:`driver-setup`.
****************************
@@ -46,27 +46,6 @@ table of the states themselves (the ``RetirementState`` model), rather than
hard-coding the states. This was done because we cannot predict all the
possible states required by all members of the Open edX community.
-The workflow also allows the inclusion of additional services that may need to be
-retired alongside the core services. These extra services can now be configured within
-the Django settings under the ``EXTRA_SERVICES_TO_RETIRE_FROM`` setting. This setting allows
-the platform to handle the retirement of PII in custom or internal services that are not
-part of the standard Open edX installation but are crucial for specific deployments.
-
-Here is an example of how the ``EXTRA_SERVICES_TO_RETIRE_FROM`` setting can be configured:
-
-.. code-block:: python
-
- EXTRA_SERVICES_TO_RETIRE_FROM = [
- {
- 'name': 'MOCK_SERVICE',
- 'service_base_url': 'http://fake_service_base_url',
- 'retirement_url_path': 'fake_retirement_url_path'
- }
- ]
-
-This setting defines a list of services, each with a ``name``, ``service_base_url``, and ``retirement_url_path``,
-allowing the user retirement process to include these additional services.
-
This example state diagram outlines the pathways users follow throughout the
workflow:
diff --git a/scripts/user_retirement/docs/service_setup.rst b/scripts/user_retirement/docs/service_setup.rst
index 58a0f41001..4fd59fcfd3 100644
--- a/scripts/user_retirement/docs/service_setup.rst
+++ b/scripts/user_retirement/docs/service_setup.rst
@@ -36,13 +36,13 @@ defining *derived* settings specific to Open edX. Read more about it in
- ``'retired.invalid'``
- The domain part of hashed emails. Used in ``RETIRED_EMAIL_FMT``.
* - RETIRED_USERNAME_FMT
- - ``lambda settings:
+ - ``lambda settings:
settings.RETIRED_USERNAME_PREFIX + '{}'``
- The username field for a retired user gets transformed into this format,
where ``{}`` is replaced with the hash of their username.
* - RETIRED_EMAIL_FMT
- - ``lambda settings:
- settings.RETIRED_EMAIL_PREFIX + '{}@' +
+ - ``lambda settings:
+ settings.RETIRED_EMAIL_PREFIX + '{}@' +
settings.RETIRED_EMAIL_DOMAIN``
- The email field for a retired user gets transformed into this format, where
``{}`` is replaced with the hash of their email.
@@ -60,27 +60,6 @@ defining *derived* settings specific to Open edX. Read more about it in
* - FEATURES['ENABLE_ACCOUNT_DELETION']
- True
- Whether to display the "Delete My Account" section the account settings page.
- * - EXTRA_SERVICES_TO_RETIRE_FROM
- - None
- - A list of additional services from which user data should be retired. Each entry in the list should be a dictionary with the following keys:
-
- - ``name``: The name of the service.
- - ``service_base_url``: The base URL of the service's API.
- - ``retirement_url_path``: The API path for the user retirement endpoint.
-
- This setting allows the retirement process to interact with external services not covered by default. For example:
-
- .. code-block:: python
-
- EXTRA_SERVICES_TO_RETIRE_FROM = [
- {
- 'name': 'MOCK_SERVICE',
- 'service_base_url': 'http://fake_service_base_url',
- 'retirement_url_path': 'fake_retirement_url_path'
- }
- ]
-
- By default, this setting is empty, meaning no additional services are included. This can be overridden in `common.py `_.
=================
@@ -100,10 +79,10 @@ state at the beginning, and ``COMPLETED``, ``ERRORED``, and ``ABORTED`` states
at the end of the list. Also, for every ``RETIRING_foo`` state, there must be
a corresponding ``foo_COMPLETE`` state.
-Override these states if you need to add any states. Typically, these
+Override these states if you need to add any states. Typically, these
settings are set in ``lms.yml``.
-After you have defined any custom states, populate the states table with the
+After you have defined any custom states, populate the states table with the
following management command:
.. code-block:: bash
@@ -141,7 +120,7 @@ Retirement Service User
The user retirement driver scripts authenticate with the LMS and IDAs as the
retirement service user with oauth client credentials. Therefore, to use the
-driver scripts, you must create a retirement service user, and generate a DOT
+driver scripts, you must create a retirement service user, and generate a DOT
application and client credentials, as in the following command.
.. code-block:: bash
@@ -183,7 +162,7 @@ that relate to user retirement.
- ``/admin/user_api/retirementstate/``
- Represents the table of states defined in ``RETIREMENT_STATES`` and
populated with ``populate_retirement_states``.
- * - User Retirement Requests
+ * - User Retirement Requests
- ``/admin/user_api/userretirementrequest/``
- Represents the table that tracks the user IDs of every learner who
has ever requested account deletion. This table is primarily used for
@@ -194,7 +173,7 @@ that relate to user retirement.
In special cases where you may need to manually intervene with the pipeline,
you can use the User Retirement Statuses management page to change the
-state for an individual user. For more information about how to handle these
+state for an individual user. For more information about how to handle these
cases, see :ref:`handling-special-cases`.
.. include:: ../../../../links/links.rst
diff --git a/scripts/user_retirement/pytest.ini b/scripts/user_retirement/pytest.ini
index 7a127196d0..e69de29bb2 100644
--- a/scripts/user_retirement/pytest.ini
+++ b/scripts/user_retirement/pytest.ini
@@ -1,3 +0,0 @@
-[pytest]
-
-DJANGO_SETTINGS_MODULE = lms.envs.test
diff --git a/scripts/user_retirement/requirements/base.in b/scripts/user_retirement/requirements/base.in
index ee037a58b5..d0bdab9082 100644
--- a/scripts/user_retirement/requirements/base.in
+++ b/scripts/user_retirement/requirements/base.in
@@ -1,5 +1,4 @@
-c ../../../requirements/constraints.txt
--r ../../../requirements/edx/base.txt
boto3
click
diff --git a/scripts/user_retirement/requirements/base.txt b/scripts/user_retirement/requirements/base.txt
index f57f1dad43..47e6e79c22 100644
--- a/scripts/user_retirement/requirements/base.txt
+++ b/scripts/user_retirement/requirements/base.txt
@@ -4,1545 +4,168 @@
#
# make upgrade
#
--e git+https://github.com/anupdhabarde/edx-proctoring-proctortrack.git@31c6c9923a51c903ae83760ecbbac191363aa2a2#egg=edx_proctoring_proctortrack
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-acid-xblock==0.3.1
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-aiohappyeyeballs==2.3.4
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # aiohttp
-aiohttp==3.10.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # geoip2
- # openai
-aiosignal==1.3.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # aiohttp
-algoliasearch==3.0.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-amqp==5.2.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # kombu
-analytics-python==1.4.post1
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-aniso8601==9.0.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-tincan-py35
-annotated-types==0.7.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # pydantic
-appdirs==1.4.4
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # fs
asgiref==3.8.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # django
- # django-cors-headers
- # django-countries
-asn1crypto==1.5.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # snowflake-connector-python
+ # via django
attrs==24.2.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # aiohttp
- # edx-ace
- # jsonschema
- # lti-consumer-xblock
- # openedx-events
- # openedx-learning
- # referencing
- # zeep
-babel==2.15.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # enmerkar
- # enmerkar-underscore
-backoff==1.10.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # -r scripts/user_retirement/requirements/base.in
- # analytics-python
-bcrypt==4.2.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # paramiko
-beautifulsoup4==4.12.3
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # pynliner
-billiard==4.2.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # celery
-bleach[css]==6.1.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
- # lti-consumer-xblock
- # openedx-django-wiki
- # ora2
- # xblock-drag-and-drop-v2
- # xblock-poll
-boto==2.49.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # via zeep
+backoff==2.2.1
+ # via -r scripts/user_retirement/requirements/base.in
boto3==1.34.154
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # -r scripts/user_retirement/requirements/base.in
- # django-ses
- # fs-s3fs
- # ora2
+ # via -r scripts/user_retirement/requirements/base.in
botocore==1.34.154
# via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# boto3
# s3transfer
-bridgekeeper==0.9
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-cachecontrol==0.14.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # firebase-admin
cachetools==5.4.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # google-auth
-camel-converter[pydantic]==3.1.2
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # meilisearch
-celery==5.4.0
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # django-celery-results
- # django-user-tasks
- # edx-celeryutils
- # edx-enterprise
- # event-tracking
- # openedx-learning
+ # via google-auth
certifi==2024.7.4
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # elasticsearch
- # py2neo
- # requests
- # snowflake-connector-python
+ # via requests
cffi==1.17.0
# via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# cryptography
# pynacl
- # snowflake-connector-python
-chardet==5.2.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # pysrt
charset-normalizer==2.0.12
# via
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# requests
- # snowflake-connector-python
-chem==1.3.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
click==8.1.6
# via
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# -r scripts/user_retirement/requirements/base.in
- # celery
- # click-didyoumean
- # click-plugins
- # click-repl
- # code-annotations
# edx-django-utils
- # nltk
- # user-util
-click-didyoumean==0.3.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # celery
-click-plugins==1.1.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # celery
-click-repl==0.3.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # celery
-code-annotations==1.8.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
- # edx-toggles
-codejail-includes==1.0.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-crowdsourcehinter-xblock==0.7
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-cryptography==42.0.8
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # django-fernet-fields-v2
- # edx-enterprise
- # jwcrypto
- # optimizely-sdk
- # paramiko
- # pgpy
- # pyjwt
- # pyopenssl
- # snowflake-connector-python
- # social-auth-core
-cssutils==2.11.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # pynliner
-defusedxml==0.7.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # djangorestframework-xml
- # ora2
- # python3-openid
- # social-auth-core
+cryptography==43.0.0
+ # via pyjwt
django==4.2.15
# via
# -c scripts/user_retirement/requirements/../../../requirements/common_constraints.txt
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # django-appconf
- # django-celery-results
- # django-classy-tags
- # django-config-models
- # django-cors-headers
# django-crum
- # django-fernet-fields-v2
- # django-filter
- # django-js-asset
- # django-method-override
- # django-model-utils
- # django-multi-email-field
- # django-mysql
- # django-oauth-toolkit
- # django-push-notifications
- # django-sekizai
- # django-ses
- # django-statici18n
- # django-storages
- # django-user-tasks
# django-waffle
- # djangorestframework
- # done-xblock
- # drf-jwt
- # drf-spectacular
- # drf-yasg
- # edx-ace
- # edx-api-doc-tools
- # edx-auth-backends
- # edx-bulk-grades
- # edx-celeryutils
- # edx-completion
- # edx-django-release-util
- # edx-django-sites-extensions
# edx-django-utils
- # edx-drf-extensions
- # edx-enterprise
- # edx-event-bus-kafka
- # edx-event-bus-redis
- # edx-i18n-tools
- # edx-milestones
- # edx-name-affirmation
- # edx-organizations
- # edx-proctoring
- # edx-rbac
- # edx-search
- # edx-submissions
- # edx-toggles
- # edx-token-utils
- # edx-when
- # edxval
- # enmerkar
- # enmerkar-underscore
- # event-tracking
- # help-tokens
- # jsonfield
- # lti-consumer-xblock
- # openedx-django-pyfs
- # openedx-django-wiki
- # openedx-events
- # openedx-filters
- # openedx-learning
- # ora2
- # social-auth-app-django
- # super-csv
- # xblock-google-drive
- # xss-utils
-django-appconf==1.0.6
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # django-statici18n
-django-cache-memoize==0.2.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
-django-celery-results==2.5.1
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-django-classy-tags==4.1.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # django-sekizai
-django-config-models==2.7.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
- # edx-name-affirmation
- # lti-consumer-xblock
-django-cors-headers==4.4.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-django-countries==7.6.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
django-crum==0.7.9
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-django-utils
- # edx-enterprise
- # edx-proctoring
- # edx-rbac
- # edx-toggles
- # super-csv
-django-fernet-fields-v2==0.9
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
-django-filter==24.3
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
- # lti-consumer-xblock
-django-ipware==7.0.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
- # edx-proctoring
-django-js-asset==2.2.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # django-mptt
-django-method-override==1.0.4
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-django-model-utils==4.5.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # django-user-tasks
- # edx-bulk-grades
- # edx-celeryutils
- # edx-completion
- # edx-enterprise
- # edx-milestones
- # edx-name-affirmation
- # edx-organizations
- # edx-proctoring
- # edx-rbac
- # edx-submissions
- # edx-when
- # edxval
- # ora2
- # super-csv
-django-mptt==0.16.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # openedx-django-wiki
-django-multi-email-field==0.7.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
-django-mysql==4.14.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-django-oauth-toolkit==1.7.1
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
-django-object-actions==4.2.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
-django-pipeline==3.1.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-django-push-notifications==3.1.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-ace
-django-ratelimit==4.1.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-django-sekizai==4.1.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # openedx-django-wiki
-django-ses==4.1.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-django-simple-history==3.4.0
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
- # edx-name-affirmation
- # edx-organizations
- # edx-proctoring
- # ora2
-django-statici18n==2.5.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # lti-consumer-xblock
- # xblock-drag-and-drop-v2
- # xblock-poll
-django-storages==1.14.3
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edxval
-django-user-tasks==3.2.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # via edx-django-utils
django-waffle==4.1.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-django-utils
- # edx-drf-extensions
- # edx-enterprise
- # edx-proctoring
- # edx-toggles
-django-webpack-loader==0.7.0
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-proctoring
-djangorestframework==3.14.0
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # django-config-models
- # django-user-tasks
- # drf-jwt
- # drf-spectacular
- # drf-yasg
- # edx-api-doc-tools
- # edx-completion
- # edx-drf-extensions
- # edx-enterprise
- # edx-name-affirmation
- # edx-organizations
- # edx-proctoring
- # edx-submissions
- # openedx-learning
- # ora2
- # super-csv
-djangorestframework-xml==2.0.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
-dnspython==2.6.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # pymongo
-done-xblock==2.3.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-drf-jwt==1.19.2
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-drf-extensions
-drf-spectacular==0.27.2
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-drf-yasg==1.21.7
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # django-user-tasks
- # edx-api-doc-tools
-edx-ace==1.11.1
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-edx-api-doc-tools==1.8.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-name-affirmation
-edx-auth-backends==4.3.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-edx-braze-client==0.2.5
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
-edx-bulk-grades==1.1.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # staff-graded-xblock
-edx-ccx-keys==1.3.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # lti-consumer-xblock
- # openedx-events
-edx-celeryutils==1.3.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-name-affirmation
- # super-csv
-edx-codejail==3.4.1
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-edx-completion==4.6.7
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-edx-django-release-util==1.4.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-submissions
- # edxval
-edx-django-sites-extensions==4.2.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # via edx-django-utils
edx-django-utils==5.15.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # django-config-models
- # edx-ace
- # edx-drf-extensions
- # edx-enterprise
- # edx-event-bus-kafka
- # edx-event-bus-redis
- # edx-name-affirmation
- # edx-rest-api-client
- # edx-toggles
- # edx-when
- # event-tracking
- # openedx-events
- # ora2
- # super-csv
-edx-drf-extensions==10.3.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-completion
- # edx-enterprise
- # edx-name-affirmation
- # edx-organizations
- # edx-proctoring
- # edx-rbac
- # edx-when
- # edxval
- # openedx-learning
-edx-enterprise==4.23.8
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-edx-event-bus-kafka==5.8.1
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-edx-event-bus-redis==0.5.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-edx-i18n-tools==1.5.0
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # ora2
-edx-milestones==0.6.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-edx-name-affirmation==2.3.7
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-edx-opaque-keys[django]==2.10.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-bulk-grades
- # edx-ccx-keys
- # edx-completion
- # edx-drf-extensions
- # edx-enterprise
- # edx-milestones
- # edx-organizations
- # edx-proctoring
- # edx-when
- # lti-consumer-xblock
- # openedx-events
- # ora2
-edx-organizations==6.13.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-edx-proctoring==4.18.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-proctoring-proctortrack
-edx-rbac==1.9.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
+ # via edx-rest-api-client
edx-rest-api-client==5.7.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # -r scripts/user_retirement/requirements/base.in
- # edx-enterprise
- # edx-proctoring
-edx-search==4.0.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-edx-sga==0.25.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-edx-submissions==3.7.6
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # ora2
-edx-tincan-py35==1.0.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
-edx-toggles==5.2.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-completion
- # edx-enterprise
- # edx-event-bus-kafka
- # edx-event-bus-redis
- # edx-name-affirmation
- # edx-search
- # edxval
- # event-tracking
- # ora2
-edx-token-utils==0.2.1
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-edx-when==2.5.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-proctoring
-edxval==2.5.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-elasticsearch==7.13.4
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/common_constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-search
-enmerkar==0.7.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # enmerkar-underscore
-enmerkar-underscore==2.3.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-event-tracking==3.0.0
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-completion
- # edx-proctoring
- # edx-search
-fastavro==1.9.5
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # openedx-events
-filelock==3.15.4
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # snowflake-connector-python
-firebase-admin==6.5.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-ace
-frozenlist==1.4.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # aiohttp
- # aiosignal
-fs==2.0.27
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # fs-s3fs
- # openedx-django-pyfs
- # xblock
-fs-s3fs==0.1.8
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # openedx-django-pyfs
-future==1.0.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # pyjwkest
-geoip2==4.8.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-glob2==0.7
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-google-api-core[grpc]==2.19.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # firebase-admin
- # google-api-python-client
- # google-cloud-core
- # google-cloud-firestore
- # google-cloud-storage
+ # via -r scripts/user_retirement/requirements/base.in
+google-api-core==2.19.1
+ # via google-api-python-client
google-api-python-client==2.139.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # -r scripts/user_retirement/requirements/base.in
- # firebase-admin
+ # via -r scripts/user_retirement/requirements/base.in
google-auth==2.32.0
# via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# google-api-core
# google-api-python-client
# google-auth-httplib2
- # google-cloud-core
- # google-cloud-firestore
- # google-cloud-storage
google-auth-httplib2==0.2.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # google-api-python-client
-google-cloud-core==2.4.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # google-cloud-firestore
- # google-cloud-storage
-google-cloud-firestore==2.17.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # firebase-admin
-google-cloud-storage==2.18.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # firebase-admin
-google-crc32c==1.5.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # google-cloud-storage
- # google-resumable-media
-google-resumable-media==2.7.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # google-cloud-storage
+ # via google-api-python-client
googleapis-common-protos==1.63.2
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # google-api-core
- # grpcio-status
-grpcio==1.65.4
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # google-api-core
- # grpcio-status
-grpcio-status==1.62.3
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # google-api-core
-gunicorn==22.0.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-help-tokens==2.4.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-html5lib==1.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # ora2
+ # via google-api-core
httplib2==0.22.0
# via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# google-api-python-client
# google-auth-httplib2
-icalendar==5.0.13
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
idna==3.7
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # optimizely-sdk
- # requests
- # snowflake-connector-python
- # yarl
-importlib-metadata==6.11.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-inflection==0.5.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # drf-spectacular
- # drf-yasg
-interchange==2021.0.4
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # py2neo
-ipaddress==1.0.23
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # via requests
isodate==0.6.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # python3-saml
- # zeep
+ # via zeep
jenkinsapi==0.3.13
# via -r scripts/user_retirement/requirements/base.in
-jinja2==3.1.4
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # code-annotations
jmespath==1.0.1
# via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# boto3
# botocore
-joblib==1.4.2
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # nltk
-jsondiff==2.2.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
-jsonfield==3.1.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-celeryutils
- # edx-enterprise
- # edx-proctoring
- # edx-submissions
- # lti-consumer-xblock
- # ora2
-jsonschema==4.23.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # drf-spectacular
- # optimizely-sdk
-jsonschema-specifications==2023.12.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # jsonschema
-jwcrypto==1.5.6
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # django-oauth-toolkit
- # pylti1p3
-kombu==5.4.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # celery
-laboratory==1.0.2
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-lazy==1.6
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # acid-xblock
- # lti-consumer-xblock
- # ora2
- # xblock
-libsass==0.10.0
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-loremipsum==1.0.5
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # ora2
-lti-consumer-xblock==9.11.3
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
lxml==4.9.4
# via
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-i18n-tools
- # edxval
- # lti-consumer-xblock
- # olxcleaner
- # openedx-calc
- # ora2
- # python3-saml
- # xblock
- # xmlsec
# zeep
-mailsnake==1.6.4
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-mako==1.3.5
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # acid-xblock
- # lti-consumer-xblock
- # xblock
- # xblock-utils
-markdown==3.3.7
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # openedx-django-wiki
- # staff-graded-xblock
- # xblock-poll
-markupsafe==2.1.5
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # chem
- # jinja2
- # mako
- # openedx-calc
- # xblock
-maxminddb==2.6.2
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # geoip2
-meilisearch==0.31.4
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-mock==5.1.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-mongoengine==0.28.2
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-monotonic==1.6
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # analytics-python
- # py2neo
more-itertools==10.3.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # cssutils
- # simple-salesforce
-mpmath==1.3.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # sympy
-msgpack==1.0.8
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # cachecontrol
-multidict==6.0.5
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # aiohttp
- # yarl
-mysqlclient==2.2.4
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # via simple-salesforce
newrelic==9.12.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-django-utils
-nh3==0.2.18
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-nltk==3.8.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # chem
-nodeenv==1.9.1
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-numpy==1.26.4
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # chem
- # openedx-calc
- # scipy
- # shapely
-oauthlib==3.2.2
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # django-oauth-toolkit
- # lti-consumer-xblock
- # requests-oauthlib
- # social-auth-core
-olxcleaner==0.3.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-openai==0.28.1
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
-openedx-atlas==0.6.1
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-openedx-calc==3.1.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-openedx-django-pyfs==3.6.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # lti-consumer-xblock
- # xblock
-openedx-django-require==2.1.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-openedx-django-wiki==2.1.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-openedx-events==9.11.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-event-bus-kafka
- # edx-event-bus-redis
- # event-tracking
- # ora2
-openedx-filters==1.9.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # lti-consumer-xblock
- # ora2
-openedx-learning==0.10.1
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-openedx-mongodbproxy==0.2.1
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-optimizely-sdk==4.1.1
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-ora2==6.11.2
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-packaging==24.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # drf-yasg
- # gunicorn
- # py2neo
- # snowflake-connector-python
-pansi==2020.7.3
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # py2neo
-paramiko==3.4.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
-path==16.11.0
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-i18n-tools
- # path-py
-path-py==12.5.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
- # ora2
- # staff-graded-xblock
-paver==1.3.4
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # via edx-django-utils
pbr==6.0.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # stevedore
-pgpy==0.6.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
-piexif==1.1.3
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-pillow==10.4.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
- # edx-organizations
- # edxval
+ # via stevedore
platformdirs==4.2.2
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # snowflake-connector-python
- # zeep
-polib==1.2.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-i18n-tools
-prompt-toolkit==3.0.47
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # click-repl
+ # via zeep
proto-plus==1.24.0
+ # via google-api-core
+protobuf==5.27.3
# via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# google-api-core
- # google-cloud-firestore
-protobuf==4.25.4
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # google-api-core
- # google-cloud-firestore
# googleapis-common-protos
- # grpcio-status
# proto-plus
psutil==6.0.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-django-utils
-py2neo @ https://github.com/overhangio/py2neo/releases/download/2021.2.3/py2neo-2021.2.3.tar.gz
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # via edx-django-utils
pyasn1==0.6.0
# via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # pgpy
# pyasn1-modules
# rsa
pyasn1-modules==0.4.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # google-auth
-pycountry==24.6.1
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # via google-auth
pycparser==2.22
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # cffi
-pycryptodomex==3.20.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-proctoring
- # lti-consumer-xblock
- # pyjwkest
-pydantic==2.8.2
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # camel-converter
-pydantic-core==2.20.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # pydantic
-pygments==2.18.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # py2neo
-pyjwkest==1.4.2
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-token-utils
- # lti-consumer-xblock
+ # via cffi
pyjwt[crypto]==2.9.0
# via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # drf-jwt
- # edx-auth-backends
- # edx-drf-extensions
- # edx-proctoring
# edx-rest-api-client
- # firebase-admin
- # pylti1p3
# simple-salesforce
- # snowflake-connector-python
- # social-auth-core
-pylatexenc==2.10
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # olxcleaner
-pylti1p3==2.0.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-pymemcache==4.0.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-pymongo==4.4.0
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-opaque-keys
- # event-tracking
- # mongoengine
- # openedx-mongodbproxy
pynacl==1.5.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-django-utils
- # paramiko
-pynliner==0.8.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-pyopenssl==24.2.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # optimizely-sdk
- # snowflake-connector-python
+ # via edx-django-utils
pyparsing==3.1.2
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # chem
- # httplib2
- # openedx-calc
-pyrsistent==0.20.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # optimizely-sdk
-pysrt==1.1.2
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edxval
+ # via httplib2
python-dateutil==2.9.0.post0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # analytics-python
- # botocore
- # celery
- # edx-ace
- # edx-enterprise
- # edx-proctoring
- # icalendar
- # olxcleaner
- # ora2
- # xblock
-python-ipware==3.0.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # django-ipware
-python-memcached==1.62
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-python-slugify==8.0.4
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # code-annotations
-python-swiftclient==4.6.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # ora2
-python3-openid==3.2.0 ; python_version >= "3"
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # social-auth-core
-python3-saml==1.16.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # via botocore
pytz==2024.1
# via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # django-ses
- # djangorestframework
- # drf-yasg
- # edx-completion
- # edx-enterprise
- # edx-proctoring
- # edx-submissions
- # edx-tincan-py35
- # event-tracking
- # fs
- # icalendar
- # interchange
# jenkinsapi
- # olxcleaner
- # ora2
- # snowflake-connector-python
- # xblock
# zeep
-pyuca==1.2
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
pyyaml==6.0.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # -r scripts/user_retirement/requirements/base.in
- # code-annotations
- # drf-spectacular
- # drf-yasg
- # edx-django-release-util
- # edx-i18n-tools
- # jsondiff
- # xblock
-random2==1.0.2
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-recommender-xblock==2.2.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-redis==5.0.8
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # walrus
-referencing==0.35.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # jsonschema
- # jsonschema-specifications
-regex==2024.7.24
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # nltk
+ # via -r scripts/user_retirement/requirements/base.in
requests==2.32.3
# via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# -r scripts/user_retirement/requirements/base.in
- # algoliasearch
- # analytics-python
- # cachecontrol
- # django-oauth-toolkit
- # edx-bulk-grades
- # edx-drf-extensions
- # edx-enterprise
# edx-rest-api-client
- # geoip2
# google-api-core
- # google-cloud-storage
# jenkinsapi
- # mailsnake
- # meilisearch
- # openai
- # optimizely-sdk
- # pyjwkest
- # pylti1p3
- # python-swiftclient
# requests-file
- # requests-oauthlib
# requests-toolbelt
- # sailthru-client
# simple-salesforce
# slumber
- # snowflake-connector-python
- # social-auth-core
- # xblock-google-drive
# zeep
requests-file==2.1.0
# via zeep
-requests-oauthlib==2.0.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # social-auth-core
requests-toolbelt==1.0.0
# via zeep
-rpds-py==0.20.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # jsonschema
- # referencing
rsa==4.9
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # google-auth
-rules==3.4
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
- # edx-proctoring
- # openedx-learning
+ # via google-auth
s3transfer==0.10.2
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # boto3
-sailthru-client==2.2.3
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-ace
-scipy==1.14.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # chem
- # openedx-calc
-semantic-version==2.10.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-drf-extensions
-shapely==2.0.5
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # via boto3
simple-salesforce==1.12.6
# via -r scripts/user_retirement/requirements/base.in
simplejson==3.19.2
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # -r scripts/user_retirement/requirements/base.in
- # sailthru-client
- # super-csv
- # xblock
- # xblock-utils
+ # via -r scripts/user_retirement/requirements/base.in
six==1.16.0
# via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # analytics-python
- # bleach
- # codejail-includes
- # crowdsourcehinter-xblock
- # edx-ace
- # edx-auth-backends
- # edx-ccx-keys
- # edx-codejail
- # edx-django-release-util
- # edx-milestones
- # edx-rbac
- # event-tracking
- # fs
- # fs-s3fs
- # html5lib
- # interchange
# isodate
# jenkinsapi
- # libsass
- # optimizely-sdk
- # pansi
- # paver
- # py2neo
- # pyjwkest
# python-dateutil
slumber==0.7.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-bulk-grades
- # edx-enterprise
- # edx-rest-api-client
-snowflake-connector-python==3.12.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
-social-auth-app-django==5.4.1
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-auth-backends
-social-auth-core==4.5.4
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-auth-backends
- # social-auth-app-django
-sorl-thumbnail==12.10.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # openedx-django-wiki
-sortedcontainers==2.4.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # snowflake-connector-python
-soupsieve==2.5
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # beautifulsoup4
+ # via edx-rest-api-client
sqlparse==0.5.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # django
-staff-graded-xblock==2.3.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
+ # via django
stevedore==5.2.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # code-annotations
- # edx-ace
- # edx-django-utils
- # edx-enterprise
- # edx-opaque-keys
-super-csv==3.2.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-bulk-grades
-sympy==1.13.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # openedx-calc
-testfixtures==8.3.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-enterprise
-text-unidecode==1.3
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # python-slugify
-tinycss2==1.2.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # bleach
-tomlkit==0.13.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # snowflake-connector-python
-tqdm==4.66.5
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # nltk
- # openai
+ # via edx-django-utils
typing-extensions==4.12.2
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # django-countries
- # edx-opaque-keys
- # jwcrypto
- # pydantic
- # pydantic-core
- # pylti1p3
- # simple-salesforce
- # snowflake-connector-python
-tzdata==2024.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # celery
+ # via simple-salesforce
unicodecsv==0.14.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # -r scripts/user_retirement/requirements/base.in
- # edx-enterprise
+ # via -r scripts/user_retirement/requirements/base.in
uritemplate==4.1.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # drf-spectacular
- # drf-yasg
- # google-api-python-client
+ # via google-api-python-client
urllib3==1.26.19
# via
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
# botocore
- # elasticsearch
- # py2neo
# requests
-user-util==1.1.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-vine==5.1.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # amqp
- # celery
- # kombu
-voluptuous==0.15.2
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # ora2
-walrus==0.9.4
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-event-bus-redis
-watchdog==4.0.1
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-wcwidth==0.2.13
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # prompt-toolkit
-web-fragments==2.2.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # crowdsourcehinter-xblock
- # edx-sga
- # staff-graded-xblock
- # xblock
- # xblock-utils
-webencodings==0.5.1
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # bleach
- # html5lib
- # tinycss2
-webob==1.8.7
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # xblock
-wrapt==1.16.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-xblock[django]==4.0.1
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # acid-xblock
- # crowdsourcehinter-xblock
- # done-xblock
- # edx-completion
- # edx-sga
- # edx-when
- # lti-consumer-xblock
- # ora2
- # staff-graded-xblock
- # xblock-drag-and-drop-v2
- # xblock-google-drive
- # xblock-utils
-xblock-drag-and-drop-v2==4.0.3
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-xblock-google-drive==0.7.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-xblock-poll==1.14.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-xblock-utils==4.0.0
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # edx-sga
- # xblock-poll
-xmlsec==1.3.13
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # python3-saml
-xss-utils==0.6.0
- # via -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
-yarl==1.9.4
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # aiohttp
zeep==4.2.1
# via simple-salesforce
-zipp==3.19.2
- # via
- # -r scripts/user_retirement/requirements/../../../requirements/edx/base.txt
- # importlib-metadata
-
-# The following packages are considered to be unsafe in a requirements file:
-# setuptools
diff --git a/scripts/user_retirement/requirements/testing.txt b/scripts/user_retirement/requirements/testing.txt
index 6231b3bb36..006eabeef4 100644
--- a/scripts/user_retirement/requirements/testing.txt
+++ b/scripts/user_retirement/requirements/testing.txt
@@ -4,1256 +4,219 @@
#
# make upgrade
#
--e git+https://github.com/anupdhabarde/edx-proctoring-proctortrack.git@31c6c9923a51c903ae83760ecbbac191363aa2a2#egg=edx_proctoring_proctortrack
- # via -r scripts/user_retirement/requirements/base.txt
-acid-xblock==0.3.1
- # via -r scripts/user_retirement/requirements/base.txt
-aiohappyeyeballs==2.3.4
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # aiohttp
-aiohttp==3.10.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # geoip2
- # openai
-aiosignal==1.3.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # aiohttp
-algoliasearch==3.0.0
- # via -r scripts/user_retirement/requirements/base.txt
-amqp==5.2.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # kombu
-analytics-python==1.4.post1
- # via -r scripts/user_retirement/requirements/base.txt
-aniso8601==9.0.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-tincan-py35
-annotated-types==0.7.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # pydantic
-appdirs==1.4.4
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # fs
asgiref==3.8.1
# via
# -r scripts/user_retirement/requirements/base.txt
# django
- # django-cors-headers
- # django-countries
-asn1crypto==1.5.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # snowflake-connector-python
attrs==24.2.0
# via
# -r scripts/user_retirement/requirements/base.txt
- # aiohttp
- # edx-ace
- # jsonschema
- # lti-consumer-xblock
- # openedx-events
- # openedx-learning
- # referencing
# zeep
-babel==2.15.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # enmerkar
- # enmerkar-underscore
-backoff==1.10.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # analytics-python
-bcrypt==4.2.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # paramiko
-beautifulsoup4==4.12.3
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # pynliner
-billiard==4.2.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # celery
-bleach[css]==6.1.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
- # lti-consumer-xblock
- # openedx-django-wiki
- # ora2
- # xblock-drag-and-drop-v2
- # xblock-poll
-boto==2.49.0
+backoff==2.2.1
# via -r scripts/user_retirement/requirements/base.txt
boto3==1.34.154
# via
# -r scripts/user_retirement/requirements/base.txt
- # django-ses
- # fs-s3fs
# moto
- # ora2
botocore==1.34.154
# via
# -r scripts/user_retirement/requirements/base.txt
# boto3
# moto
# s3transfer
-bridgekeeper==0.9
- # via -r scripts/user_retirement/requirements/base.txt
-cachecontrol==0.14.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # firebase-admin
cachetools==5.4.0
# via
# -r scripts/user_retirement/requirements/base.txt
# google-auth
-camel-converter[pydantic]==3.1.2
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # meilisearch
-celery==5.4.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # django-celery-results
- # django-user-tasks
- # edx-celeryutils
- # edx-enterprise
- # event-tracking
- # openedx-learning
certifi==2024.7.4
# via
# -r scripts/user_retirement/requirements/base.txt
- # elasticsearch
- # py2neo
# requests
- # snowflake-connector-python
cffi==1.17.0
# via
# -r scripts/user_retirement/requirements/base.txt
# cryptography
# pynacl
- # snowflake-connector-python
-chardet==5.2.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # pysrt
charset-normalizer==2.0.12
# via
# -r scripts/user_retirement/requirements/base.txt
# requests
- # snowflake-connector-python
-chem==1.3.0
- # via -r scripts/user_retirement/requirements/base.txt
click==8.1.6
# via
# -r scripts/user_retirement/requirements/base.txt
- # celery
- # click-didyoumean
- # click-plugins
- # click-repl
- # code-annotations
# edx-django-utils
- # nltk
- # user-util
-click-didyoumean==0.3.1
+cryptography==43.0.0
# via
# -r scripts/user_retirement/requirements/base.txt
- # celery
-click-plugins==1.1.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # celery
-click-repl==0.3.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # celery
-code-annotations==1.8.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
- # edx-toggles
-codejail-includes==1.0.0
- # via -r scripts/user_retirement/requirements/base.txt
-crowdsourcehinter-xblock==0.7
- # via -r scripts/user_retirement/requirements/base.txt
-cryptography==42.0.8
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # django-fernet-fields-v2
- # edx-enterprise
- # jwcrypto
# moto
- # optimizely-sdk
- # paramiko
- # pgpy
# pyjwt
- # pyopenssl
- # snowflake-connector-python
- # social-auth-core
-cssutils==2.11.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # pynliner
ddt==1.7.2
# via -r scripts/user_retirement/requirements/testing.in
-defusedxml==0.7.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # djangorestframework-xml
- # ora2
- # python3-openid
- # social-auth-core
django==4.2.15
# via
# -r scripts/user_retirement/requirements/base.txt
- # django-appconf
- # django-celery-results
- # django-classy-tags
- # django-config-models
- # django-cors-headers
# django-crum
- # django-fernet-fields-v2
- # django-filter
- # django-js-asset
- # django-method-override
- # django-model-utils
- # django-multi-email-field
- # django-mysql
- # django-oauth-toolkit
- # django-push-notifications
- # django-sekizai
- # django-ses
- # django-statici18n
- # django-storages
- # django-user-tasks
# django-waffle
- # djangorestframework
- # done-xblock
- # drf-jwt
- # drf-spectacular
- # drf-yasg
- # edx-ace
- # edx-api-doc-tools
- # edx-auth-backends
- # edx-bulk-grades
- # edx-celeryutils
- # edx-completion
- # edx-django-release-util
- # edx-django-sites-extensions
# edx-django-utils
- # edx-drf-extensions
- # edx-enterprise
- # edx-event-bus-kafka
- # edx-event-bus-redis
- # edx-i18n-tools
- # edx-milestones
- # edx-name-affirmation
- # edx-organizations
- # edx-proctoring
- # edx-rbac
- # edx-search
- # edx-submissions
- # edx-toggles
- # edx-token-utils
- # edx-when
- # edxval
- # enmerkar
- # enmerkar-underscore
- # event-tracking
- # help-tokens
- # jsonfield
- # lti-consumer-xblock
- # openedx-django-pyfs
- # openedx-django-wiki
- # openedx-events
- # openedx-filters
- # openedx-learning
- # ora2
- # social-auth-app-django
- # super-csv
- # xblock-google-drive
- # xss-utils
-django-appconf==1.0.6
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # django-statici18n
-django-cache-memoize==0.2.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
-django-celery-results==2.5.1
- # via -r scripts/user_retirement/requirements/base.txt
-django-classy-tags==4.1.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # django-sekizai
-django-config-models==2.7.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
- # edx-name-affirmation
- # lti-consumer-xblock
-django-cors-headers==4.4.0
- # via -r scripts/user_retirement/requirements/base.txt
-django-countries==7.6.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
django-crum==0.7.9
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
- # edx-enterprise
- # edx-proctoring
- # edx-rbac
- # edx-toggles
- # super-csv
-django-fernet-fields-v2==0.9
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
-django-filter==24.3
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
- # lti-consumer-xblock
-django-ipware==7.0.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
- # edx-proctoring
-django-js-asset==2.2.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # django-mptt
-django-method-override==1.0.4
- # via -r scripts/user_retirement/requirements/base.txt
-django-model-utils==4.5.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # django-user-tasks
- # edx-bulk-grades
- # edx-celeryutils
- # edx-completion
- # edx-enterprise
- # edx-milestones
- # edx-name-affirmation
- # edx-organizations
- # edx-proctoring
- # edx-rbac
- # edx-submissions
- # edx-when
- # edxval
- # ora2
- # super-csv
-django-mptt==0.16.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # openedx-django-wiki
-django-multi-email-field==0.7.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
-django-mysql==4.14.0
- # via -r scripts/user_retirement/requirements/base.txt
-django-oauth-toolkit==1.7.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
-django-object-actions==4.2.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
-django-pipeline==3.1.0
- # via -r scripts/user_retirement/requirements/base.txt
-django-push-notifications==3.1.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-ace
-django-ratelimit==4.1.0
- # via -r scripts/user_retirement/requirements/base.txt
-django-sekizai==4.1.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # openedx-django-wiki
-django-ses==4.1.0
- # via -r scripts/user_retirement/requirements/base.txt
-django-simple-history==3.4.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
- # edx-name-affirmation
- # edx-organizations
- # edx-proctoring
- # ora2
-django-statici18n==2.5.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # lti-consumer-xblock
- # xblock-drag-and-drop-v2
- # xblock-poll
-django-storages==1.14.3
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edxval
-django-user-tasks==3.2.0
- # via -r scripts/user_retirement/requirements/base.txt
django-waffle==4.1.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
- # edx-drf-extensions
- # edx-enterprise
- # edx-proctoring
- # edx-toggles
-django-webpack-loader==0.7.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-proctoring
-djangorestframework==3.14.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # django-config-models
- # django-user-tasks
- # drf-jwt
- # drf-spectacular
- # drf-yasg
- # edx-api-doc-tools
- # edx-completion
- # edx-drf-extensions
- # edx-enterprise
- # edx-name-affirmation
- # edx-organizations
- # edx-proctoring
- # edx-submissions
- # openedx-learning
- # ora2
- # super-csv
-djangorestframework-xml==2.0.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
-dnspython==2.6.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # pymongo
-done-xblock==2.3.0
- # via -r scripts/user_retirement/requirements/base.txt
-drf-jwt==1.19.2
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-drf-extensions
-drf-spectacular==0.27.2
- # via -r scripts/user_retirement/requirements/base.txt
-drf-yasg==1.21.7
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # django-user-tasks
- # edx-api-doc-tools
-edx-ace==1.11.1
- # via -r scripts/user_retirement/requirements/base.txt
-edx-api-doc-tools==1.8.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-name-affirmation
-edx-auth-backends==4.3.0
- # via -r scripts/user_retirement/requirements/base.txt
-edx-braze-client==0.2.5
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
-edx-bulk-grades==1.1.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # staff-graded-xblock
-edx-ccx-keys==1.3.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # lti-consumer-xblock
- # openedx-events
-edx-celeryutils==1.3.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-name-affirmation
- # super-csv
-edx-codejail==3.4.1
- # via -r scripts/user_retirement/requirements/base.txt
-edx-completion==4.6.7
- # via -r scripts/user_retirement/requirements/base.txt
-edx-django-release-util==1.4.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-submissions
- # edxval
-edx-django-sites-extensions==4.2.0
- # via -r scripts/user_retirement/requirements/base.txt
edx-django-utils==5.15.0
# via
# -r scripts/user_retirement/requirements/base.txt
- # django-config-models
- # edx-ace
- # edx-drf-extensions
- # edx-enterprise
- # edx-event-bus-kafka
- # edx-event-bus-redis
- # edx-name-affirmation
# edx-rest-api-client
- # edx-toggles
- # edx-when
- # event-tracking
- # openedx-events
- # ora2
- # super-csv
-edx-drf-extensions==10.3.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-completion
- # edx-enterprise
- # edx-name-affirmation
- # edx-organizations
- # edx-proctoring
- # edx-rbac
- # edx-when
- # edxval
- # openedx-learning
-edx-enterprise==4.23.8
- # via -r scripts/user_retirement/requirements/base.txt
-edx-event-bus-kafka==5.8.1
- # via -r scripts/user_retirement/requirements/base.txt
-edx-event-bus-redis==0.5.0
- # via -r scripts/user_retirement/requirements/base.txt
-edx-i18n-tools==1.5.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # ora2
-edx-milestones==0.6.0
- # via -r scripts/user_retirement/requirements/base.txt
-edx-name-affirmation==2.3.7
- # via -r scripts/user_retirement/requirements/base.txt
-edx-opaque-keys[django]==2.10.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-bulk-grades
- # edx-ccx-keys
- # edx-completion
- # edx-drf-extensions
- # edx-enterprise
- # edx-milestones
- # edx-organizations
- # edx-proctoring
- # edx-when
- # lti-consumer-xblock
- # openedx-events
- # ora2
-edx-organizations==6.13.0
- # via -r scripts/user_retirement/requirements/base.txt
-edx-proctoring==4.18.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-proctoring-proctortrack
-edx-rbac==1.9.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
edx-rest-api-client==5.7.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
- # edx-proctoring
-edx-search==4.0.0
# via -r scripts/user_retirement/requirements/base.txt
-edx-sga==0.25.0
- # via -r scripts/user_retirement/requirements/base.txt
-edx-submissions==3.7.6
+google-api-core==2.19.1
# via
# -r scripts/user_retirement/requirements/base.txt
- # ora2
-edx-tincan-py35==1.0.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
-edx-toggles==5.2.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-completion
- # edx-enterprise
- # edx-event-bus-kafka
- # edx-event-bus-redis
- # edx-name-affirmation
- # edx-search
- # edxval
- # event-tracking
- # ora2
-edx-token-utils==0.2.1
- # via -r scripts/user_retirement/requirements/base.txt
-edx-when==2.5.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-proctoring
-edxval==2.5.0
- # via -r scripts/user_retirement/requirements/base.txt
-elasticsearch==7.13.4
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-search
-enmerkar==0.7.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # enmerkar-underscore
-enmerkar-underscore==2.3.0
- # via -r scripts/user_retirement/requirements/base.txt
-event-tracking==3.0.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-completion
- # edx-proctoring
- # edx-search
-fastavro==1.9.5
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # openedx-events
-filelock==3.15.4
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # snowflake-connector-python
-firebase-admin==6.5.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-ace
-frozenlist==1.4.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # aiohttp
- # aiosignal
-fs==2.0.27
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # fs-s3fs
- # openedx-django-pyfs
- # xblock
-fs-s3fs==0.1.8
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # openedx-django-pyfs
-future==1.0.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # pyjwkest
-geoip2==4.8.0
- # via -r scripts/user_retirement/requirements/base.txt
-glob2==0.7
- # via -r scripts/user_retirement/requirements/base.txt
-google-api-core[grpc]==2.19.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # firebase-admin
# google-api-python-client
- # google-cloud-core
- # google-cloud-firestore
- # google-cloud-storage
google-api-python-client==2.139.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # firebase-admin
+ # via -r scripts/user_retirement/requirements/base.txt
google-auth==2.32.0
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-core
# google-api-python-client
# google-auth-httplib2
- # google-cloud-core
- # google-cloud-firestore
- # google-cloud-storage
google-auth-httplib2==0.2.0
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-python-client
-google-cloud-core==2.4.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # google-cloud-firestore
- # google-cloud-storage
-google-cloud-firestore==2.17.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # firebase-admin
-google-cloud-storage==2.18.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # firebase-admin
-google-crc32c==1.5.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # google-cloud-storage
- # google-resumable-media
-google-resumable-media==2.7.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # google-cloud-storage
googleapis-common-protos==1.63.2
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-core
- # grpcio-status
-grpcio==1.65.4
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # google-api-core
- # grpcio-status
-grpcio-status==1.62.3
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # google-api-core
-gunicorn==22.0.0
- # via -r scripts/user_retirement/requirements/base.txt
-help-tokens==2.4.0
- # via -r scripts/user_retirement/requirements/base.txt
-html5lib==1.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # ora2
httplib2==0.22.0
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-python-client
# google-auth-httplib2
-icalendar==5.0.13
- # via -r scripts/user_retirement/requirements/base.txt
idna==3.7
# via
# -r scripts/user_retirement/requirements/base.txt
- # optimizely-sdk
# requests
- # snowflake-connector-python
- # yarl
-importlib-metadata==6.11.0
- # via -r scripts/user_retirement/requirements/base.txt
-inflection==0.5.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # drf-spectacular
- # drf-yasg
iniconfig==2.0.0
# via pytest
-interchange==2021.0.4
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # py2neo
-ipaddress==1.0.23
- # via -r scripts/user_retirement/requirements/base.txt
isodate==0.6.1
# via
# -r scripts/user_retirement/requirements/base.txt
- # python3-saml
# zeep
jenkinsapi==0.3.13
# via -r scripts/user_retirement/requirements/base.txt
jinja2==3.1.4
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # code-annotations
- # moto
+ # via moto
jmespath==1.0.1
# via
# -r scripts/user_retirement/requirements/base.txt
# boto3
# botocore
-joblib==1.4.2
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # nltk
-jsondiff==2.2.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
-jsonfield==3.1.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-celeryutils
- # edx-enterprise
- # edx-proctoring
- # edx-submissions
- # lti-consumer-xblock
- # ora2
-jsonschema==4.23.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # drf-spectacular
- # optimizely-sdk
-jsonschema-specifications==2023.12.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # jsonschema
-jwcrypto==1.5.6
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # django-oauth-toolkit
- # pylti1p3
-kombu==5.4.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # celery
-laboratory==1.0.2
- # via -r scripts/user_retirement/requirements/base.txt
-lazy==1.6
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # acid-xblock
- # lti-consumer-xblock
- # ora2
- # xblock
-libsass==0.10.0
- # via -r scripts/user_retirement/requirements/base.txt
-loremipsum==1.0.5
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # ora2
-lti-consumer-xblock==9.11.3
- # via -r scripts/user_retirement/requirements/base.txt
lxml==4.9.4
# via
# -r scripts/user_retirement/requirements/base.txt
- # edx-i18n-tools
- # edxval
- # lti-consumer-xblock
- # olxcleaner
- # openedx-calc
- # ora2
- # python3-saml
- # xblock
- # xmlsec
# zeep
-mailsnake==1.6.4
- # via -r scripts/user_retirement/requirements/base.txt
-mako==1.3.5
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # acid-xblock
- # lti-consumer-xblock
- # xblock
- # xblock-utils
-markdown==3.3.7
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # openedx-django-wiki
- # staff-graded-xblock
- # xblock-poll
markupsafe==2.1.5
# via
- # -r scripts/user_retirement/requirements/base.txt
- # chem
# jinja2
- # mako
- # openedx-calc
# werkzeug
- # xblock
-maxminddb==2.6.2
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # geoip2
-meilisearch==0.31.4
- # via -r scripts/user_retirement/requirements/base.txt
mock==5.1.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # -r scripts/user_retirement/requirements/testing.in
-mongoengine==0.28.2
- # via -r scripts/user_retirement/requirements/base.txt
-monotonic==1.6
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # analytics-python
- # py2neo
+ # via -r scripts/user_retirement/requirements/testing.in
more-itertools==10.3.0
# via
# -r scripts/user_retirement/requirements/base.txt
- # cssutils
# simple-salesforce
moto==4.2.14
# via -r scripts/user_retirement/requirements/testing.in
-mpmath==1.3.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # sympy
-msgpack==1.0.8
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # cachecontrol
-multidict==6.0.5
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # aiohttp
- # yarl
-mysqlclient==2.2.4
- # via -r scripts/user_retirement/requirements/base.txt
newrelic==9.12.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
-nh3==0.2.18
- # via -r scripts/user_retirement/requirements/base.txt
-nltk==3.8.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # chem
-nodeenv==1.9.1
- # via -r scripts/user_retirement/requirements/base.txt
-numpy==1.26.4
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # chem
- # openedx-calc
- # scipy
- # shapely
-oauthlib==3.2.2
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # django-oauth-toolkit
- # lti-consumer-xblock
- # requests-oauthlib
- # social-auth-core
-olxcleaner==0.3.0
- # via -r scripts/user_retirement/requirements/base.txt
-openai==0.28.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
-openedx-atlas==0.6.1
- # via -r scripts/user_retirement/requirements/base.txt
-openedx-calc==3.1.0
- # via -r scripts/user_retirement/requirements/base.txt
-openedx-django-pyfs==3.6.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # lti-consumer-xblock
- # xblock
-openedx-django-require==2.1.0
- # via -r scripts/user_retirement/requirements/base.txt
-openedx-django-wiki==2.1.0
- # via -r scripts/user_retirement/requirements/base.txt
-openedx-events==9.11.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-event-bus-kafka
- # edx-event-bus-redis
- # event-tracking
- # ora2
-openedx-filters==1.9.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # lti-consumer-xblock
- # ora2
-openedx-learning==0.10.1
- # via -r scripts/user_retirement/requirements/base.txt
-openedx-mongodbproxy==0.2.1
- # via -r scripts/user_retirement/requirements/base.txt
-optimizely-sdk==4.1.1
- # via -r scripts/user_retirement/requirements/base.txt
-ora2==6.11.2
- # via -r scripts/user_retirement/requirements/base.txt
packaging==24.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # drf-yasg
- # gunicorn
- # py2neo
- # pytest
- # snowflake-connector-python
-pansi==2020.7.3
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # py2neo
-paramiko==3.4.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
-path==16.11.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-i18n-tools
- # path-py
-path-py==12.5.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
- # ora2
- # staff-graded-xblock
-paver==1.3.4
- # via -r scripts/user_retirement/requirements/base.txt
+ # via pytest
pbr==6.0.0
# via
# -r scripts/user_retirement/requirements/base.txt
# stevedore
-pgpy==0.6.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
-piexif==1.1.3
- # via -r scripts/user_retirement/requirements/base.txt
-pillow==10.4.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
- # edx-organizations
- # edxval
platformdirs==4.2.2
# via
# -r scripts/user_retirement/requirements/base.txt
- # snowflake-connector-python
# zeep
pluggy==1.5.0
# via pytest
-polib==1.2.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-i18n-tools
-prompt-toolkit==3.0.47
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # click-repl
proto-plus==1.24.0
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-core
- # google-cloud-firestore
-protobuf==4.25.4
+protobuf==5.27.3
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-core
- # google-cloud-firestore
# googleapis-common-protos
- # grpcio-status
# proto-plus
psutil==6.0.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
-py2neo @ https://github.com/overhangio/py2neo/releases/download/2021.2.3/py2neo-2021.2.3.tar.gz
- # via -r scripts/user_retirement/requirements/base.txt
pyasn1==0.6.0
# via
# -r scripts/user_retirement/requirements/base.txt
- # pgpy
# pyasn1-modules
# rsa
pyasn1-modules==0.4.0
# via
# -r scripts/user_retirement/requirements/base.txt
# google-auth
-pycountry==24.6.1
- # via -r scripts/user_retirement/requirements/base.txt
pycparser==2.22
# via
# -r scripts/user_retirement/requirements/base.txt
# cffi
-pycryptodomex==3.20.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-proctoring
- # lti-consumer-xblock
- # pyjwkest
-pydantic==2.8.2
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # camel-converter
-pydantic-core==2.20.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # pydantic
-pygments==2.18.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # py2neo
-pyjwkest==1.4.2
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-token-utils
- # lti-consumer-xblock
pyjwt[crypto]==2.9.0
# via
# -r scripts/user_retirement/requirements/base.txt
- # drf-jwt
- # edx-auth-backends
- # edx-drf-extensions
- # edx-proctoring
# edx-rest-api-client
- # firebase-admin
- # pylti1p3
# simple-salesforce
- # snowflake-connector-python
- # social-auth-core
-pylatexenc==2.10
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # olxcleaner
-pylti1p3==2.0.0
- # via -r scripts/user_retirement/requirements/base.txt
-pymemcache==4.0.0
- # via -r scripts/user_retirement/requirements/base.txt
-pymongo==4.4.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-opaque-keys
- # event-tracking
- # mongoengine
- # openedx-mongodbproxy
pynacl==1.5.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
- # paramiko
-pynliner==0.8.0
- # via -r scripts/user_retirement/requirements/base.txt
-pyopenssl==24.2.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # optimizely-sdk
- # snowflake-connector-python
pyparsing==3.1.2
# via
# -r scripts/user_retirement/requirements/base.txt
- # chem
# httplib2
- # openedx-calc
-pyrsistent==0.20.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # optimizely-sdk
-pysrt==1.1.2
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edxval
pytest==8.3.2
# via -r scripts/user_retirement/requirements/testing.in
python-dateutil==2.9.0.post0
# via
# -r scripts/user_retirement/requirements/base.txt
- # analytics-python
# botocore
- # celery
- # edx-ace
- # edx-enterprise
- # edx-proctoring
- # icalendar
# moto
- # olxcleaner
- # ora2
- # xblock
-python-ipware==3.0.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # django-ipware
-python-memcached==1.62
- # via -r scripts/user_retirement/requirements/base.txt
-python-slugify==8.0.4
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # code-annotations
-python-swiftclient==4.6.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # ora2
-python3-openid==3.2.0 ; python_version >= "3"
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # social-auth-core
-python3-saml==1.16.0
- # via -r scripts/user_retirement/requirements/base.txt
pytz==2024.1
# via
# -r scripts/user_retirement/requirements/base.txt
- # django-ses
- # djangorestframework
- # drf-yasg
- # edx-completion
- # edx-enterprise
- # edx-proctoring
- # edx-submissions
- # edx-tincan-py35
- # event-tracking
- # fs
- # icalendar
- # interchange
# jenkinsapi
- # olxcleaner
- # ora2
- # snowflake-connector-python
- # xblock
# zeep
-pyuca==1.2
- # via -r scripts/user_retirement/requirements/base.txt
pyyaml==6.0.1
# via
# -r scripts/user_retirement/requirements/base.txt
- # code-annotations
- # drf-spectacular
- # drf-yasg
- # edx-django-release-util
- # edx-i18n-tools
- # jsondiff
# responses
- # xblock
-random2==1.0.2
- # via -r scripts/user_retirement/requirements/base.txt
-recommender-xblock==2.2.0
- # via -r scripts/user_retirement/requirements/base.txt
-redis==5.0.8
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # walrus
-referencing==0.35.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # jsonschema
- # jsonschema-specifications
-regex==2024.7.24
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # nltk
requests==2.32.3
# via
# -r scripts/user_retirement/requirements/base.txt
- # algoliasearch
- # analytics-python
- # cachecontrol
- # django-oauth-toolkit
- # edx-bulk-grades
- # edx-drf-extensions
- # edx-enterprise
# edx-rest-api-client
- # geoip2
# google-api-core
- # google-cloud-storage
# jenkinsapi
- # mailsnake
- # meilisearch
# moto
- # openai
- # optimizely-sdk
- # pyjwkest
- # pylti1p3
- # python-swiftclient
# requests-file
# requests-mock
- # requests-oauthlib
# requests-toolbelt
# responses
- # sailthru-client
# simple-salesforce
# slumber
- # snowflake-connector-python
- # social-auth-core
- # xblock-google-drive
# zeep
requests-file==2.1.0
# via
@@ -1261,10 +224,6 @@ requests-file==2.1.0
# zeep
requests-mock==1.12.1
# via -r scripts/user_retirement/requirements/testing.in
-requests-oauthlib==2.0.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # social-auth-core
requests-toolbelt==1.0.0
# via
# -r scripts/user_retirement/requirements/base.txt
@@ -1273,274 +232,57 @@ responses==0.25.3
# via
# -r scripts/user_retirement/requirements/testing.in
# moto
-rpds-py==0.20.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # jsonschema
- # referencing
rsa==4.9
# via
# -r scripts/user_retirement/requirements/base.txt
# google-auth
-rules==3.4
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
- # edx-proctoring
- # openedx-learning
s3transfer==0.10.2
# via
# -r scripts/user_retirement/requirements/base.txt
# boto3
-sailthru-client==2.2.3
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-ace
-scipy==1.14.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # chem
- # openedx-calc
-semantic-version==2.10.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-drf-extensions
-shapely==2.0.5
- # via -r scripts/user_retirement/requirements/base.txt
simple-salesforce==1.12.6
# via -r scripts/user_retirement/requirements/base.txt
simplejson==3.19.2
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # sailthru-client
- # super-csv
- # xblock
- # xblock-utils
+ # via -r scripts/user_retirement/requirements/base.txt
six==1.16.0
# via
# -r scripts/user_retirement/requirements/base.txt
- # analytics-python
- # bleach
- # codejail-includes
- # crowdsourcehinter-xblock
- # edx-ace
- # edx-auth-backends
- # edx-ccx-keys
- # edx-codejail
- # edx-django-release-util
- # edx-milestones
- # edx-rbac
- # event-tracking
- # fs
- # fs-s3fs
- # html5lib
- # interchange
# isodate
# jenkinsapi
- # libsass
- # optimizely-sdk
- # pansi
- # paver
- # py2neo
- # pyjwkest
# python-dateutil
slumber==0.7.1
# via
# -r scripts/user_retirement/requirements/base.txt
- # edx-bulk-grades
- # edx-enterprise
# edx-rest-api-client
-snowflake-connector-python==3.12.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
-social-auth-app-django==5.4.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-auth-backends
-social-auth-core==4.5.4
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-auth-backends
- # social-auth-app-django
-sorl-thumbnail==12.10.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # openedx-django-wiki
-sortedcontainers==2.4.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # snowflake-connector-python
-soupsieve==2.5
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # beautifulsoup4
sqlparse==0.5.1
# via
# -r scripts/user_retirement/requirements/base.txt
# django
-staff-graded-xblock==2.3.0
- # via -r scripts/user_retirement/requirements/base.txt
stevedore==5.2.0
# via
# -r scripts/user_retirement/requirements/base.txt
- # code-annotations
- # edx-ace
# edx-django-utils
- # edx-enterprise
- # edx-opaque-keys
-super-csv==3.2.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-bulk-grades
-sympy==1.13.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # openedx-calc
-testfixtures==8.3.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
-text-unidecode==1.3
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # python-slugify
-tinycss2==1.2.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # bleach
-tomlkit==0.13.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # snowflake-connector-python
-tqdm==4.66.5
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # nltk
- # openai
typing-extensions==4.12.2
# via
# -r scripts/user_retirement/requirements/base.txt
- # django-countries
- # edx-opaque-keys
- # jwcrypto
- # pydantic
- # pydantic-core
- # pylti1p3
# simple-salesforce
- # snowflake-connector-python
-tzdata==2024.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # celery
unicodecsv==0.14.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-enterprise
+ # via -r scripts/user_retirement/requirements/base.txt
uritemplate==4.1.1
# via
# -r scripts/user_retirement/requirements/base.txt
- # drf-spectacular
- # drf-yasg
# google-api-python-client
urllib3==1.26.19
# via
# -r scripts/user_retirement/requirements/base.txt
# botocore
- # elasticsearch
- # py2neo
# requests
# responses
-user-util==1.1.0
- # via -r scripts/user_retirement/requirements/base.txt
-vine==5.1.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # amqp
- # celery
- # kombu
-voluptuous==0.15.2
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # ora2
-walrus==0.9.4
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-event-bus-redis
-watchdog==4.0.1
- # via -r scripts/user_retirement/requirements/base.txt
-wcwidth==0.2.13
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # prompt-toolkit
-web-fragments==2.2.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # crowdsourcehinter-xblock
- # edx-sga
- # staff-graded-xblock
- # xblock
- # xblock-utils
-webencodings==0.5.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # bleach
- # html5lib
- # tinycss2
-webob==1.8.7
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # xblock
werkzeug==3.0.3
# via moto
-wrapt==1.16.0
- # via -r scripts/user_retirement/requirements/base.txt
-xblock[django]==4.0.1
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # acid-xblock
- # crowdsourcehinter-xblock
- # done-xblock
- # edx-completion
- # edx-sga
- # edx-when
- # lti-consumer-xblock
- # ora2
- # staff-graded-xblock
- # xblock-drag-and-drop-v2
- # xblock-google-drive
- # xblock-utils
-xblock-drag-and-drop-v2==4.0.3
- # via -r scripts/user_retirement/requirements/base.txt
-xblock-google-drive==0.7.0
- # via -r scripts/user_retirement/requirements/base.txt
-xblock-poll==1.14.0
- # via -r scripts/user_retirement/requirements/base.txt
-xblock-utils==4.0.0
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # edx-sga
- # xblock-poll
-xmlsec==1.3.13
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # python3-saml
xmltodict==0.13.0
# via moto
-xss-utils==0.6.0
- # via -r scripts/user_retirement/requirements/base.txt
-yarl==1.9.4
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # aiohttp
zeep==4.2.1
# via
# -r scripts/user_retirement/requirements/base.txt
# simple-salesforce
-zipp==3.19.2
- # via
- # -r scripts/user_retirement/requirements/base.txt
- # importlib-metadata
-
-# The following packages are considered to be unsafe in a requirements file:
-# setuptools
diff --git a/scripts/user_retirement/tests/test_retire_one_learner.py b/scripts/user_retirement/tests/test_retire_one_learner.py
index a30186cf6e..844325ea3e 100644
--- a/scripts/user_retirement/tests/test_retire_one_learner.py
+++ b/scripts/user_retirement/tests/test_retire_one_learner.py
@@ -4,8 +4,6 @@ Test the retire_one_learner.py script
from unittest.mock import DEFAULT, patch
from click.testing import CliRunner
-from django.conf import settings
-from django.test.utils import override_settings
from scripts.user_retirement.retire_one_learner import (
END_STATES,
@@ -63,8 +61,8 @@ def test_successful_retirement(*args, **kwargs):
result = _call_script(username, fetch_ecom_segment_id=True)
- # Called once per API we instantiate (LMS, ECommerce, Credentials, Additional Services)
- assert mock_get_access_token.call_count == 3 + len(settings.EXTRA_SERVICES_TO_RETIRE_FROM)
+ # Called once per API we instantiate (LMS, ECommerce, Credentials)
+ assert mock_get_access_token.call_count == 3
mock_get_retirement_state.assert_called_once_with(username)
assert mock_update_learner_state.call_count == 9
@@ -99,7 +97,7 @@ def test_user_does_not_exist(*args, **kwargs):
result = _call_script(username)
- assert mock_get_access_token.call_count == 3 + len(settings.EXTRA_SERVICES_TO_RETIRE_FROM)
+ assert mock_get_access_token.call_count == 3
mock_get_retirement_state.assert_called_once_with(username)
mock_update_learner_state.assert_not_called()
@@ -134,7 +132,7 @@ def test_bad_learner(*args, **kwargs):
mock_get_retirement_state.side_effect = HttpDoesNotExistException
result = _call_script(username)
- assert mock_get_access_token.call_count == 3 + len(settings.EXTRA_SERVICES_TO_RETIRE_FROM)
+ assert mock_get_access_token.call_count == 3
mock_get_retirement_state.assert_called_once_with(username)
mock_update_learner_state.assert_not_called()
@@ -162,7 +160,7 @@ def test_user_in_working_state(*args, **kwargs):
result = _call_script(username)
- assert mock_get_access_token.call_count == 3 + len(settings.EXTRA_SERVICES_TO_RETIRE_FROM)
+ assert mock_get_access_token.call_count == 3
mock_get_retirement_state.assert_called_once_with(username)
mock_update_learner_state.assert_not_called()
@@ -190,7 +188,7 @@ def test_user_in_bad_state(*args, **kwargs):
)
result = _call_script(username)
- assert mock_get_access_token.call_count == 3 + len(settings.EXTRA_SERVICES_TO_RETIRE_FROM)
+ assert mock_get_access_token.call_count == 3
mock_get_retirement_state.assert_called_once_with(username)
mock_update_learner_state.assert_not_called()
@@ -225,7 +223,7 @@ def test_user_in_end_state(*args, **kwargs):
result = _call_script(username)
- assert mock_get_access_token.call_count == 3 + len(settings.EXTRA_SERVICES_TO_RETIRE_FROM)
+ assert mock_get_access_token.call_count == 3
mock_get_retirement_state.assert_called_once_with(username)
mock_update_learner_state.assert_not_called()
@@ -267,7 +265,7 @@ def test_skipping_states(*args, **kwargs):
result = _call_script(username)
# Called once per API we instantiate (LMS, ECommerce, Credentials)
- assert mock_get_access_token.call_count == 3 + len(settings.EXTRA_SERVICES_TO_RETIRE_FROM)
+ assert mock_get_access_token.call_count == 3
mock_get_retirement_state.assert_called_once_with(username)
assert mock_update_learner_state.call_count == 5
diff --git a/scripts/user_retirement/tests/utils/test_edx_api.py b/scripts/user_retirement/tests/utils/test_edx_api.py
index 2c86c2cb97..eb826cd1a4 100644
--- a/scripts/user_retirement/tests/utils/test_edx_api.py
+++ b/scripts/user_retirement/tests/utils/test_edx_api.py
@@ -542,44 +542,3 @@ class TestLicenseManagerApi(OAuth2Mixin, unittest.TestCase):
original_username=FAKE_ORIGINAL_USERNAME
)
)
-
-
-class TestGenericApi(OAuth2Mixin, unittest.TestCase):
- """
- Test the Generic API client.
- """
-
- @responses.activate(registry=OrderedRegistry)
- def setUp(self):
- super().setUp()
- self.mock_access_token_response()
- self.lms_base_url = 'http://localhost:18000/'
- self.service_api_base_url = 'http://mock_service_url/'
- self.retirement_url = 'mock/retirement_url'
- self.generic_api = edx_api.GenericRetirementApi(
- self.lms_base_url,
- self.service_api_base_url,
- 'the_client_id',
- 'the_client_secret',
- self.retirement_url
- )
-
- @patch.object(edx_api.GenericRetirementApi, '_request')
- def test_retire_learner(self, mock_request):
- learner_data = get_fake_user_retirement()
- json_data = {
- 'edx_lms_user_id': learner_data['user']['id']
- }
- responses.add(
- POST,
- urljoin(self.service_api_base_url, 'mock/retirement_url'),
- match=[matchers.json_params_matcher(json_data)]
- )
-
- self.generic_api.retire_learner(learner=learner_data)
-
- mock_request.assert_called_once_with(
- 'POST',
- urljoin(self.service_api_base_url, 'mock/retirement_url/'),
- json=json_data
- )
diff --git a/scripts/user_retirement/utils/edx_api.py b/scripts/user_retirement/utils/edx_api.py
index b0c5ffae46..e891f04019 100644
--- a/scripts/user_retirement/utils/edx_api.py
+++ b/scripts/user_retirement/utils/edx_api.py
@@ -490,21 +490,3 @@ class LicenseManagerApi(BaseApiClient):
except HttpDoesNotExistException:
LOG.info("No license manager data found for user")
return True
-
-
-class GenericRetirementApi(BaseApiClient):
- """
- Generic API client.
- """
- def __init__(self, lms_base_url, api_base_url, client_id, client_secret, retirement_url_path):
- super().__init__(lms_base_url, api_base_url, client_id, client_secret)
- self.retirement_url_path = retirement_url_path
-
- @_retry_lms_api()
- def retire_learner(self, learner):
- """
- Performs the learner retirement step for additonal services.
- """
- data = {'edx_lms_user_id': learner['user']['id']}
- api_url = self.get_api_url(self.retirement_url_path)
- return self._request('POST', api_url, json=data)
diff --git a/scripts/user_retirement/utils/helpers.py b/scripts/user_retirement/utils/helpers.py
index 7704a25621..1bcbadb4b3 100644
--- a/scripts/user_retirement/utils/helpers.py
+++ b/scripts/user_retirement/utils/helpers.py
@@ -16,11 +16,9 @@ import unicodedata
import yaml
from six import text_type
-from django.conf import settings
from scripts.user_retirement.utils.edx_api import LmsApi # pylint: disable=wrong-import-position
-from scripts.user_retirement.utils.edx_api import CredentialsApi, EcommerceApi, GenericRetirementApi, \
- LicenseManagerApi
+from scripts.user_retirement.utils.edx_api import CredentialsApi, EcommerceApi, LicenseManagerApi
from scripts.user_retirement.utils.thirdparty_apis.amplitude_api import \
AmplitudeApi # pylint: disable=wrong-import-position
from scripts.user_retirement.utils.thirdparty_apis.braze_api import BrazeApi # pylint: disable=wrong-import-position
@@ -174,19 +172,15 @@ def _setup_all_apis_or_exit(fail_func, fail_code, config):
hubspot_from_address = config.get('hubspot_from_address', None)
hubspot_alert_email = config.get('hubspot_alert_email', None)
- required_services = [
- ('BRAZE', braze_api_key),
- ('AMPLITUDE', amplitude_api_key),
- ('ECOMMERCE', ecommerce_base_url),
- ('CREDENTIALS', credentials_base_url),
- ('SEGMENT', segment_base_url),
- ('HUBSPOT', hubspot_api_key),
- ]
- extra_services = [(service['name'], service['service_base_url']) for service in settings.EXTRA_SERVICES_TO_RETIRE_FROM]
- all_services = required_services + extra_services
-
for state in config['retirement_pipeline']:
- for service, service_url in (all_services):
+ for service, service_url in (
+ ('BRAZE', braze_api_key),
+ ('AMPLITUDE', amplitude_api_key),
+ ('ECOMMERCE', ecommerce_base_url),
+ ('CREDENTIALS', credentials_base_url),
+ ('SEGMENT', segment_base_url),
+ ('HUBSPOT', hubspot_api_key),
+ ):
if state[2] == service and service_url is None:
fail_func(fail_code, 'Service URL is not configured, but required for state {}'.format(state))
@@ -241,15 +235,5 @@ def _setup_all_apis_or_exit(fail_func, fail_code, config):
segment_auth_token,
segment_workspace_slug
)
-
- for service_config in settings.EXTRA_SERVICES_TO_RETIRE_FROM:
- service_name = service_config['name']
- config[service_name] = GenericRetirementApi(
- lms_base_url,
- service_config['service_base_url'],
- client_id,
- client_secret,
- service_config['retirement_url_path']
- )
except Exception as exc: # pylint: disable=broad-except
fail_func(fail_code, 'Unexpected error occurred!', exc)
From 11de2a4055d3157eb636b9a8019b61a8bc6e98df Mon Sep 17 00:00:00 2001
From: Yusuf Musleh
Date: Thu, 15 Aug 2024 19:58:06 +0300
Subject: [PATCH 046/242] feat: REST API to allow pasting clipboard (staged)
content into a library (#35199)
---
.../core/djangoapps/content_libraries/api.py | 87 +++++++++++++++----
.../content_libraries/serializers.py | 11 +++
.../content_libraries/tests/base.py | 7 ++
.../tests/test_content_libraries.py | 48 ++++++++++
.../core/djangoapps/content_libraries/urls.py | 2 +
.../djangoapps/content_libraries/views.py | 29 +++++++
6 files changed, 169 insertions(+), 15 deletions(-)
diff --git a/openedx/core/djangoapps/content_libraries/api.py b/openedx/core/djangoapps/content_libraries/api.py
index d2c6eeeeab..17bea80b3a 100644
--- a/openedx/core/djangoapps/content_libraries/api.py
+++ b/openedx/core/djangoapps/content_libraries/api.py
@@ -751,27 +751,30 @@ def set_library_block_olx(usage_key, new_olx_str):
)
-def create_library_block(library_key, block_type, definition_id, user_id=None):
+def validate_can_add_block_to_library(
+ library_key: LibraryLocatorV2,
+ block_type: str,
+ block_id: str,
+) -> tuple[ContentLibrary, LibraryUsageLocatorV2]:
"""
- Create a new XBlock in this library of the specified type (e.g. "html").
- """
- # It's in the serializer as ``definition_id``, but for our purposes, it's
- # the block_id. See the comments in ``LibraryXBlockCreationSerializer`` for
- # more details. TODO: Change the param name once we change the serializer.
- block_id = definition_id
+ Perform checks to validate whether a new block with `block_id` and type `block_type` can be added to
+ the library with key `library_key`.
+ Returns the ContentLibrary that has the passed in `library_key` and newly created LibraryUsageLocatorV2 if
+ validation successful, otherwise raises errors.
+ """
assert isinstance(library_key, LibraryLocatorV2)
- ref = ContentLibrary.objects.get_by_key(library_key)
- if ref.type != COMPLEX:
- if block_type != ref.type:
+ content_library = ContentLibrary.objects.get_by_key(library_key) # type: ignore[attr-defined]
+ if content_library.type != COMPLEX:
+ if block_type != content_library.type:
raise IncompatibleTypesError(
_('Block type "{block_type}" is not compatible with library type "{library_type}".').format(
- block_type=block_type, library_type=ref.type,
+ block_type=block_type, library_type=content_library.type,
)
)
# If adding a component would take us over our max, return an error.
- component_count = authoring_api.get_all_drafts(ref.learning_package.id).count()
+ component_count = authoring_api.get_all_drafts(content_library.learning_package.id).count()
if component_count + 1 > settings.MAX_BLOCKS_PER_CONTENT_LIBRARY:
raise BlockLimitReachedError(
_("Library cannot have more than {} Components").format(
@@ -784,7 +787,7 @@ def create_library_block(library_key, block_type, definition_id, user_id=None):
# Ensure the XBlock type is valid and installed:
XBlock.load_class(block_type) # Will raise an exception if invalid
# Make sure the new ID is not taken already:
- usage_key = LibraryUsageLocatorV2(
+ usage_key = LibraryUsageLocatorV2( # type: ignore[abstract]
lib_key=library_key,
block_type=block_type,
usage_id=block_id,
@@ -793,12 +796,26 @@ def create_library_block(library_key, block_type, definition_id, user_id=None):
if _component_exists(usage_key):
raise LibraryBlockAlreadyExists(f"An XBlock with ID '{usage_key}' already exists")
- _create_component_for_block(ref, usage_key, user_id=user_id)
+ return content_library, usage_key
+
+
+def create_library_block(library_key, block_type, definition_id, user_id=None):
+ """
+ Create a new XBlock in this library of the specified type (e.g. "html").
+ """
+ # It's in the serializer as ``definition_id``, but for our purposes, it's
+ # the block_id. See the comments in ``LibraryXBlockCreationSerializer`` for
+ # more details. TODO: Change the param name once we change the serializer.
+ block_id = definition_id
+
+ content_library, usage_key = validate_can_add_block_to_library(library_key, block_type, block_id)
+
+ _create_component_for_block(content_library, usage_key, user_id)
# Now return the metadata about the new block:
LIBRARY_BLOCK_CREATED.send_event(
library_block=LibraryBlockData(
- library_key=ref.library_key,
+ library_key=content_library.library_key,
usage_key=usage_key
)
)
@@ -820,6 +837,46 @@ def _component_exists(usage_key: UsageKeyV2) -> bool:
return True
+def import_staged_content_from_user_clipboard(library_key: LibraryLocatorV2, user, block_id) -> XBlock:
+ """
+ Create a new library block and populate it with staged content from clipboard
+
+ Returns the newly created library block
+ """
+ from openedx.core.djangoapps.content_staging import api as content_staging_api
+ if not content_staging_api:
+ raise RuntimeError("The required content_staging app is not installed")
+
+ user_clipboard = content_staging_api.get_user_clipboard(user)
+ if not user_clipboard:
+ return None
+
+ olx_str = content_staging_api.get_staged_content_olx(user_clipboard.content.id)
+
+ # TODO: Handle importing over static assets
+
+ content_library, usage_key = validate_can_add_block_to_library(
+ library_key,
+ user_clipboard.content.block_type,
+ block_id
+ )
+
+ # Create component for block then populate it with clipboard data
+ _create_component_for_block(content_library, usage_key, user.id)
+ set_library_block_olx(usage_key, olx_str)
+
+ # Emit library block created event
+ LIBRARY_BLOCK_CREATED.send_event(
+ library_block=LibraryBlockData(
+ library_key=content_library.library_key,
+ usage_key=usage_key
+ )
+ )
+
+ # Now return the metadata about the new block
+ return get_library_block(usage_key)
+
+
def get_or_create_olx_media_type(block_type: str) -> MediaType:
"""
Get or create a MediaType for the block type.
diff --git a/openedx/core/djangoapps/content_libraries/serializers.py b/openedx/core/djangoapps/content_libraries/serializers.py
index e714718a77..497eda8147 100644
--- a/openedx/core/djangoapps/content_libraries/serializers.py
+++ b/openedx/core/djangoapps/content_libraries/serializers.py
@@ -179,6 +179,17 @@ class LibraryXBlockCreationSerializer(serializers.Serializer):
# slugs at the moment, but hopefully we can change this soon.
definition_id = serializers.CharField(validators=(validate_unicode_slug, ))
+ # Optional param specified when pasting data from clipboard instead of
+ # creating new block from scratch
+ staged_content = serializers.CharField(required=False)
+
+
+class LibraryPasteClipboardSerializer(serializers.Serializer):
+ """
+ Serializer for pasting clipboard data into library
+ """
+ block_id = serializers.CharField(validators=(validate_unicode_slug, ))
+
class LibraryXBlockOlxSerializer(serializers.Serializer):
"""
diff --git a/openedx/core/djangoapps/content_libraries/tests/base.py b/openedx/core/djangoapps/content_libraries/tests/base.py
index 40fe3ba949..987133255f 100644
--- a/openedx/core/djangoapps/content_libraries/tests/base.py
+++ b/openedx/core/djangoapps/content_libraries/tests/base.py
@@ -25,6 +25,7 @@ URL_LIB_BLOCKS = URL_LIB_DETAIL + 'blocks/' # Get the list of XBlocks in this l
URL_LIB_TEAM = URL_LIB_DETAIL + 'team/' # Get the list of users/groups authorized to use this library
URL_LIB_TEAM_USER = URL_LIB_TEAM + 'user/{username}/' # Add/edit/remove a user's permission to use this library
URL_LIB_TEAM_GROUP = URL_LIB_TEAM + 'group/{group_name}/' # Add/edit/remove a group's permission to use this library
+URL_LIB_PASTE_CLIPBOARD = URL_LIB_DETAIL + 'paste_clipboard/' # Paste user clipboard (POST) containing Xblock data
URL_LIB_BLOCK = URL_PREFIX + 'blocks/{block_key}/' # Get data about a block, or delete it
URL_LIB_BLOCK_OLX = URL_LIB_BLOCK + 'olx/' # Get or set the OLX of the specified XBlock
URL_LIB_BLOCK_ASSETS = URL_LIB_BLOCK + 'assets/' # List the static asset files of the specified XBlock
@@ -284,6 +285,12 @@ class ContentLibrariesRestApiTest(APITransactionTestCase):
url = URL_LIB_BLOCK_ASSET_FILE.format(block_key=block_key, file_name=file_name)
return self._api('delete', url, None, expect_response)
+ def _paste_clipboard_content_in_library(self, lib_key, block_id, expect_response=200):
+ """ Paste's the users clipboard content into Library """
+ url = URL_LIB_PASTE_CLIPBOARD.format(lib_key=lib_key)
+ data = {"block_id": block_id}
+ return self._api('post', url, data, expect_response)
+
def _render_block_view(self, block_key, view_name, expect_response=200):
"""
Render an XBlock's view in the active application's runtime.
diff --git a/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py b/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py
index 576d506f9d..95b7309b3c 100644
--- a/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py
+++ b/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py
@@ -5,6 +5,7 @@ from unittest.mock import Mock, patch
from unittest import skip
import ddt
+from uuid import uuid4
from django.contrib.auth.models import Group
from django.test.client import Client
from organizations.models import Organization
@@ -29,6 +30,7 @@ from openedx.core.djangoapps.content_libraries.tests.base import (
URL_BLOCK_XBLOCK_HANDLER,
)
from openedx.core.djangoapps.content_libraries.constants import VIDEO, COMPLEX, PROBLEM, CC_4_BY
+from openedx.core.djangoapps.xblock import api as xblock_api
from openedx.core.djangolib.testing.utils import skip_unless_cms
from common.djangoapps.student.tests.factories import UserFactory
@@ -974,6 +976,52 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix
event_receiver.call_args.kwargs
)
+ def test_library_paste_clipboard(self):
+ """
+ Check the a new block is created in the library after pasting from clipboard.
+ The content of the new block should match the content of the block in the clipboard.
+ """
+ # Importing here since this was failing when tests ran in the LMS
+ from openedx.core.djangoapps.content_staging.api import save_xblock_to_user_clipboard
+
+ # Create user to perform tests on
+ author = UserFactory.create(username="Author", email="author@example.com")
+ with self.as_user(author):
+ lib = self._create_library(
+ slug="test_lib_paste_clipboard",
+ title="Paste Clipboard Test Library",
+ description="Testing pasting clipboard in library"
+ )
+ lib_id = lib["id"]
+
+ # Add a 'problem' XBlock to the library:
+ block_data = self._add_block_to_library(lib_id, "problem", "problem1")
+
+ # Get the usage_key of the created block
+ library_key = LibraryLocatorV2.from_string(lib_id)
+ usage_key = LibraryUsageLocatorV2(
+ lib_key=library_key,
+ block_type="problem",
+ usage_id="problem1"
+ )
+
+ # Get the XBlock created in the previous step
+ block = xblock_api.load_block(usage_key, user=author)
+
+ # Copy the block to the user's clipboard
+ save_xblock_to_user_clipboard(block, author.id)
+
+ # Paste the content of the clipboard into the library
+ pasted_block_id = str(uuid4())
+ paste_data = self._paste_clipboard_content_in_library(lib_id, pasted_block_id)
+
+ # Check that the new block was created after the paste and it's content matches
+ # the the block in the clipboard
+ self.assertDictContainsEntries(self._get_library_block(paste_data["id"]), {
+ **block_data,
+ "id": f"lb:CL-TEST:test_lib_paste_clipboard:problem:{pasted_block_id}",
+ })
+
@ddt.ddt
class ContentLibraryXBlockValidationTest(APITestCase):
diff --git a/openedx/core/djangoapps/content_libraries/urls.py b/openedx/core/djangoapps/content_libraries/urls.py
index 022c288e36..6e450df635 100644
--- a/openedx/core/djangoapps/content_libraries/urls.py
+++ b/openedx/core/djangoapps/content_libraries/urls.py
@@ -43,6 +43,8 @@ urlpatterns = [
path('team/group//', views.LibraryTeamGroupView.as_view()),
# Import blocks into this library.
path('import_blocks/', include(import_blocks_router.urls)),
+ # Paste contents of clipboard into library
+ path('paste_clipboard/', views.LibraryPasteClipboardView.as_view()),
])),
path('blocks//', include([
# Get metadata about a specific XBlock in this library, or delete the block:
diff --git a/openedx/core/djangoapps/content_libraries/views.py b/openedx/core/djangoapps/content_libraries/views.py
index 502150b47d..bde8142d3f 100644
--- a/openedx/core/djangoapps/content_libraries/views.py
+++ b/openedx/core/djangoapps/content_libraries/views.py
@@ -112,6 +112,7 @@ from openedx.core.djangoapps.content_libraries.serializers import (
LibraryXBlockStaticFileSerializer,
LibraryXBlockStaticFilesSerializer,
ContentLibraryAddPermissionByEmailSerializer,
+ LibraryPasteClipboardSerializer,
)
import openedx.core.djangoapps.site_configuration.helpers as configuration_helpers
from openedx.core.lib.api.view_utils import view_auth_classes
@@ -502,6 +503,34 @@ class LibraryCommitView(APIView):
return Response({})
+@method_decorator(non_atomic_requests, name="dispatch")
+@view_auth_classes()
+class LibraryPasteClipboardView(GenericAPIView):
+ """
+ Paste content of clipboard into Library.
+ """
+ @convert_exceptions
+ def post(self, request, lib_key_str):
+ """
+ Import the contents of the user's clipboard and paste them into the Library
+ """
+ library_key = LibraryLocatorV2.from_string(lib_key_str)
+ api.require_permission_for_library_key(library_key, request.user, permissions.CAN_EDIT_THIS_CONTENT_LIBRARY)
+ serializer = LibraryPasteClipboardSerializer(data=request.data)
+ serializer.is_valid(raise_exception=True)
+
+ try:
+ result = api.import_staged_content_from_user_clipboard(
+ library_key, request.user, **serializer.validated_data
+ )
+ except api.IncompatibleTypesError as err:
+ raise ValidationError( # lint-amnesty, pylint: disable=raise-missing-from
+ detail={'block_type': str(err)},
+ )
+
+ return Response(LibraryXBlockMetadataSerializer(result).data)
+
+
@method_decorator(non_atomic_requests, name="dispatch")
@view_auth_classes()
class LibraryBlocksView(GenericAPIView):
From ca46c20abb646b8b887e3c4d999f4e069f7eb871 Mon Sep 17 00:00:00 2001
From: Eemaan Amir <57627710+eemaanamir@users.noreply.github.com>
Date: Fri, 16 Aug 2024 14:20:33 +0500
Subject: [PATCH 047/242] fix: update content reported notification (#35315)
---
lms/djangoapps/discussion/django_comment_client/base/tests.py | 2 ++
lms/djangoapps/discussion/rest_api/discussions_notifications.py | 2 +-
.../discussion/rest_api/tests/test_discussions_notifications.py | 2 +-
3 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/lms/djangoapps/discussion/django_comment_client/base/tests.py b/lms/djangoapps/discussion/django_comment_client/base/tests.py
index f8242efa0c..62af24f0ee 100644
--- a/lms/djangoapps/discussion/django_comment_client/base/tests.py
+++ b/lms/djangoapps/discussion/django_comment_client/base/tests.py
@@ -1458,6 +1458,8 @@ class CreateSubCommentUnicodeTestCase(
@disable_signal(views, 'comment_created')
@disable_signal(views, 'comment_voted')
@disable_signal(views, 'comment_deleted')
+@disable_signal(views, 'comment_flagged')
+@disable_signal(views, 'thread_flagged')
class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStoreTestCase, MockRequestSetupMixin):
# Most of the test points use the same ddt data.
# args: user, commentable_id, status_code
diff --git a/lms/djangoapps/discussion/rest_api/discussions_notifications.py b/lms/djangoapps/discussion/rest_api/discussions_notifications.py
index 7249d086cc..21b1e27fcd 100644
--- a/lms/djangoapps/discussion/rest_api/discussions_notifications.py
+++ b/lms/djangoapps/discussion/rest_api/discussions_notifications.py
@@ -314,7 +314,7 @@ class DiscussionNotificationSender:
content_type = thread_types[self.thread.type][getattr(self.thread, 'depth', 0)]
context = {
- 'username': self.creator.username,
+ 'username': self.thread.username,
'content_type': content_type,
'content': thread_body
}
diff --git a/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py b/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py
index eeed292037..b4d3f3d18a 100644
--- a/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py
+++ b/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py
@@ -44,7 +44,7 @@ class TestDiscussionNotificationSender(unittest.TestCase):
self.assertEqual(notification_type, "content_reported")
self.assertEqual(context, {
- 'username': 'test_user',
+ 'username': self.thread.username,
'content_type': expected_content_type,
'content': 'Thread body'
})
From 00772693ef0f29a01872348124b40be4ef3a5c32 Mon Sep 17 00:00:00 2001
From: Muhammad Adeel Tajamul
<77053848+muhammadadeeltajamul@users.noreply.github.com>
Date: Mon, 19 Aug 2024 00:21:43 -0700
Subject: [PATCH 048/242] feat: added unsubscribe url in digest header (#35319)
---
.../djangoapps/notifications/email/utils.py | 25 ++++++++------
.../notifications/digest_footer.html | 2 +-
.../notifications/digest_header.html | 7 ++++
.../notifications/tests/test_views.py | 34 +++++++++++++++++--
.../core/djangoapps/notifications/views.py | 7 ++--
5 files changed, 55 insertions(+), 20 deletions(-)
diff --git a/openedx/core/djangoapps/notifications/email/utils.py b/openedx/core/djangoapps/notifications/email/utils.py
index 1e0f4c81c7..da288750bb 100644
--- a/openedx/core/djangoapps/notifications/email/utils.py
+++ b/openedx/core/djangoapps/notifications/email/utils.py
@@ -7,7 +7,6 @@ import json
from django.conf import settings
from django.contrib.auth import get_user_model
from django.shortcuts import get_object_or_404
-from django.urls import reverse
from pytz import utc
from waffle import get_waffle_flag_model # pylint: disable=invalid-django-waffle-import
@@ -20,7 +19,10 @@ from openedx.core.djangoapps.notifications.base_notification import (
)
from openedx.core.djangoapps.notifications.config.waffle import ENABLE_EMAIL_NOTIFICATIONS
from openedx.core.djangoapps.notifications.email_notifications import EmailCadence
-from openedx.core.djangoapps.notifications.models import CourseNotificationPreference
+from openedx.core.djangoapps.notifications.models import (
+ CourseNotificationPreference,
+ get_course_notification_preference_config_version
+)
from xmodule.modulestore.django import modulestore
from .notification_icons import NotificationTypeIcons
@@ -71,15 +73,7 @@ def get_unsubscribe_link(username, patch):
"""
encrypted_username = encrypt_string(username)
encrypted_patch = encrypt_object(patch)
- kwargs = {
- 'username': encrypted_username,
- 'patch': encrypted_patch
- }
- relative_url = reverse('preference_update_from_encrypted_username_view', kwargs=kwargs)
- protocol = 'https://'
- if settings.DEBUG:
- protocol = 'http://'
- return f"{protocol}{settings.LMS_BASE}{relative_url}"
+ return f"{settings.LEARNING_MICROFRONTEND_URL}/preferences-unsubscribe/{encrypted_username}/{encrypted_patch}"
def create_email_template_context(username):
@@ -363,6 +357,14 @@ def update_user_preferences_from_patch(encrypted_username, encrypted_patch):
return COURSE_NOTIFICATION_APPS[app_name]['core_email_cadence']
return COURSE_NOTIFICATION_TYPES[notification_type]['email_cadence']
+ def get_updated_preference(pref):
+ """
+ Update preference if config version doesn't match
+ """
+ if pref.config_version != get_course_notification_preference_config_version():
+ pref = pref.get_user_course_preference(pref.user_id, pref.course_id)
+ return pref
+
course_ids = CourseEnrollment.objects.filter(user=user).values_list('course_id', flat=True)
CourseNotificationPreference.objects.bulk_create(
[
@@ -375,6 +377,7 @@ def update_user_preferences_from_patch(encrypted_username, encrypted_patch):
# pylint: disable=too-many-nested-blocks
for preference in preferences:
+ preference = get_updated_preference(preference)
preference_json = preference.notification_preference_config
for app_name, app_prefs in preference_json.items():
if not is_name_match(app_name, app_value):
diff --git a/openedx/core/djangoapps/notifications/templates/notifications/digest_footer.html b/openedx/core/djangoapps/notifications/templates/notifications/digest_footer.html
index 0419b25665..4fa903d127 100644
--- a/openedx/core/djangoapps/notifications/templates/notifications/digest_footer.html
+++ b/openedx/core/djangoapps/notifications/templates/notifications/digest_footer.html
@@ -38,7 +38,7 @@
Notification Settings
- Unsubscribe
+ Unsubscribe from email digest for learning activity
diff --git a/openedx/core/djangoapps/notifications/templates/notifications/digest_header.html b/openedx/core/djangoapps/notifications/templates/notifications/digest_header.html
index 7957524e8a..1f22ced200 100644
--- a/openedx/core/djangoapps/notifications/templates/notifications/digest_header.html
+++ b/openedx/core/djangoapps/notifications/templates/notifications/digest_header.html
@@ -5,6 +5,13 @@
style="background: #00262b; color: white; width: 100%; padding: 1.5rem"
>
+
+
+
+ Unsubscribe
+
+
+
diff --git a/openedx/core/djangoapps/notifications/tests/test_views.py b/openedx/core/djangoapps/notifications/tests/test_views.py
index d2968749ef..e40e520789 100644
--- a/openedx/core/djangoapps/notifications/tests/test_views.py
+++ b/openedx/core/djangoapps/notifications/tests/test_views.py
@@ -28,9 +28,13 @@ from openedx.core.djangoapps.django_comment_common.models import (
)
from openedx.core.djangoapps.notifications.config.waffle import ENABLE_NOTIFICATIONS
from openedx.core.djangoapps.notifications.email_notifications import EmailCadence
-from openedx.core.djangoapps.notifications.models import CourseNotificationPreference, Notification
+from openedx.core.djangoapps.notifications.models import (
+ CourseNotificationPreference,
+ Notification,
+ get_course_notification_preference_config_version
+)
from openedx.core.djangoapps.notifications.serializers import NotificationCourseEnrollmentSerializer
-from openedx.core.djangoapps.notifications.email.utils import get_unsubscribe_link
+from openedx.core.djangoapps.notifications.email.utils import encrypt_object, encrypt_string
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@@ -910,7 +914,13 @@ class UpdatePreferenceFromEncryptedDataView(ModuleStoreTestCase):
"""
Tests if preference is updated when url is hit
"""
- url = get_unsubscribe_link(self.user.username, {'channel': 'email', 'value': False})
+ user_hash = encrypt_string(self.user.username)
+ patch_hash = encrypt_object({'channel': 'email', 'value': False})
+ url_params = {
+ "username": user_hash,
+ "patch": patch_hash
+ }
+ url = reverse("preference_update_from_encrypted_username_view", kwargs=url_params)
func = getattr(self.client, request_type)
response = func(url)
assert response.status_code == status.HTTP_200_OK
@@ -921,6 +931,24 @@ class UpdatePreferenceFromEncryptedDataView(ModuleStoreTestCase):
assert type_prefs['email'] is False
assert type_prefs['email_cadence'] == EmailCadence.NEVER
+ def test_if_config_version_is_updated(self):
+ """
+ Tests if preference version is updated before applying patch data
+ """
+ preference = CourseNotificationPreference.objects.get(user=self.user, course_id=self.course.id)
+ preference.config_version -= 1
+ preference.save()
+ user_hash = encrypt_string(self.user.username)
+ patch_hash = encrypt_object({'channel': 'email', 'value': False})
+ url_params = {
+ "username": user_hash,
+ "patch": patch_hash
+ }
+ url = reverse("preference_update_from_encrypted_username_view", kwargs=url_params)
+ self.client.get(url)
+ preference = CourseNotificationPreference.objects.get(user=self.user, course_id=self.course.id)
+ assert preference.config_version == get_course_notification_preference_config_version()
+
def remove_notifications_with_visibility_settings(expected_response):
"""
diff --git a/openedx/core/djangoapps/notifications/views.py b/openedx/core/djangoapps/notifications/views.py
index ee5e282d90..fdc91c12a9 100644
--- a/openedx/core/djangoapps/notifications/views.py
+++ b/openedx/core/djangoapps/notifications/views.py
@@ -5,7 +5,7 @@ from datetime import datetime, timedelta
from django.conf import settings
from django.db.models import Count
-from django.shortcuts import get_object_or_404, render
+from django.shortcuts import get_object_or_404
from django.utils.translation import gettext as _
from opaque_keys.edx.keys import CourseKey
from pytz import UTC
@@ -441,7 +441,4 @@ def preference_update_from_encrypted_username_view(request, username, patch):
username and patch must be string
"""
update_user_preferences_from_patch(username, patch)
- context = {
- "notification_preferences_url": f"{settings.ACCOUNT_MICROFRONTEND_URL}/notifications"
- }
- return render(request, "notifications/email_digest_preference_update.html", context=context)
+ return Response({"result": "success"}, status=status.HTTP_200_OK)
From 6bcbb1df29904901f4b28958b6b1c9983f2c450c Mon Sep 17 00:00:00 2001
From: Awais Qureshi
Date: Mon, 19 Aug 2024 14:25:08 +0500
Subject: [PATCH 049/242] feat: Upgrading api to use drf 5th api.
register_and_enroll_students api (#35084)
* feat: upgrading simple api to drf compatible.
---
lms/djangoapps/instructor/tests/test_api.py | 19 +
lms/djangoapps/instructor/views/api.py | 540 ++++++++++----------
lms/djangoapps/instructor/views/api_urls.py | 2 +-
3 files changed, 293 insertions(+), 268 deletions(-)
diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py
index 2548cc4f41..b0e533ee6f 100644
--- a/lms/djangoapps/instructor/tests/test_api.py
+++ b/lms/djangoapps/instructor/tests/test_api.py
@@ -642,6 +642,25 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCas
last_name='Student'
)
+ def test_api_without_login(self):
+ """
+ verify in case of no authentication it returns 401.
+ """
+ self.client.logout()
+ uploaded_file = SimpleUploadedFile("temp.jpg", io.BytesIO(b"some initial binary data: \x00\x01").read())
+ response = self.client.post(self.url, {'students_list': uploaded_file})
+ assert response.status_code == 401
+
+ def test_api_without_permission(self):
+ """
+ verify in case of no authentication it returns 403.
+ """
+ # removed role from course for instructor
+ CourseInstructorRole(self.course.id).remove_users(self.instructor)
+ uploaded_file = SimpleUploadedFile("temp.jpg", io.BytesIO(b"some initial binary data: \x00\x01").read())
+ response = self.client.post(self.url, {'students_list': uploaded_file})
+ assert response.status_code == 403
+
@patch('lms.djangoapps.instructor.views.api.log.info')
@ddt.data(
b"test_student@example.com,test_student_1,tester1,USA", # Typical use case.
diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py
index 6abfc81e75..ceb699cd90 100644
--- a/lms/djangoapps/instructor/views/api.py
+++ b/lms/djangoapps/instructor/views/api.py
@@ -283,299 +283,305 @@ def require_finance_admin(func):
return wrapped
-@require_POST
-@ensure_csrf_cookie
-@cache_control(no_cache=True, no_store=True, must_revalidate=True)
-@require_course_permission(permissions.CAN_ENROLL)
-def register_and_enroll_students(request, course_id): # pylint: disable=too-many-statements
+@method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True), name='dispatch')
+class RegisterAndEnrollStudents(APIView):
"""
Create new account and Enroll students in this course.
- Passing a csv file that contains a list of students.
- Order in csv should be the following email = 0; username = 1; name = 2; country = 3.
- If there are more than 4 columns in the csv: cohort = 4, course mode = 5.
- Requires staff access.
-
- -If the email address and username already exists and the user is enrolled in the course,
- do nothing (including no email gets sent out)
-
- -If the email address already exists, but the username is different,
- match on the email address only and continue to enroll the user in the course using the email address
- as the matching criteria. Note the change of username as a warning message (but not a failure).
- Send a standard enrollment email which is the same as the existing manual enrollment
-
- -If the username already exists (but not the email), assume it is a different user and fail
- to create the new account.
- The failure will be messaged in a response in the browser.
"""
+ permission_classes = (IsAuthenticated, permissions.InstructorPermission)
+ permission_name = permissions.CAN_ENROLL
- if not configuration_helpers.get_value(
- 'ALLOW_AUTOMATED_SIGNUPS',
- settings.FEATURES.get('ALLOW_AUTOMATED_SIGNUPS', False),
- ):
- return HttpResponseForbidden()
+ @method_decorator(ensure_csrf_cookie)
+ def post(self, request, course_id): # pylint: disable=too-many-statements
+ """
+ Create new account and Enroll students in this course.
+ Passing a csv file that contains a list of students.
+ Order in csv should be the following email = 0; username = 1; name = 2; country = 3.
+ If there are more than 4 columns in the csv: cohort = 4, course mode = 5.
+ Requires staff access.
- course_id = CourseKey.from_string(course_id)
- warnings = []
- row_errors = []
- general_errors = []
+ -If the email address and username already exists and the user is enrolled in the course,
+ do nothing (including no email gets sent out)
- # email-students is a checkbox input type; will be present in POST if checked, absent otherwise
- notify_by_email = 'email-students' in request.POST
+ -If the email address already exists, but the username is different,
+ match on the email address only and continue to enroll the user in the course using the email address
+ as the matching criteria. Note the change of username as a warning message (but not a failure).
+ Send a standard enrollment email which is the same as the existing manual enrollment
- # for white labels we use 'shopping cart' which uses CourseMode.HONOR as
- # course mode for creating course enrollments.
- if CourseMode.is_white_label(course_id):
- default_course_mode = CourseMode.HONOR
- else:
- default_course_mode = None
+ -If the username already exists (but not the email), assume it is a different user and fail
+ to create the new account.
+ The failure will be messaged in a response in the browser.
+ """
+ if not configuration_helpers.get_value(
+ 'ALLOW_AUTOMATED_SIGNUPS',
+ settings.FEATURES.get('ALLOW_AUTOMATED_SIGNUPS', False),
+ ):
+ return HttpResponseForbidden()
- # Allow bulk enrollments in all non-expired course modes including "credit" (which is non-selectable)
- valid_course_modes = set(map(lambda x: x.slug, CourseMode.modes_for_course(
- course_id=course_id,
- only_selectable=False,
- include_expired=False,
- )))
+ course_id = CourseKey.from_string(course_id)
+ warnings = []
+ row_errors = []
+ general_errors = []
- if 'students_list' in request.FILES: # lint-amnesty, pylint: disable=too-many-nested-blocks
- students = []
+ # email-students is a checkbox input type; will be present in POST if checked, absent otherwise
+ notify_by_email = 'email-students' in request.POST
- try:
- upload_file = request.FILES.get('students_list')
- if upload_file.name.endswith('.csv'):
- students = list(csv.reader(upload_file.read().decode('utf-8-sig').splitlines()))
- course = get_course_by_id(course_id)
- else:
- general_errors.append({
- 'username': '', 'email': '',
- 'response': _(
- 'Make sure that the file you upload is in CSV format with no extraneous characters or rows.')
- })
+ # for white labels we use 'shopping cart' which uses CourseMode.HONOR as
+ # course mode for creating course enrollments.
+ if CourseMode.is_white_label(course_id):
+ default_course_mode = CourseMode.HONOR
+ else:
+ default_course_mode = None
- except Exception: # pylint: disable=broad-except
- general_errors.append({
- 'username': '', 'email': '', 'response': _('Could not read uploaded file.')
- })
- finally:
- upload_file.close()
+ # Allow bulk enrollments in all non-expired course modes including "credit" (which is non-selectable)
+ valid_course_modes = set(map(lambda x: x.slug, CourseMode.modes_for_course(
+ course_id=course_id,
+ only_selectable=False,
+ include_expired=False,
+ )))
- generated_passwords = []
- # To skip fetching cohorts from the DB while iterating on students,
- # {: CourseUserGroup}
- cohorts_cache = {}
- already_warned_not_cohorted = False
- extra_fields_is_enabled = configuration_helpers.get_value(
- 'ENABLE_AUTOMATED_SIGNUPS_EXTRA_FIELDS',
- settings.FEATURES.get('ENABLE_AUTOMATED_SIGNUPS_EXTRA_FIELDS', False),
- )
+ if 'students_list' in request.FILES: # lint-amnesty, pylint: disable=too-many-nested-blocks
+ students = []
- # Iterate each student in the uploaded csv file.
- for row_num, student in enumerate(students, 1):
-
- # Verify that we have the expected number of columns in every row
- # but allow for blank lines.
- if not student:
- continue
-
- if extra_fields_is_enabled:
- is_valid_csv = 4 <= len(student) <= 6
- error = _('Data in row #{row_num} must have between four and six columns: '
- 'email, username, full name, country, cohort, and course mode. '
- 'The last two columns are optional.').format(row_num=row_num)
- else:
- is_valid_csv = len(student) == 4
- error = _('Data in row #{row_num} must have exactly four columns: '
- 'email, username, full name, and country.').format(row_num=row_num)
-
- if not is_valid_csv:
- general_errors.append({
- 'username': '',
- 'email': '',
- 'response': error
- })
- continue
-
- # Extract each column, handle optional columns if they exist.
- email, username, name, country, *optional_cols = student
- if optional_cols:
- optional_cols.append(default_course_mode)
- cohort_name, course_mode, *_tail = optional_cols
- else:
- cohort_name = None
- course_mode = None
-
- # Validate cohort name, and get the cohort object. Skip if course
- # is not cohorted.
- cohort = None
-
- if cohort_name and not already_warned_not_cohorted:
- if not is_course_cohorted(course_id):
- row_errors.append({
- 'username': username,
- 'email': email,
- 'response': _('Course is not cohorted but cohort provided. '
- 'Ignoring cohort assignment for all users.')
- })
- already_warned_not_cohorted = True
- elif cohort_name in cohorts_cache:
- cohort = cohorts_cache[cohort_name]
+ try:
+ upload_file = request.FILES.get('students_list')
+ if upload_file.name.endswith('.csv'):
+ students = list(csv.reader(upload_file.read().decode('utf-8-sig').splitlines()))
+ course = get_course_by_id(course_id)
else:
- # Don't attempt to create cohort or assign student if cohort
- # does not exist.
- try:
- cohort = get_cohort_by_name(course_id, cohort_name)
- except CourseUserGroup.DoesNotExist:
+ general_errors.append({
+ 'username': '', 'email': '',
+ 'response': _(
+ 'Make sure that the file you upload is in CSV format with no '
+ 'extraneous characters or rows.')
+ })
+
+ except Exception: # pylint: disable=broad-except
+ general_errors.append({
+ 'username': '', 'email': '', 'response': _('Could not read uploaded file.')
+ })
+ finally:
+ upload_file.close()
+
+ generated_passwords = []
+ # To skip fetching cohorts from the DB while iterating on students,
+ # {: CourseUserGroup}
+ cohorts_cache = {}
+ already_warned_not_cohorted = False
+ extra_fields_is_enabled = configuration_helpers.get_value(
+ 'ENABLE_AUTOMATED_SIGNUPS_EXTRA_FIELDS',
+ settings.FEATURES.get('ENABLE_AUTOMATED_SIGNUPS_EXTRA_FIELDS', False),
+ )
+
+ # Iterate each student in the uploaded csv file.
+ for row_num, student in enumerate(students, 1):
+
+ # Verify that we have the expected number of columns in every row
+ # but allow for blank lines.
+ if not student:
+ continue
+
+ if extra_fields_is_enabled:
+ is_valid_csv = 4 <= len(student) <= 6
+ error = _('Data in row #{row_num} must have between four and six columns: '
+ 'email, username, full name, country, cohort, and course mode. '
+ 'The last two columns are optional.').format(row_num=row_num)
+ else:
+ is_valid_csv = len(student) == 4
+ error = _('Data in row #{row_num} must have exactly four columns: '
+ 'email, username, full name, and country.').format(row_num=row_num)
+
+ if not is_valid_csv:
+ general_errors.append({
+ 'username': '',
+ 'email': '',
+ 'response': error
+ })
+ continue
+
+ # Extract each column, handle optional columns if they exist.
+ email, username, name, country, *optional_cols = student
+ if optional_cols:
+ optional_cols.append(default_course_mode)
+ cohort_name, course_mode, *_tail = optional_cols
+ else:
+ cohort_name = None
+ course_mode = None
+
+ # Validate cohort name, and get the cohort object. Skip if course
+ # is not cohorted.
+ cohort = None
+
+ if cohort_name and not already_warned_not_cohorted:
+ if not is_course_cohorted(course_id):
row_errors.append({
'username': username,
'email': email,
- 'response': _('Cohort name not found: {cohort}. '
- 'Ignoring cohort assignment for '
- 'all users.').format(cohort=cohort_name)
+ 'response': _('Course is not cohorted but cohort provided. '
+ 'Ignoring cohort assignment for all users.')
})
- cohorts_cache[cohort_name] = cohort
-
- # Validate course mode.
- if not course_mode:
- course_mode = default_course_mode
-
- if (course_mode is not None
- and course_mode not in valid_course_modes):
- # If `default is None` and the user is already enrolled,
- # `CourseEnrollment.change_mode()` will not update the mode,
- # hence two error messages.
- if default_course_mode is None:
- err_msg = _(
- 'Invalid course mode: {mode}. Falling back to the '
- 'default mode, or keeping the current mode in case the '
- 'user is already enrolled.'
- ).format(mode=course_mode)
- else:
- err_msg = _(
- 'Invalid course mode: {mode}. Failling back to '
- '{default_mode}, or resetting to {default_mode} in case '
- 'the user is already enrolled.'
- ).format(mode=course_mode, default_mode=default_course_mode)
- row_errors.append({
- 'username': username,
- 'email': email,
- 'response': err_msg,
- })
- course_mode = default_course_mode
-
- email_params = get_email_params(course, True, secure=request.is_secure())
- try:
- validate_email(email) # Raises ValidationError if invalid
- except ValidationError:
- row_errors.append({
- 'username': username,
- 'email': email,
- 'response': _('Invalid email {email_address}.').format(email_address=email)
- })
- else:
- if User.objects.filter(email=email).exists():
- # Email address already exists. assume it is the correct user
- # and just register the user in the course and send an enrollment email.
- user = User.objects.get(email=email)
-
- # see if it is an exact match with email and username
- # if it's not an exact match then just display a warning message, but continue onwards
- if not User.objects.filter(email=email, username=username).exists():
- warning_message = _(
- 'An account with email {email} exists but the provided username {username} '
- 'is different. Enrolling anyway with {email}.'
- ).format(email=email, username=username)
-
- warnings.append({
- 'username': username, 'email': email, 'response': warning_message
- })
- log.warning('email %s already exist', email)
+ already_warned_not_cohorted = True
+ elif cohort_name in cohorts_cache:
+ cohort = cohorts_cache[cohort_name]
else:
- log.info(
- "user already exists with username '%s' and email '%s'",
- username,
- email
- )
-
- # enroll a user if it is not already enrolled.
- if not is_user_enrolled_in_course(user, course_id):
- # Enroll user to the course and add manual enrollment audit trail
- create_manual_course_enrollment(
- user=user,
- course_id=course_id,
- mode=course_mode,
- enrolled_by=request.user,
- reason='Enrolling via csv upload',
- state_transition=UNENROLLED_TO_ENROLLED,
- )
- enroll_email(course_id=course_id,
- student_email=email,
- auto_enroll=True,
- email_students=notify_by_email,
- email_params=email_params)
- else:
- # update the course mode if already enrolled
- existing_enrollment = CourseEnrollment.get_enrollment(user, course_id)
- if existing_enrollment.mode != course_mode:
- existing_enrollment.change_mode(mode=course_mode)
- if cohort:
+ # Don't attempt to create cohort or assign student if cohort
+ # does not exist.
try:
- add_user_to_cohort(cohort, user)
- except ValueError:
- # user already in this cohort; ignore
- pass
- elif is_email_retired(email):
- # We are either attempting to enroll a retired user or create a new user with an email which is
- # already associated with a retired account. Simply block these attempts.
- row_errors.append({
- 'username': username,
- 'email': email,
- 'response': _('Invalid email {email_address}.').format(email_address=email),
- })
- log.warning('Email address %s is associated with a retired user, so course enrollment was ' + # lint-amnesty, pylint: disable=logging-not-lazy
- 'blocked.', email)
- else:
- # This email does not yet exist, so we need to create a new account
- # If username already exists in the database, then create_and_enroll_user
- # will raise an IntegrityError exception.
- password = generate_unique_password(generated_passwords)
- errors = create_and_enroll_user(
- email,
- username,
- name,
- country,
- password,
- course_id,
- course_mode,
- request.user,
- email_params,
- email_user=notify_by_email,
- )
- row_errors.extend(errors)
- if cohort:
- try:
- add_user_to_cohort(cohort, email)
- except ValueError:
- # user already in this cohort; ignore
- # NOTE: Checking this here may be unnecessary if we can prove that a new user will never be
- # automatically assigned to a cohort from the above.
- pass
- except ValidationError:
+ cohort = get_cohort_by_name(course_id, cohort_name)
+ except CourseUserGroup.DoesNotExist:
row_errors.append({
'username': username,
'email': email,
- 'response': _('Invalid email {email_address}.').format(email_address=email),
+ 'response': _('Cohort name not found: {cohort}. '
+ 'Ignoring cohort assignment for '
+ 'all users.').format(cohort=cohort_name)
})
+ cohorts_cache[cohort_name] = cohort
- else:
- general_errors.append({
- 'username': '', 'email': '', 'response': _('File is not attached.')
- })
+ # Validate course mode.
+ if not course_mode:
+ course_mode = default_course_mode
- results = {
- 'row_errors': row_errors,
- 'general_errors': general_errors,
- 'warnings': warnings
- }
- return JsonResponse(results)
+ if (course_mode is not None
+ and course_mode not in valid_course_modes):
+ # If `default is None` and the user is already enrolled,
+ # `CourseEnrollment.change_mode()` will not update the mode,
+ # hence two error messages.
+ if default_course_mode is None:
+ err_msg = _(
+ 'Invalid course mode: {mode}. Falling back to the '
+ 'default mode, or keeping the current mode in case the '
+ 'user is already enrolled.'
+ ).format(mode=course_mode)
+ else:
+ err_msg = _(
+ 'Invalid course mode: {mode}. Failling back to '
+ '{default_mode}, or resetting to {default_mode} in case '
+ 'the user is already enrolled.'
+ ).format(mode=course_mode, default_mode=default_course_mode)
+ row_errors.append({
+ 'username': username,
+ 'email': email,
+ 'response': err_msg,
+ })
+ course_mode = default_course_mode
+
+ email_params = get_email_params(course, True, secure=request.is_secure())
+ try:
+ validate_email(email) # Raises ValidationError if invalid
+ except ValidationError:
+ row_errors.append({
+ 'username': username,
+ 'email': email,
+ 'response': _('Invalid email {email_address}.').format(email_address=email)
+ })
+ else:
+ if User.objects.filter(email=email).exists():
+ # Email address already exists. assume it is the correct user
+ # and just register the user in the course and send an enrollment email.
+ user = User.objects.get(email=email)
+
+ # see if it is an exact match with email and username
+ # if it's not an exact match then just display a warning message, but continue onwards
+ if not User.objects.filter(email=email, username=username).exists():
+ warning_message = _(
+ 'An account with email {email} exists but the provided username {username} '
+ 'is different. Enrolling anyway with {email}.'
+ ).format(email=email, username=username)
+
+ warnings.append({
+ 'username': username, 'email': email, 'response': warning_message
+ })
+ log.warning('email %s already exist', email)
+ else:
+ log.info(
+ "user already exists with username '%s' and email '%s'",
+ username,
+ email
+ )
+
+ # enroll a user if it is not already enrolled.
+ if not is_user_enrolled_in_course(user, course_id):
+ # Enroll user to the course and add manual enrollment audit trail
+ create_manual_course_enrollment(
+ user=user,
+ course_id=course_id,
+ mode=course_mode,
+ enrolled_by=request.user,
+ reason='Enrolling via csv upload',
+ state_transition=UNENROLLED_TO_ENROLLED,
+ )
+ enroll_email(course_id=course_id,
+ student_email=email,
+ auto_enroll=True,
+ email_students=notify_by_email,
+ email_params=email_params)
+ else:
+ # update the course mode if already enrolled
+ existing_enrollment = CourseEnrollment.get_enrollment(user, course_id)
+ if existing_enrollment.mode != course_mode:
+ existing_enrollment.change_mode(mode=course_mode)
+ if cohort:
+ try:
+ add_user_to_cohort(cohort, user)
+ except ValueError:
+ # user already in this cohort; ignore
+ pass
+ elif is_email_retired(email):
+ # We are either attempting to enroll a retired user or create a new user with an email which is
+ # already associated with a retired account. Simply block these attempts.
+ row_errors.append({
+ 'username': username,
+ 'email': email,
+ 'response': _('Invalid email {email_address}.').format(email_address=email),
+ })
+ log.warning('Email address %s is associated with a retired user, so course enrollment was ' + # lint-amnesty, pylint: disable=logging-not-lazy
+ 'blocked.', email)
+ else:
+ # This email does not yet exist, so we need to create a new account
+ # If username already exists in the database, then create_and_enroll_user
+ # will raise an IntegrityError exception.
+ password = generate_unique_password(generated_passwords)
+ errors = create_and_enroll_user(
+ email,
+ username,
+ name,
+ country,
+ password,
+ course_id,
+ course_mode,
+ request.user,
+ email_params,
+ email_user=notify_by_email,
+ )
+ row_errors.extend(errors)
+ if cohort:
+ try:
+ add_user_to_cohort(cohort, email)
+ except ValueError:
+ # user already in this cohort; ignore
+ # NOTE: Checking this here may be unnecessary if we can prove that a
+ # new user will never be
+ # automatically assigned to a cohort from the above.
+ pass
+ except ValidationError:
+ row_errors.append({
+ 'username': username,
+ 'email': email,
+ 'response': _('Invalid email {email_address}.').format(email_address=email),
+ })
+
+ else:
+ general_errors.append({
+ 'username': '', 'email': '', 'response': _('File is not attached.')
+ })
+
+ results = {
+ 'row_errors': row_errors,
+ 'general_errors': general_errors,
+ 'warnings': warnings
+ }
+ return JsonResponse(results)
def generate_random_string(length):
diff --git a/lms/djangoapps/instructor/views/api_urls.py b/lms/djangoapps/instructor/views/api_urls.py
index fc9d5a3427..08b68b0760 100644
--- a/lms/djangoapps/instructor/views/api_urls.py
+++ b/lms/djangoapps/instructor/views/api_urls.py
@@ -22,7 +22,7 @@ v1_api_urls = [
urlpatterns = [
path('students_update_enrollment', api.students_update_enrollment, name='students_update_enrollment'),
- path('register_and_enroll_students', api.register_and_enroll_students, name='register_and_enroll_students'),
+ path('register_and_enroll_students', api.RegisterAndEnrollStudents.as_view(), name='register_and_enroll_students'),
path('list_course_role_members', api.ListCourseRoleMembersView.as_view(), name='list_course_role_members'),
path('modify_access', api.modify_access, name='modify_access'),
path('bulk_beta_modify_access', api.bulk_beta_modify_access, name='bulk_beta_modify_access'),
From 65258117d83142b6c5db40c9a21257455360d13b Mon Sep 17 00:00:00 2001
From: Navin Karkera
Date: Tue, 6 Aug 2024 19:38:42 +0530
Subject: [PATCH 050/242] fix: delete discarded drafts from meilisearch index
---
openedx/core/djangoapps/content/search/api.py | 37 ++++++++++++++++++-
.../core/djangoapps/content/search/tasks.py | 1 +
.../core/djangoapps/content_libraries/api.py | 5 ++-
3 files changed, 40 insertions(+), 3 deletions(-)
diff --git a/openedx/core/djangoapps/content/search/api.py b/openedx/core/djangoapps/content/search/api.py
index 9473dabbe4..e1ae589f89 100644
--- a/openedx/core/djangoapps/content/search/api.py
+++ b/openedx/core/djangoapps/content/search/api.py
@@ -18,7 +18,7 @@ from meilisearch import Client as MeilisearchClient
from meilisearch.errors import MeilisearchError
from meilisearch.models.task import TaskInfo
from opaque_keys.edx.keys import UsageKey
-from opaque_keys.edx.locator import LibraryLocatorV2
+from opaque_keys.edx.locator import LibraryLocatorV2, LibraryUsageLocatorV2
from common.djangoapps.student.roles import GlobalStaff
from rest_framework.request import Request
from common.djangoapps.student.role_helpers import get_course_roles
@@ -467,6 +467,26 @@ def delete_index_doc(usage_key: UsageKey) -> None:
_wait_for_meili_tasks(tasks)
+def delete_index_docs(meili_ids: list[str]) -> None:
+ """
+ Deletes documents for the given XBlocks from the search index
+
+ Args:
+ meili_ids (list[str]): List of meili_ids from usage keys of the XBlocks to be removed from the index
+ """
+ current_rebuild_index_name = _get_running_rebuild_index_name()
+
+ client = _get_meilisearch_client()
+
+ tasks = []
+ if current_rebuild_index_name:
+ # If there is a rebuild in progress, the document will also be deleted from the new index.
+ tasks.append(client.index(current_rebuild_index_name).delete_documents(meili_ids))
+ tasks.append(client.index(STUDIO_INDEX_NAME).delete_documents(meili_ids))
+
+ _wait_for_meili_tasks(tasks)
+
+
def upsert_library_block_index_doc(usage_key: UsageKey) -> None:
"""
Creates or updates the document for the given Library Block in the search index
@@ -495,6 +515,21 @@ def upsert_content_library_index_docs(library_key: LibraryLocatorV2) -> None:
_update_index_docs(docs)
+def delete_content_library_index_docs(library_key: LibraryLocatorV2) -> None:
+ """
+ Deletes the discarded draft documents for the given Content Library in the search index
+ """
+ meili_ids = []
+ for component in lib_api.get_library_components(library_key, draft=False, published=False):
+ usage_key = LibraryUsageLocatorV2(
+ library_key,
+ component.component_type.name,
+ component.local_key,
+ )
+ meili_ids.append(meili_id_from_opaque_key(usage_key))
+ delete_index_docs(meili_ids)
+
+
def upsert_block_tags_index_docs(usage_key: UsageKey):
"""
Updates the tags data in documents for the given Course/Library block
diff --git a/openedx/core/djangoapps/content/search/tasks.py b/openedx/core/djangoapps/content/search/tasks.py
index 06ea3e777c..2eacd02c76 100644
--- a/openedx/core/djangoapps/content/search/tasks.py
+++ b/openedx/core/djangoapps/content/search/tasks.py
@@ -80,4 +80,5 @@ def update_content_library_index_docs(library_key_str: str) -> None:
log.info("Updating content index documents for library with id: %s", library_key)
+ api.delete_content_library_index_docs(library_key)
api.upsert_content_library_index_docs(library_key)
diff --git a/openedx/core/djangoapps/content_libraries/api.py b/openedx/core/djangoapps/content_libraries/api.py
index 17bea80b3a..5a956b9613 100644
--- a/openedx/core/djangoapps/content_libraries/api.py
+++ b/openedx/core/djangoapps/content_libraries/api.py
@@ -642,7 +642,7 @@ def _get_library_component_tags_count(library_key) -> dict:
return get_object_tag_counts(library_key_pattern, count_implicit=True)
-def get_library_components(library_key, text_search=None, block_types=None) -> QuerySet[Component]:
+def get_library_components(library_key, text_search=None, block_types=None, draft=True, published=None) -> QuerySet[Component]:
"""
Get the library components and filter.
@@ -653,7 +653,8 @@ def get_library_components(library_key, text_search=None, block_types=None) -> Q
learning_package = lib.learning_package
components = authoring_api.get_components(
learning_package.id,
- draft=True,
+ draft=draft,
+ published=published,
namespace='xblock.v1',
type_names=block_types,
draft_title=text_search,
From 2efff2697bed9a1e952c64a129d9b318d897dd04 Mon Sep 17 00:00:00 2001
From: Navin Karkera
Date: Wed, 7 Aug 2024 12:12:25 +0530
Subject: [PATCH 051/242] refactor: parallelize content block update on discard
---
openedx/core/djangoapps/content/search/api.py | 25 +++++++++++--------
.../djangoapps/content/search/handlers.py | 3 ++-
.../core/djangoapps/content/search/tasks.py | 7 +++---
3 files changed, 21 insertions(+), 14 deletions(-)
diff --git a/openedx/core/djangoapps/content/search/api.py b/openedx/core/djangoapps/content/search/api.py
index e1ae589f89..776de6738e 100644
--- a/openedx/core/djangoapps/content/search/api.py
+++ b/openedx/core/djangoapps/content/search/api.py
@@ -143,7 +143,7 @@ def _wait_for_meili_task(info: TaskInfo) -> None:
raise MeilisearchError(err_reason)
-def _wait_for_meili_tasks(info_list: list[TaskInfo]) -> None:
+def wait_for_meili_tasks(info_list: list[TaskInfo]) -> None:
"""
Simple helper method to wait for multiple Meilisearch tasks to complete
"""
@@ -231,7 +231,7 @@ def _recurse_children(block, fn, status_cb: Callable[[str], None] | None = None)
fn(child)
-def _update_index_docs(docs) -> None:
+def _update_index_docs(docs, wait=True) -> list[TaskInfo]:
"""
Helper function that updates the documents in the search index
@@ -249,7 +249,9 @@ def _update_index_docs(docs) -> None:
tasks.append(client.index(current_rebuild_index_name).update_documents(docs))
tasks.append(client.index(STUDIO_INDEX_NAME).update_documents(docs))
- _wait_for_meili_tasks(tasks)
+ if wait:
+ wait_for_meili_tasks(tasks)
+ return tasks
def only_if_meilisearch_enabled(f):
@@ -464,10 +466,10 @@ def delete_index_doc(usage_key: UsageKey) -> None:
tasks.append(client.index(current_rebuild_index_name).delete_document(meili_id_from_opaque_key(usage_key)))
tasks.append(client.index(STUDIO_INDEX_NAME).delete_document(meili_id_from_opaque_key(usage_key)))
- _wait_for_meili_tasks(tasks)
+ wait_for_meili_tasks(tasks)
-def delete_index_docs(meili_ids: list[str]) -> None:
+def delete_index_docs(meili_ids: list[str], wait=True) -> list[TaskInfo]:
"""
Deletes documents for the given XBlocks from the search index
@@ -484,7 +486,9 @@ def delete_index_docs(meili_ids: list[str]) -> None:
tasks.append(client.index(current_rebuild_index_name).delete_documents(meili_ids))
tasks.append(client.index(STUDIO_INDEX_NAME).delete_documents(meili_ids))
- _wait_for_meili_tasks(tasks)
+ if wait:
+ wait_for_meili_tasks(tasks)
+ return tasks
def upsert_library_block_index_doc(usage_key: UsageKey) -> None:
@@ -502,7 +506,7 @@ def upsert_library_block_index_doc(usage_key: UsageKey) -> None:
_update_index_docs(docs)
-def upsert_content_library_index_docs(library_key: LibraryLocatorV2) -> None:
+def upsert_content_library_index_docs(library_key: LibraryLocatorV2, wait=True) -> None:
"""
Creates or updates the documents for the given Content Library in the search index
"""
@@ -512,14 +516,15 @@ def upsert_content_library_index_docs(library_key: LibraryLocatorV2) -> None:
doc = searchable_doc_for_library_block(metadata)
docs.append(doc)
- _update_index_docs(docs)
+ return _update_index_docs(docs, wait=wait)
-def delete_content_library_index_docs(library_key: LibraryLocatorV2) -> None:
+def delete_content_library_index_docs(library_key: LibraryLocatorV2, wait=True) -> None:
"""
Deletes the discarded draft documents for the given Content Library in the search index
"""
meili_ids = []
+ # draft version and published version equal to False/null means those drafts were discarded.
for component in lib_api.get_library_components(library_key, draft=False, published=False):
usage_key = LibraryUsageLocatorV2(
library_key,
@@ -527,7 +532,7 @@ def delete_content_library_index_docs(library_key: LibraryLocatorV2) -> None:
component.local_key,
)
meili_ids.append(meili_id_from_opaque_key(usage_key))
- delete_index_docs(meili_ids)
+ return delete_index_docs(meili_ids, wait=wait)
def upsert_block_tags_index_docs(usage_key: UsageKey):
diff --git a/openedx/core/djangoapps/content/search/handlers.py b/openedx/core/djangoapps/content/search/handlers.py
index ba0e8c1a16..02b2e66c47 100644
--- a/openedx/core/djangoapps/content/search/handlers.py
+++ b/openedx/core/djangoapps/content/search/handlers.py
@@ -136,7 +136,8 @@ def content_library_updated_handler(**kwargs) -> None:
log.error("Received null or incorrect data for event")
return
- update_content_library_index_docs.delay(str(content_library_data.library_key))
+ if content_library_data.update_blocks:
+ update_content_library_index_docs.delay(str(content_library_data.library_key))
@receiver(CONTENT_OBJECT_TAGS_CHANGED)
diff --git a/openedx/core/djangoapps/content/search/tasks.py b/openedx/core/djangoapps/content/search/tasks.py
index 2eacd02c76..a4e9eda2f8 100644
--- a/openedx/core/djangoapps/content/search/tasks.py
+++ b/openedx/core/djangoapps/content/search/tasks.py
@@ -9,9 +9,9 @@ import logging
from celery import shared_task
from celery_utils.logged_task import LoggedTask
from edx_django_utils.monitoring import set_code_owner_attribute
+from meilisearch.errors import MeilisearchError
from opaque_keys.edx.keys import UsageKey
from opaque_keys.edx.locator import LibraryLocatorV2, LibraryUsageLocatorV2
-from meilisearch.errors import MeilisearchError
from . import api
@@ -80,5 +80,6 @@ def update_content_library_index_docs(library_key_str: str) -> None:
log.info("Updating content index documents for library with id: %s", library_key)
- api.delete_content_library_index_docs(library_key)
- api.upsert_content_library_index_docs(library_key)
+ tasks = api.delete_content_library_index_docs(library_key, wait=False) + \
+ api.upsert_content_library_index_docs(library_key, wait=False)
+ api.wait_for_meili_tasks(tasks)
From 140b9bb9684644176210120151df50b04a3394b8 Mon Sep 17 00:00:00 2001
From: Navin Karkera
Date: Wed, 7 Aug 2024 12:36:24 +0530
Subject: [PATCH 052/242] refactor: delete all documents on discard
---
openedx/core/djangoapps/content/search/api.py | 47 +++++--------------
.../core/djangoapps/content/search/tasks.py | 5 +-
2 files changed, 14 insertions(+), 38 deletions(-)
diff --git a/openedx/core/djangoapps/content/search/api.py b/openedx/core/djangoapps/content/search/api.py
index 776de6738e..79726df65d 100644
--- a/openedx/core/djangoapps/content/search/api.py
+++ b/openedx/core/djangoapps/content/search/api.py
@@ -143,7 +143,7 @@ def _wait_for_meili_task(info: TaskInfo) -> None:
raise MeilisearchError(err_reason)
-def wait_for_meili_tasks(info_list: list[TaskInfo]) -> None:
+def _wait_for_meili_tasks(info_list: list[TaskInfo]) -> None:
"""
Simple helper method to wait for multiple Meilisearch tasks to complete
"""
@@ -231,7 +231,7 @@ def _recurse_children(block, fn, status_cb: Callable[[str], None] | None = None)
fn(child)
-def _update_index_docs(docs, wait=True) -> list[TaskInfo]:
+def _update_index_docs(docs) -> list[TaskInfo]:
"""
Helper function that updates the documents in the search index
@@ -249,9 +249,7 @@ def _update_index_docs(docs, wait=True) -> list[TaskInfo]:
tasks.append(client.index(current_rebuild_index_name).update_documents(docs))
tasks.append(client.index(STUDIO_INDEX_NAME).update_documents(docs))
- if wait:
- wait_for_meili_tasks(tasks)
- return tasks
+ _wait_for_meili_tasks(tasks)
def only_if_meilisearch_enabled(f):
@@ -466,29 +464,24 @@ def delete_index_doc(usage_key: UsageKey) -> None:
tasks.append(client.index(current_rebuild_index_name).delete_document(meili_id_from_opaque_key(usage_key)))
tasks.append(client.index(STUDIO_INDEX_NAME).delete_document(meili_id_from_opaque_key(usage_key)))
- wait_for_meili_tasks(tasks)
+ _wait_for_meili_tasks(tasks)
-def delete_index_docs(meili_ids: list[str], wait=True) -> list[TaskInfo]:
+def delete_all_index_docs_for_library(library_key: LibraryLocatorV2) -> None:
"""
Deletes documents for the given XBlocks from the search index
-
- Args:
- meili_ids (list[str]): List of meili_ids from usage keys of the XBlocks to be removed from the index
"""
current_rebuild_index_name = _get_running_rebuild_index_name()
-
client = _get_meilisearch_client()
+ filter = f'{Fields.context_key}="{library_key}"'
tasks = []
if current_rebuild_index_name:
- # If there is a rebuild in progress, the document will also be deleted from the new index.
- tasks.append(client.index(current_rebuild_index_name).delete_documents(meili_ids))
- tasks.append(client.index(STUDIO_INDEX_NAME).delete_documents(meili_ids))
+ # If there is a rebuild in progress, the documents will also be deleted from the new index.
+ tasks.append(client.index(current_rebuild_index_name).delete_documents(filter=filter))
+ tasks.append(client.index(STUDIO_INDEX_NAME).delete_documents(filter=filter))
- if wait:
- wait_for_meili_tasks(tasks)
- return tasks
+ _wait_for_meili_tasks(tasks)
def upsert_library_block_index_doc(usage_key: UsageKey) -> None:
@@ -506,7 +499,7 @@ def upsert_library_block_index_doc(usage_key: UsageKey) -> None:
_update_index_docs(docs)
-def upsert_content_library_index_docs(library_key: LibraryLocatorV2, wait=True) -> None:
+def upsert_content_library_index_docs(library_key: LibraryLocatorV2) -> None:
"""
Creates or updates the documents for the given Content Library in the search index
"""
@@ -516,23 +509,7 @@ def upsert_content_library_index_docs(library_key: LibraryLocatorV2, wait=True)
doc = searchable_doc_for_library_block(metadata)
docs.append(doc)
- return _update_index_docs(docs, wait=wait)
-
-
-def delete_content_library_index_docs(library_key: LibraryLocatorV2, wait=True) -> None:
- """
- Deletes the discarded draft documents for the given Content Library in the search index
- """
- meili_ids = []
- # draft version and published version equal to False/null means those drafts were discarded.
- for component in lib_api.get_library_components(library_key, draft=False, published=False):
- usage_key = LibraryUsageLocatorV2(
- library_key,
- component.component_type.name,
- component.local_key,
- )
- meili_ids.append(meili_id_from_opaque_key(usage_key))
- return delete_index_docs(meili_ids, wait=wait)
+ return _update_index_docs(docs)
def upsert_block_tags_index_docs(usage_key: UsageKey):
diff --git a/openedx/core/djangoapps/content/search/tasks.py b/openedx/core/djangoapps/content/search/tasks.py
index a4e9eda2f8..73dcfe284d 100644
--- a/openedx/core/djangoapps/content/search/tasks.py
+++ b/openedx/core/djangoapps/content/search/tasks.py
@@ -80,6 +80,5 @@ def update_content_library_index_docs(library_key_str: str) -> None:
log.info("Updating content index documents for library with id: %s", library_key)
- tasks = api.delete_content_library_index_docs(library_key, wait=False) + \
- api.upsert_content_library_index_docs(library_key, wait=False)
- api.wait_for_meili_tasks(tasks)
+ api.delete_all_index_docs_for_library(library_key)
+ api.upsert_content_library_index_docs(library_key)
From e1495398e2c30f1c92663552664590fce1dc2dd0 Mon Sep 17 00:00:00 2001
From: Navin Karkera
Date: Wed, 7 Aug 2024 13:14:17 +0530
Subject: [PATCH 053/242] refactor: delete documents that were never published
on discard
---
openedx/core/djangoapps/content/search/api.py | 17 +++++++++++------
openedx/core/djangoapps/content/search/tasks.py | 4 +++-
.../core/djangoapps/content_libraries/api.py | 5 ++---
3 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/openedx/core/djangoapps/content/search/api.py b/openedx/core/djangoapps/content/search/api.py
index 79726df65d..bad46dab91 100644
--- a/openedx/core/djangoapps/content/search/api.py
+++ b/openedx/core/djangoapps/content/search/api.py
@@ -18,7 +18,7 @@ from meilisearch import Client as MeilisearchClient
from meilisearch.errors import MeilisearchError
from meilisearch.models.task import TaskInfo
from opaque_keys.edx.keys import UsageKey
-from opaque_keys.edx.locator import LibraryLocatorV2, LibraryUsageLocatorV2
+from opaque_keys.edx.locator import LibraryLocatorV2
from common.djangoapps.student.roles import GlobalStaff
from rest_framework.request import Request
from common.djangoapps.student.role_helpers import get_course_roles
@@ -231,7 +231,7 @@ def _recurse_children(block, fn, status_cb: Callable[[str], None] | None = None)
fn(child)
-def _update_index_docs(docs) -> list[TaskInfo]:
+def _update_index_docs(docs) -> None:
"""
Helper function that updates the documents in the search index
@@ -467,13 +467,18 @@ def delete_index_doc(usage_key: UsageKey) -> None:
_wait_for_meili_tasks(tasks)
-def delete_all_index_docs_for_library(library_key: LibraryLocatorV2) -> None:
+def delete_all_draft_docs_for_library(library_key: LibraryLocatorV2) -> None:
"""
- Deletes documents for the given XBlocks from the search index
+ Deletes draft documents for the given XBlocks from the search index
"""
current_rebuild_index_name = _get_running_rebuild_index_name()
client = _get_meilisearch_client()
- filter = f'{Fields.context_key}="{library_key}"'
+ # Delete all documents where last_published is null i.e. never published before.
+ filter = [
+ f'{Fields.context_key}="{library_key}"',
+ # inner arrays are connected by an OR
+ [f'{Fields.last_published} IS EMPTY', f'{Fields.last_published} IS NULL'],
+ ]
tasks = []
if current_rebuild_index_name:
@@ -509,7 +514,7 @@ def upsert_content_library_index_docs(library_key: LibraryLocatorV2) -> None:
doc = searchable_doc_for_library_block(metadata)
docs.append(doc)
- return _update_index_docs(docs)
+ _update_index_docs(docs)
def upsert_block_tags_index_docs(usage_key: UsageKey):
diff --git a/openedx/core/djangoapps/content/search/tasks.py b/openedx/core/djangoapps/content/search/tasks.py
index 73dcfe284d..dfd6037769 100644
--- a/openedx/core/djangoapps/content/search/tasks.py
+++ b/openedx/core/djangoapps/content/search/tasks.py
@@ -80,5 +80,7 @@ def update_content_library_index_docs(library_key_str: str) -> None:
log.info("Updating content index documents for library with id: %s", library_key)
- api.delete_all_index_docs_for_library(library_key)
api.upsert_content_library_index_docs(library_key)
+ # Delete all documents in this library that were not published by above function
+ # as this task is also triggered on discard event.
+ api.delete_all_draft_docs_for_library(library_key)
diff --git a/openedx/core/djangoapps/content_libraries/api.py b/openedx/core/djangoapps/content_libraries/api.py
index 5a956b9613..17bea80b3a 100644
--- a/openedx/core/djangoapps/content_libraries/api.py
+++ b/openedx/core/djangoapps/content_libraries/api.py
@@ -642,7 +642,7 @@ def _get_library_component_tags_count(library_key) -> dict:
return get_object_tag_counts(library_key_pattern, count_implicit=True)
-def get_library_components(library_key, text_search=None, block_types=None, draft=True, published=None) -> QuerySet[Component]:
+def get_library_components(library_key, text_search=None, block_types=None) -> QuerySet[Component]:
"""
Get the library components and filter.
@@ -653,8 +653,7 @@ def get_library_components(library_key, text_search=None, block_types=None, draf
learning_package = lib.learning_package
components = authoring_api.get_components(
learning_package.id,
- draft=draft,
- published=published,
+ draft=True,
namespace='xblock.v1',
type_names=block_types,
draft_title=text_search,
From fed8a803f1bb53f9dfe15b5fe3f53e1f2e7e31be Mon Sep 17 00:00:00 2001
From: Navin Karkera
Date: Wed, 7 Aug 2024 15:36:42 +0530
Subject: [PATCH 054/242] test: delete documents that were never published on
discard
---
openedx/core/djangoapps/content/search/api.py | 6 +++---
.../core/djangoapps/content/search/handlers.py | 3 +--
.../djangoapps/content/search/tests/test_api.py | 15 +++++++++++++++
3 files changed, 19 insertions(+), 5 deletions(-)
diff --git a/openedx/core/djangoapps/content/search/api.py b/openedx/core/djangoapps/content/search/api.py
index bad46dab91..cc80d36cbd 100644
--- a/openedx/core/djangoapps/content/search/api.py
+++ b/openedx/core/djangoapps/content/search/api.py
@@ -474,7 +474,7 @@ def delete_all_draft_docs_for_library(library_key: LibraryLocatorV2) -> None:
current_rebuild_index_name = _get_running_rebuild_index_name()
client = _get_meilisearch_client()
# Delete all documents where last_published is null i.e. never published before.
- filter = [
+ delete_filter = [
f'{Fields.context_key}="{library_key}"',
# inner arrays are connected by an OR
[f'{Fields.last_published} IS EMPTY', f'{Fields.last_published} IS NULL'],
@@ -483,8 +483,8 @@ def delete_all_draft_docs_for_library(library_key: LibraryLocatorV2) -> None:
tasks = []
if current_rebuild_index_name:
# If there is a rebuild in progress, the documents will also be deleted from the new index.
- tasks.append(client.index(current_rebuild_index_name).delete_documents(filter=filter))
- tasks.append(client.index(STUDIO_INDEX_NAME).delete_documents(filter=filter))
+ tasks.append(client.index(current_rebuild_index_name).delete_documents(filter=delete_filter))
+ tasks.append(client.index(STUDIO_INDEX_NAME).delete_documents(filter=delete_filter))
_wait_for_meili_tasks(tasks)
diff --git a/openedx/core/djangoapps/content/search/handlers.py b/openedx/core/djangoapps/content/search/handlers.py
index 02b2e66c47..ba0e8c1a16 100644
--- a/openedx/core/djangoapps/content/search/handlers.py
+++ b/openedx/core/djangoapps/content/search/handlers.py
@@ -136,8 +136,7 @@ def content_library_updated_handler(**kwargs) -> None:
log.error("Received null or incorrect data for event")
return
- if content_library_data.update_blocks:
- update_content_library_index_docs.delay(str(content_library_data.library_key))
+ update_content_library_index_docs.delay(str(content_library_data.library_key))
@receiver(CONTENT_OBJECT_TAGS_CHANGED)
diff --git a/openedx/core/djangoapps/content/search/tests/test_api.py b/openedx/core/djangoapps/content/search/tests/test_api.py
index 9dcdfb76b4..e8616cee60 100644
--- a/openedx/core/djangoapps/content/search/tests/test_api.py
+++ b/openedx/core/djangoapps/content/search/tests/test_api.py
@@ -388,3 +388,18 @@ class TestSearchApi(ModuleStoreTestCase):
mock_meilisearch.return_value.index.return_value.update_documents.assert_called_once_with(
[self.doc_problem1, self.doc_problem2]
)
+
+ @override_settings(MEILISEARCH_ENABLED=True)
+ def test_delete_all_drafts(self, mock_meilisearch):
+ """
+ Test deleting all draft documents from the index.
+ """
+ api.delete_all_draft_docs_for_library(self.library.key)
+
+ delete_filter = [
+ f'context_key="{self.library.key}"',
+ ['last_published IS EMPTY', 'last_published IS NULL'],
+ ]
+ mock_meilisearch.return_value.index.return_value.delete_documents.assert_called_once_with(
+ filter=delete_filter
+ )
From c3480b8f0bc2c9adde6b4a19f3656947492ee31f Mon Sep 17 00:00:00 2001
From: Navin Karkera
Date: Wed, 14 Aug 2024 21:03:27 +0530
Subject: [PATCH 055/242] chore: add comment
---
openedx/core/djangoapps/content/search/api.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/openedx/core/djangoapps/content/search/api.py b/openedx/core/djangoapps/content/search/api.py
index cc80d36cbd..4262b8a7b1 100644
--- a/openedx/core/djangoapps/content/search/api.py
+++ b/openedx/core/djangoapps/content/search/api.py
@@ -476,7 +476,8 @@ def delete_all_draft_docs_for_library(library_key: LibraryLocatorV2) -> None:
# Delete all documents where last_published is null i.e. never published before.
delete_filter = [
f'{Fields.context_key}="{library_key}"',
- # inner arrays are connected by an OR
+ # This field should only be NULL or have a value, but we're also checking IS EMPTY just in case.
+ # Inner arrays are connected by an OR
[f'{Fields.last_published} IS EMPTY', f'{Fields.last_published} IS NULL'],
]
From 860ca3c204bd94bf353c3cc564f021ac17f8b229 Mon Sep 17 00:00:00 2001
From: edX requirements bot
Date: Mon, 19 Aug 2024 22:23:27 -0400
Subject: [PATCH 056/242] chore: Upgrade Python requirements
---
requirements/edx-sandbox/base.txt | 6 +-
requirements/edx/base.txt | 72 +++++++++-------
requirements/edx/development.txt | 86 ++++++++++---------
requirements/edx/doc.txt | 71 ++++++++-------
requirements/edx/paver.txt | 2 +-
requirements/edx/testing.txt | 83 ++++++++++--------
requirements/pip.txt | 2 +-
scripts/user_retirement/requirements/base.txt | 18 ++--
.../user_retirement/requirements/testing.txt | 18 ++--
9 files changed, 193 insertions(+), 165 deletions(-)
diff --git a/requirements/edx-sandbox/base.txt b/requirements/edx-sandbox/base.txt
index 8942bc7dd9..4a7b0c0a7d 100644
--- a/requirements/edx-sandbox/base.txt
+++ b/requirements/edx-sandbox/base.txt
@@ -35,13 +35,13 @@ markupsafe==2.1.5
# via
# chem
# openedx-calc
-matplotlib==3.9.0
+matplotlib==3.9.2
# via -r requirements/edx-sandbox/base.in
mpmath==1.3.0
# via sympy
networkx==3.3
# via -r requirements/edx-sandbox/base.in
-nltk==3.8.1
+nltk==3.9.1
# via
# -r requirements/edx-sandbox/base.in
# chem
@@ -82,7 +82,7 @@ six==1.16.0
# via
# codejail-includes
# python-dateutil
-sympy==1.13.1
+sympy==1.13.2
# via
# -r requirements/edx-sandbox/base.in
# openedx-calc
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 6a15216943..8e0a834436 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -8,15 +8,16 @@
# via -r requirements/edx/github.in
acid-xblock==0.3.1
# via -r requirements/edx/kernel.in
-aiohappyeyeballs==2.3.4
+aiohappyeyeballs==2.4.0
# via aiohttp
-aiohttp==3.10.1
+aiohttp==3.10.5
# via
+ # algoliasearch
# geoip2
# openai
aiosignal==1.3.1
# via aiohttp
-algoliasearch==3.0.0
+algoliasearch==4.0.2
# via -r requirements/edx/bundled.in
amqp==5.2.0
# via kombu
@@ -35,6 +36,8 @@ asgiref==3.8.1
# django-countries
asn1crypto==1.5.1
# via snowflake-connector-python
+async-timeout==4.0.3
+ # via algoliasearch
attrs==24.2.0
# via
# -r requirements/edx/kernel.in
@@ -45,7 +48,7 @@ attrs==24.2.0
# openedx-events
# openedx-learning
# referencing
-babel==2.15.0
+babel==2.16.0
# via
# -r requirements/edx/kernel.in
# enmerkar
@@ -68,13 +71,13 @@ bleach[css]==6.1.0
# xblock-poll
boto==2.49.0
# via -r requirements/edx/kernel.in
-boto3==1.34.154
+boto3==1.35.1
# via
# -r requirements/edx/kernel.in
# django-ses
# fs-s3fs
# ora2
-botocore==1.34.154
+botocore==1.35.1
# via
# -r requirements/edx/kernel.in
# boto3
@@ -83,7 +86,7 @@ bridgekeeper==0.9
# via -r requirements/edx/kernel.in
cachecontrol==0.14.0
# via firebase-admin
-cachetools==5.4.0
+cachetools==5.5.0
# via google-auth
camel-converter[pydantic]==3.1.2
# via meilisearch
@@ -515,7 +518,7 @@ edx-search==4.0.0
# via -r requirements/edx/kernel.in
edx-sga==0.25.0
# via -r requirements/edx/bundled.in
-edx-submissions==3.7.6
+edx-submissions==3.7.7
# via
# -r requirements/edx/kernel.in
# ora2
@@ -589,9 +592,9 @@ google-api-core[grpc]==2.19.1
# google-cloud-core
# google-cloud-firestore
# google-cloud-storage
-google-api-python-client==2.139.0
+google-api-python-client==2.141.0
# via firebase-admin
-google-auth==2.32.0
+google-auth==2.34.0
# via
# google-api-core
# google-api-python-client
@@ -605,27 +608,27 @@ google-cloud-core==2.4.1
# via
# google-cloud-firestore
# google-cloud-storage
-google-cloud-firestore==2.17.0
+google-cloud-firestore==2.17.2
# via firebase-admin
-google-cloud-storage==2.18.0
+google-cloud-storage==2.18.2
# via firebase-admin
google-crc32c==1.5.0
# via
# google-cloud-storage
# google-resumable-media
-google-resumable-media==2.7.1
+google-resumable-media==2.7.2
# via google-cloud-storage
googleapis-common-protos==1.63.2
# via
# google-api-core
# grpcio-status
-grpcio==1.65.4
+grpcio==1.65.5
# via
# google-api-core
# grpcio-status
-grpcio-status==1.62.3
+grpcio-status==1.65.5
# via google-api-core
-gunicorn==22.0.0
+gunicorn==23.0.0
# via -r requirements/edx/kernel.in
help-tokens==2.4.0
# via -r requirements/edx/kernel.in
@@ -646,7 +649,7 @@ idna==3.7
# requests
# snowflake-connector-python
# yarl
-importlib-metadata==6.11.0
+importlib-metadata==8.3.0
# via -r requirements/edx/kernel.in
inflection==0.5.1
# via
@@ -755,7 +758,7 @@ monotonic==1.6
# via
# analytics-python
# py2neo
-more-itertools==10.3.0
+more-itertools==10.4.0
# via cssutils
mpmath==1.3.0
# via sympy
@@ -767,13 +770,13 @@ multidict==6.0.5
# yarl
mysqlclient==2.2.4
# via -r requirements/edx/kernel.in
-newrelic==9.12.0
+newrelic==9.13.0
# via
# -r requirements/edx/bundled.in
# edx-django-utils
nh3==0.2.18
# via -r requirements/edx/kernel.in
-nltk==3.8.1
+nltk==3.9.1
# via chem
nodeenv==1.9.1
# via -r requirements/edx/kernel.in
@@ -841,7 +844,7 @@ packaging==24.1
# snowflake-connector-python
pansi==2020.7.3
# via py2neo
-paramiko==3.4.0
+paramiko==3.4.1
# via edx-enterprise
path==16.11.0
# via
@@ -881,7 +884,7 @@ proto-plus==1.24.0
# via
# google-api-core
# google-cloud-firestore
-protobuf==4.25.4
+protobuf==5.27.3
# via
# google-api-core
# google-cloud-firestore
@@ -914,7 +917,9 @@ pycryptodomex==3.20.0
# lti-consumer-xblock
# pyjwkest
pydantic==2.8.2
- # via camel-converter
+ # via
+ # algoliasearch
+ # camel-converter
pydantic-core==2.20.1
# via pydantic
pygments==2.18.0
@@ -977,6 +982,7 @@ pysrt==1.1.2
python-dateutil==2.9.0.post0
# via
# -r requirements/edx/kernel.in
+ # algoliasearch
# analytics-python
# botocore
# celery
@@ -1022,7 +1028,7 @@ pytz==2024.1
# xblock
pyuca==1.2
# via -r requirements/edx/kernel.in
-pyyaml==6.0.1
+pyyaml==6.0.2
# via
# -r requirements/edx/kernel.in
# code-annotations
@@ -1049,7 +1055,6 @@ regex==2024.7.24
requests==2.32.3
# via
# -r requirements/edx/paver.txt
- # algoliasearch
# analytics-python
# cachecontrol
# django-oauth-toolkit
@@ -1099,9 +1104,9 @@ scipy==1.14.0
# openedx-calc
semantic-version==2.10.0
# via edx-drf-extensions
-shapely==2.0.5
+shapely==2.0.6
# via -r requirements/edx/kernel.in
-simplejson==3.19.2
+simplejson==3.19.3
# via
# -r requirements/edx/kernel.in
# sailthru-client
@@ -1162,7 +1167,7 @@ sortedcontainers==2.4.0
# via
# -r requirements/edx/kernel.in
# snowflake-connector-python
-soupsieve==2.5
+soupsieve==2.6
# via beautifulsoup4
sqlparse==0.5.1
# via django
@@ -1179,7 +1184,7 @@ stevedore==5.2.0
# edx-opaque-keys
super-csv==3.2.0
# via edx-bulk-grades
-sympy==1.13.1
+sympy==1.13.2
# via openedx-calc
testfixtures==8.3.0
# via edx-enterprise
@@ -1187,7 +1192,7 @@ text-unidecode==1.3
# via python-slugify
tinycss2==1.2.1
# via bleach
-tomlkit==0.13.0
+tomlkit==0.13.2
# via snowflake-connector-python
tqdm==4.66.5
# via
@@ -1218,6 +1223,7 @@ urllib3==1.26.19
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/paver.txt
+ # algoliasearch
# botocore
# elasticsearch
# py2neo
@@ -1233,7 +1239,7 @@ voluptuous==0.15.2
# via ora2
walrus==0.9.4
# via edx-event-bus-redis
-watchdog==4.0.1
+watchdog==4.0.2
# via -r requirements/edx/paver.txt
wcwidth==0.2.13
# via prompt-toolkit
@@ -1250,7 +1256,7 @@ webencodings==0.5.1
# bleach
# html5lib
# tinycss2
-webob==1.8.7
+webob==1.8.8
# via
# -r requirements/edx/kernel.in
# xblock
@@ -1290,7 +1296,7 @@ xss-utils==0.6.0
# via -r requirements/edx/kernel.in
yarl==1.9.4
# via aiohttp
-zipp==3.19.2
+zipp==3.20.0
# via importlib-metadata
# The following packages are considered to be unsafe in a requirements file:
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index 8155169e33..35a171123a 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -16,15 +16,16 @@ acid-xblock==0.3.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-aiohappyeyeballs==2.3.4
+aiohappyeyeballs==2.4.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# aiohttp
-aiohttp==3.10.1
+aiohttp==3.10.5
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
+ # algoliasearch
# geoip2
# openai
aiosignal==1.3.1
@@ -36,7 +37,7 @@ alabaster==1.0.0
# via
# -r requirements/edx/doc.txt
# sphinx
-algoliasearch==3.0.0
+algoliasearch==4.0.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -85,6 +86,11 @@ astroid==2.13.5
# -r requirements/edx/testing.txt
# pylint
# pylint-celery
+async-timeout==4.0.3
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # algoliasearch
attrs==24.2.0
# via
# -r requirements/edx/doc.txt
@@ -96,7 +102,7 @@ attrs==24.2.0
# openedx-events
# openedx-learning
# referencing
-babel==2.15.0
+babel==2.16.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -139,14 +145,14 @@ boto==2.49.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-boto3==1.34.154
+boto3==1.35.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# django-ses
# fs-s3fs
# ora2
-botocore==1.34.154
+botocore==1.35.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -165,7 +171,7 @@ cachecontrol==0.14.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# firebase-admin
-cachetools==5.4.0
+cachetools==5.5.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -813,7 +819,7 @@ edx-sga==0.25.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-edx-submissions==3.7.6
+edx-submissions==3.7.7
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -876,13 +882,13 @@ execnet==2.1.1
# via
# -r requirements/edx/testing.txt
# pytest-xdist
-factory-boy==3.3.0
+factory-boy==3.3.1
# via -r requirements/edx/testing.txt
-faker==26.2.0
+faker==27.0.0
# via
# -r requirements/edx/testing.txt
# factory-boy
-fastapi==0.112.0
+fastapi==0.112.1
# via
# -r requirements/edx/testing.txt
# pact-python
@@ -951,12 +957,12 @@ google-api-core[grpc]==2.19.1
# google-cloud-core
# google-cloud-firestore
# google-cloud-storage
-google-api-python-client==2.139.0
+google-api-python-client==2.141.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# firebase-admin
-google-auth==2.32.0
+google-auth==2.34.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -977,12 +983,12 @@ google-cloud-core==2.4.1
# -r requirements/edx/testing.txt
# google-cloud-firestore
# google-cloud-storage
-google-cloud-firestore==2.17.0
+google-cloud-firestore==2.17.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# firebase-admin
-google-cloud-storage==2.18.0
+google-cloud-storage==2.18.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -993,7 +999,7 @@ google-crc32c==1.5.0
# -r requirements/edx/testing.txt
# google-cloud-storage
# google-resumable-media
-google-resumable-media==2.7.1
+google-resumable-media==2.7.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1008,18 +1014,18 @@ grimp==3.4.1
# via
# -r requirements/edx/testing.txt
# import-linter
-grpcio==1.65.4
+grpcio==1.65.5
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# google-api-core
# grpcio-status
-grpcio-status==1.62.3
+grpcio-status==1.65.5
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# google-api-core
-gunicorn==22.0.0
+gunicorn==23.0.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1063,7 +1069,7 @@ imagesize==1.4.1
# sphinx
import-linter==2.0
# via -r requirements/edx/testing.txt
-importlib-metadata==6.11.0
+importlib-metadata==8.3.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1257,7 +1263,7 @@ monotonic==1.6
# -r requirements/edx/testing.txt
# analytics-python
# py2neo
-more-itertools==10.3.0
+more-itertools==10.4.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1289,7 +1295,7 @@ mysqlclient==2.2.4
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-newrelic==9.12.0
+newrelic==9.13.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1298,7 +1304,7 @@ nh3==0.2.18
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-nltk==3.8.1
+nltk==3.9.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1411,7 +1417,7 @@ pansi==2020.7.3
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# py2neo
-paramiko==3.4.0
+paramiko==3.4.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1491,7 +1497,7 @@ proto-plus==1.24.0
# -r requirements/edx/testing.txt
# google-api-core
# google-cloud-firestore
-protobuf==4.25.4
+protobuf==5.27.3
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1550,6 +1556,7 @@ pydantic==2.8.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
+ # algoliasearch
# camel-converter
# fastapi
pydantic-core==2.20.1
@@ -1711,6 +1718,7 @@ python-dateutil==2.9.0.post0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
+ # algoliasearch
# analytics-python
# botocore
# celery
@@ -1777,7 +1785,7 @@ pyuca==1.2
# -r requirements/edx/testing.txt
pywatchman==2.0.0
# via -r requirements/edx/development.in
-pyyaml==6.0.1
+pyyaml==6.0.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1817,7 +1825,6 @@ requests==2.32.3
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
- # algoliasearch
# analytics-python
# cachecontrol
# django-oauth-toolkit
@@ -1888,11 +1895,11 @@ semantic-version==2.10.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# edx-drf-extensions
-shapely==2.0.5
+shapely==2.0.6
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-simplejson==3.19.2
+simplejson==3.19.3
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1980,7 +1987,7 @@ sortedcontainers==2.4.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# snowflake-connector-python
-soupsieve==2.5
+soupsieve==2.6
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -2048,7 +2055,7 @@ staff-graded-xblock==2.3.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-starlette==0.37.2
+starlette==0.38.2
# via
# -r requirements/edx/testing.txt
# fastapi
@@ -2066,7 +2073,7 @@ super-csv==3.2.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# edx-bulk-grades
-sympy==1.13.1
+sympy==1.13.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -2088,13 +2095,13 @@ tinycss2==1.2.1
# bleach
tomli==2.0.1
# via django-stubs
-tomlkit==0.13.0
+tomlkit==0.13.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# pylint
# snowflake-connector-python
-tox==4.17.0
+tox==4.18.0
# via -r requirements/edx/testing.txt
tqdm==4.66.5
# via
@@ -2104,7 +2111,7 @@ tqdm==4.66.5
# openai
types-pytz==2024.1.0.20240417
# via django-stubs
-types-pyyaml==6.0.12.20240724
+types-pyyaml==6.0.12.20240808
# via
# django-stubs
# djangorestframework-stubs
@@ -2155,6 +2162,7 @@ urllib3==1.26.19
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
+ # algoliasearch
# botocore
# elasticsearch
# py2neo
@@ -2163,7 +2171,7 @@ user-util==1.1.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-uvicorn==0.30.5
+uvicorn==0.30.6
# via
# -r requirements/edx/testing.txt
# pact-python
@@ -2190,7 +2198,7 @@ walrus==0.9.4
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# edx-event-bus-redis
-watchdog==4.0.1
+watchdog==4.0.2
# via
# -r requirements/edx/development.in
# -r requirements/edx/doc.txt
@@ -2216,7 +2224,7 @@ webencodings==0.5.1
# bleach
# html5lib
# tinycss2
-webob==1.8.7
+webob==1.8.8
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -2281,7 +2289,7 @@ yarl==1.9.4
# -r requirements/edx/testing.txt
# aiohttp
# pact-python
-zipp==3.19.2
+zipp==3.20.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index bb81b7b71a..2367be11e0 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -10,13 +10,14 @@ accessible-pygments==0.0.5
# via pydata-sphinx-theme
acid-xblock==0.3.1
# via -r requirements/edx/base.txt
-aiohappyeyeballs==2.3.4
+aiohappyeyeballs==2.4.0
# via
# -r requirements/edx/base.txt
# aiohttp
-aiohttp==3.10.1
+aiohttp==3.10.5
# via
# -r requirements/edx/base.txt
+ # algoliasearch
# geoip2
# openai
aiosignal==1.3.1
@@ -25,7 +26,7 @@ aiosignal==1.3.1
# aiohttp
alabaster==1.0.0
# via sphinx
-algoliasearch==3.0.0
+algoliasearch==4.0.2
# via -r requirements/edx/base.txt
amqp==5.2.0
# via
@@ -55,6 +56,10 @@ asn1crypto==1.5.1
# via
# -r requirements/edx/base.txt
# snowflake-connector-python
+async-timeout==4.0.3
+ # via
+ # -r requirements/edx/base.txt
+ # algoliasearch
attrs==24.2.0
# via
# -r requirements/edx/base.txt
@@ -65,7 +70,7 @@ attrs==24.2.0
# openedx-events
# openedx-learning
# referencing
-babel==2.15.0
+babel==2.16.0
# via
# -r requirements/edx/base.txt
# enmerkar
@@ -100,13 +105,13 @@ bleach[css]==6.1.0
# xblock-poll
boto==2.49.0
# via -r requirements/edx/base.txt
-boto3==1.34.154
+boto3==1.35.1
# via
# -r requirements/edx/base.txt
# django-ses
# fs-s3fs
# ora2
-botocore==1.34.154
+botocore==1.35.1
# via
# -r requirements/edx/base.txt
# boto3
@@ -117,7 +122,7 @@ cachecontrol==0.14.0
# via
# -r requirements/edx/base.txt
# firebase-admin
-cachetools==5.4.0
+cachetools==5.5.0
# via
# -r requirements/edx/base.txt
# google-auth
@@ -596,7 +601,7 @@ edx-search==4.0.0
# via -r requirements/edx/base.txt
edx-sga==0.25.0
# via -r requirements/edx/base.txt
-edx-submissions==3.7.6
+edx-submissions==3.7.7
# via
# -r requirements/edx/base.txt
# ora2
@@ -689,11 +694,11 @@ google-api-core[grpc]==2.19.1
# google-cloud-core
# google-cloud-firestore
# google-cloud-storage
-google-api-python-client==2.139.0
+google-api-python-client==2.141.0
# via
# -r requirements/edx/base.txt
# firebase-admin
-google-auth==2.32.0
+google-auth==2.34.0
# via
# -r requirements/edx/base.txt
# google-api-core
@@ -711,11 +716,11 @@ google-cloud-core==2.4.1
# -r requirements/edx/base.txt
# google-cloud-firestore
# google-cloud-storage
-google-cloud-firestore==2.17.0
+google-cloud-firestore==2.17.2
# via
# -r requirements/edx/base.txt
# firebase-admin
-google-cloud-storage==2.18.0
+google-cloud-storage==2.18.2
# via
# -r requirements/edx/base.txt
# firebase-admin
@@ -724,7 +729,7 @@ google-crc32c==1.5.0
# -r requirements/edx/base.txt
# google-cloud-storage
# google-resumable-media
-google-resumable-media==2.7.1
+google-resumable-media==2.7.2
# via
# -r requirements/edx/base.txt
# google-cloud-storage
@@ -733,16 +738,16 @@ googleapis-common-protos==1.63.2
# -r requirements/edx/base.txt
# google-api-core
# grpcio-status
-grpcio==1.65.4
+grpcio==1.65.5
# via
# -r requirements/edx/base.txt
# google-api-core
# grpcio-status
-grpcio-status==1.62.3
+grpcio-status==1.65.5
# via
# -r requirements/edx/base.txt
# google-api-core
-gunicorn==22.0.0
+gunicorn==23.0.0
# via -r requirements/edx/base.txt
help-tokens==2.4.0
# via -r requirements/edx/base.txt
@@ -766,7 +771,7 @@ idna==3.7
# yarl
imagesize==1.4.1
# via sphinx
-importlib-metadata==6.11.0
+importlib-metadata==8.3.0
# via -r requirements/edx/base.txt
inflection==0.5.1
# via
@@ -902,7 +907,7 @@ monotonic==1.6
# -r requirements/edx/base.txt
# analytics-python
# py2neo
-more-itertools==10.3.0
+more-itertools==10.4.0
# via
# -r requirements/edx/base.txt
# cssutils
@@ -921,13 +926,13 @@ multidict==6.0.5
# yarl
mysqlclient==2.2.4
# via -r requirements/edx/base.txt
-newrelic==9.12.0
+newrelic==9.13.0
# via
# -r requirements/edx/base.txt
# edx-django-utils
nh3==0.2.18
# via -r requirements/edx/base.txt
-nltk==3.8.1
+nltk==3.9.1
# via
# -r requirements/edx/base.txt
# chem
@@ -1005,7 +1010,7 @@ pansi==2020.7.3
# via
# -r requirements/edx/base.txt
# py2neo
-paramiko==3.4.0
+paramiko==3.4.1
# via
# -r requirements/edx/base.txt
# edx-enterprise
@@ -1058,7 +1063,7 @@ proto-plus==1.24.0
# -r requirements/edx/base.txt
# google-api-core
# google-cloud-firestore
-protobuf==4.25.4
+protobuf==5.27.3
# via
# -r requirements/edx/base.txt
# google-api-core
@@ -1099,6 +1104,7 @@ pycryptodomex==3.20.0
pydantic==2.8.2
# via
# -r requirements/edx/base.txt
+ # algoliasearch
# camel-converter
pydantic-core==2.20.1
# via
@@ -1176,6 +1182,7 @@ pysrt==1.1.2
python-dateutil==2.9.0.post0
# via
# -r requirements/edx/base.txt
+ # algoliasearch
# analytics-python
# botocore
# celery
@@ -1227,7 +1234,7 @@ pytz==2024.1
# xblock
pyuca==1.2
# via -r requirements/edx/base.txt
-pyyaml==6.0.1
+pyyaml==6.0.2
# via
# -r requirements/edx/base.txt
# code-annotations
@@ -1258,7 +1265,6 @@ regex==2024.7.24
requests==2.32.3
# via
# -r requirements/edx/base.txt
- # algoliasearch
# analytics-python
# cachecontrol
# django-oauth-toolkit
@@ -1319,9 +1325,9 @@ semantic-version==2.10.0
# via
# -r requirements/edx/base.txt
# edx-drf-extensions
-shapely==2.0.5
+shapely==2.0.6
# via -r requirements/edx/base.txt
-simplejson==3.19.2
+simplejson==3.19.3
# via
# -r requirements/edx/base.txt
# sailthru-client
@@ -1388,7 +1394,7 @@ sortedcontainers==2.4.0
# via
# -r requirements/edx/base.txt
# snowflake-connector-python
-soupsieve==2.5
+soupsieve==2.6
# via
# -r requirements/edx/base.txt
# beautifulsoup4
@@ -1447,7 +1453,7 @@ super-csv==3.2.0
# via
# -r requirements/edx/base.txt
# edx-bulk-grades
-sympy==1.13.1
+sympy==1.13.2
# via
# -r requirements/edx/base.txt
# openedx-calc
@@ -1463,7 +1469,7 @@ tinycss2==1.2.1
# via
# -r requirements/edx/base.txt
# bleach
-tomlkit==0.13.0
+tomlkit==0.13.2
# via
# -r requirements/edx/base.txt
# snowflake-connector-python
@@ -1501,6 +1507,7 @@ urllib3==1.26.19
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
+ # algoliasearch
# botocore
# elasticsearch
# py2neo
@@ -1521,7 +1528,7 @@ walrus==0.9.4
# via
# -r requirements/edx/base.txt
# edx-event-bus-redis
-watchdog==4.0.1
+watchdog==4.0.2
# via -r requirements/edx/base.txt
wcwidth==0.2.13
# via
@@ -1541,7 +1548,7 @@ webencodings==0.5.1
# bleach
# html5lib
# tinycss2
-webob==1.8.7
+webob==1.8.8
# via
# -r requirements/edx/base.txt
# xblock
@@ -1585,7 +1592,7 @@ yarl==1.9.4
# via
# -r requirements/edx/base.txt
# aiohttp
-zipp==3.19.2
+zipp==3.20.0
# via
# -r requirements/edx/base.txt
# importlib-metadata
diff --git a/requirements/edx/paver.txt b/requirements/edx/paver.txt
index 0b82d71c91..faa0085f16 100644
--- a/requirements/edx/paver.txt
+++ b/requirements/edx/paver.txt
@@ -61,7 +61,7 @@ urllib3==1.26.19
# via
# -c requirements/edx/../constraints.txt
# requests
-watchdog==4.0.1
+watchdog==4.0.2
# via -r requirements/edx/paver.in
wrapt==1.16.0
# via -r requirements/edx/paver.in
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 18c988dfb6..8b5ef476ff 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -8,20 +8,21 @@
# via -r requirements/edx/base.txt
acid-xblock==0.3.1
# via -r requirements/edx/base.txt
-aiohappyeyeballs==2.3.4
+aiohappyeyeballs==2.4.0
# via
# -r requirements/edx/base.txt
# aiohttp
-aiohttp==3.10.1
+aiohttp==3.10.5
# via
# -r requirements/edx/base.txt
+ # algoliasearch
# geoip2
# openai
aiosignal==1.3.1
# via
# -r requirements/edx/base.txt
# aiohttp
-algoliasearch==3.0.0
+algoliasearch==4.0.2
# via -r requirements/edx/base.txt
amqp==5.2.0
# via
@@ -57,6 +58,10 @@ astroid==2.13.5
# via
# pylint
# pylint-celery
+async-timeout==4.0.3
+ # via
+ # -r requirements/edx/base.txt
+ # algoliasearch
attrs==24.2.0
# via
# -r requirements/edx/base.txt
@@ -67,7 +72,7 @@ attrs==24.2.0
# openedx-events
# openedx-learning
# referencing
-babel==2.15.0
+babel==2.16.0
# via
# -r requirements/edx/base.txt
# enmerkar
@@ -100,13 +105,13 @@ bleach[css]==6.1.0
# xblock-poll
boto==2.49.0
# via -r requirements/edx/base.txt
-boto3==1.34.154
+boto3==1.35.1
# via
# -r requirements/edx/base.txt
# django-ses
# fs-s3fs
# ora2
-botocore==1.34.154
+botocore==1.35.1
# via
# -r requirements/edx/base.txt
# boto3
@@ -117,7 +122,7 @@ cachecontrol==0.14.0
# via
# -r requirements/edx/base.txt
# firebase-admin
-cachetools==5.4.0
+cachetools==5.5.0
# via
# -r requirements/edx/base.txt
# google-auth
@@ -622,7 +627,7 @@ edx-search==4.0.0
# via -r requirements/edx/base.txt
edx-sga==0.25.0
# via -r requirements/edx/base.txt
-edx-submissions==3.7.6
+edx-submissions==3.7.7
# via
# -r requirements/edx/base.txt
# ora2
@@ -670,11 +675,11 @@ event-tracking==3.0.0
# edx-search
execnet==2.1.1
# via pytest-xdist
-factory-boy==3.3.0
+factory-boy==3.3.1
# via -r requirements/edx/testing.in
-faker==26.2.0
+faker==27.0.0
# via factory-boy
-fastapi==0.112.0
+fastapi==0.112.1
# via pact-python
fastavro==1.9.5
# via
@@ -723,11 +728,11 @@ google-api-core[grpc]==2.19.1
# google-cloud-core
# google-cloud-firestore
# google-cloud-storage
-google-api-python-client==2.139.0
+google-api-python-client==2.141.0
# via
# -r requirements/edx/base.txt
# firebase-admin
-google-auth==2.32.0
+google-auth==2.34.0
# via
# -r requirements/edx/base.txt
# google-api-core
@@ -745,11 +750,11 @@ google-cloud-core==2.4.1
# -r requirements/edx/base.txt
# google-cloud-firestore
# google-cloud-storage
-google-cloud-firestore==2.17.0
+google-cloud-firestore==2.17.2
# via
# -r requirements/edx/base.txt
# firebase-admin
-google-cloud-storage==2.18.0
+google-cloud-storage==2.18.2
# via
# -r requirements/edx/base.txt
# firebase-admin
@@ -758,7 +763,7 @@ google-crc32c==1.5.0
# -r requirements/edx/base.txt
# google-cloud-storage
# google-resumable-media
-google-resumable-media==2.7.1
+google-resumable-media==2.7.2
# via
# -r requirements/edx/base.txt
# google-cloud-storage
@@ -769,16 +774,16 @@ googleapis-common-protos==1.63.2
# grpcio-status
grimp==3.4.1
# via import-linter
-grpcio==1.65.4
+grpcio==1.65.5
# via
# -r requirements/edx/base.txt
# google-api-core
# grpcio-status
-grpcio-status==1.62.3
+grpcio-status==1.65.5
# via
# -r requirements/edx/base.txt
# google-api-core
-gunicorn==22.0.0
+gunicorn==23.0.0
# via -r requirements/edx/base.txt
h11==0.14.0
# via uvicorn
@@ -807,7 +812,7 @@ idna==3.7
# yarl
import-linter==2.0
# via -r requirements/edx/testing.in
-importlib-metadata==6.11.0
+importlib-metadata==8.3.0
# via -r requirements/edx/base.txt
inflection==0.5.1
# via
@@ -953,7 +958,7 @@ monotonic==1.6
# -r requirements/edx/base.txt
# analytics-python
# py2neo
-more-itertools==10.3.0
+more-itertools==10.4.0
# via
# -r requirements/edx/base.txt
# cssutils
@@ -972,13 +977,13 @@ multidict==6.0.5
# yarl
mysqlclient==2.2.4
# via -r requirements/edx/base.txt
-newrelic==9.12.0
+newrelic==9.13.0
# via
# -r requirements/edx/base.txt
# edx-django-utils
nh3==0.2.18
# via -r requirements/edx/base.txt
-nltk==3.8.1
+nltk==3.9.1
# via
# -r requirements/edx/base.txt
# chem
@@ -1059,7 +1064,7 @@ pansi==2020.7.3
# via
# -r requirements/edx/base.txt
# py2neo
-paramiko==3.4.0
+paramiko==3.4.1
# via
# -r requirements/edx/base.txt
# edx-enterprise
@@ -1120,7 +1125,7 @@ proto-plus==1.24.0
# -r requirements/edx/base.txt
# google-api-core
# google-cloud-firestore
-protobuf==4.25.4
+protobuf==5.27.3
# via
# -r requirements/edx/base.txt
# google-api-core
@@ -1169,6 +1174,7 @@ pycryptodomex==3.20.0
pydantic==2.8.2
# via
# -r requirements/edx/base.txt
+ # algoliasearch
# camel-converter
# fastapi
pydantic-core==2.20.1
@@ -1292,6 +1298,7 @@ pytest-xdist[psutil]==3.6.1
python-dateutil==2.9.0.post0
# via
# -r requirements/edx/base.txt
+ # algoliasearch
# analytics-python
# botocore
# celery
@@ -1345,7 +1352,7 @@ pytz==2024.1
# xblock
pyuca==1.2
# via -r requirements/edx/base.txt
-pyyaml==6.0.1
+pyyaml==6.0.2
# via
# -r requirements/edx/base.txt
# code-annotations
@@ -1375,7 +1382,6 @@ regex==2024.7.24
requests==2.32.3
# via
# -r requirements/edx/base.txt
- # algoliasearch
# analytics-python
# cachecontrol
# django-oauth-toolkit
@@ -1436,9 +1442,9 @@ semantic-version==2.10.0
# via
# -r requirements/edx/base.txt
# edx-drf-extensions
-shapely==2.0.5
+shapely==2.0.6
# via -r requirements/edx/base.txt
-simplejson==3.19.2
+simplejson==3.19.3
# via
# -r requirements/edx/base.txt
# sailthru-client
@@ -1506,7 +1512,7 @@ sortedcontainers==2.4.0
# via
# -r requirements/edx/base.txt
# snowflake-connector-python
-soupsieve==2.5
+soupsieve==2.6
# via
# -r requirements/edx/base.txt
# beautifulsoup4
@@ -1516,7 +1522,7 @@ sqlparse==0.5.1
# django
staff-graded-xblock==2.3.0
# via -r requirements/edx/base.txt
-starlette==0.37.2
+starlette==0.38.2
# via fastapi
stevedore==5.2.0
# via
@@ -1530,7 +1536,7 @@ super-csv==3.2.0
# via
# -r requirements/edx/base.txt
# edx-bulk-grades
-sympy==1.13.1
+sympy==1.13.2
# via
# -r requirements/edx/base.txt
# openedx-calc
@@ -1547,12 +1553,12 @@ tinycss2==1.2.1
# via
# -r requirements/edx/base.txt
# bleach
-tomlkit==0.13.0
+tomlkit==0.13.2
# via
# -r requirements/edx/base.txt
# pylint
# snowflake-connector-python
-tox==4.17.0
+tox==4.18.0
# via -r requirements/edx/testing.in
tqdm==4.66.5
# via
@@ -1592,13 +1598,14 @@ urllib3==1.26.19
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
+ # algoliasearch
# botocore
# elasticsearch
# py2neo
# requests
user-util==1.1.0
# via -r requirements/edx/base.txt
-uvicorn==0.30.5
+uvicorn==0.30.6
# via pact-python
vine==5.1.0
# via
@@ -1616,7 +1623,7 @@ walrus==0.9.4
# via
# -r requirements/edx/base.txt
# edx-event-bus-redis
-watchdog==4.0.1
+watchdog==4.0.2
# via -r requirements/edx/base.txt
wcwidth==0.2.13
# via
@@ -1636,7 +1643,7 @@ webencodings==0.5.1
# bleach
# html5lib
# tinycss2
-webob==1.8.7
+webob==1.8.8
# via
# -r requirements/edx/base.txt
# xblock
@@ -1683,7 +1690,7 @@ yarl==1.9.4
# -r requirements/edx/base.txt
# aiohttp
# pact-python
-zipp==3.19.2
+zipp==3.20.0
# via
# -r requirements/edx/base.txt
# importlib-metadata
diff --git a/requirements/pip.txt b/requirements/pip.txt
index 7a6ada8e0a..f0cf3d1099 100644
--- a/requirements/pip.txt
+++ b/requirements/pip.txt
@@ -10,5 +10,5 @@ wheel==0.44.0
# The following packages are considered to be unsafe in a requirements file:
pip==24.2
# via -r requirements/pip.in
-setuptools==72.1.0
+setuptools==73.0.0
# via -r requirements/pip.in
diff --git a/scripts/user_retirement/requirements/base.txt b/scripts/user_retirement/requirements/base.txt
index 47e6e79c22..3e5ed47380 100644
--- a/scripts/user_retirement/requirements/base.txt
+++ b/scripts/user_retirement/requirements/base.txt
@@ -10,13 +10,13 @@ attrs==24.2.0
# via zeep
backoff==2.2.1
# via -r scripts/user_retirement/requirements/base.in
-boto3==1.34.154
+boto3==1.35.1
# via -r scripts/user_retirement/requirements/base.in
-botocore==1.34.154
+botocore==1.35.1
# via
# boto3
# s3transfer
-cachetools==5.4.0
+cachetools==5.5.0
# via google-auth
certifi==2024.7.4
# via requests
@@ -52,9 +52,9 @@ edx-rest-api-client==5.7.1
# via -r scripts/user_retirement/requirements/base.in
google-api-core==2.19.1
# via google-api-python-client
-google-api-python-client==2.139.0
+google-api-python-client==2.141.0
# via -r scripts/user_retirement/requirements/base.in
-google-auth==2.32.0
+google-auth==2.34.0
# via
# google-api-core
# google-api-python-client
@@ -81,9 +81,9 @@ lxml==4.9.4
# via
# -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
# zeep
-more-itertools==10.3.0
+more-itertools==10.4.0
# via simple-salesforce
-newrelic==9.12.0
+newrelic==9.13.0
# via edx-django-utils
pbr==6.0.0
# via stevedore
@@ -120,7 +120,7 @@ pytz==2024.1
# via
# jenkinsapi
# zeep
-pyyaml==6.0.1
+pyyaml==6.0.2
# via -r scripts/user_retirement/requirements/base.in
requests==2.32.3
# via
@@ -143,7 +143,7 @@ s3transfer==0.10.2
# via boto3
simple-salesforce==1.12.6
# via -r scripts/user_retirement/requirements/base.in
-simplejson==3.19.2
+simplejson==3.19.3
# via -r scripts/user_retirement/requirements/base.in
six==1.16.0
# via
diff --git a/scripts/user_retirement/requirements/testing.txt b/scripts/user_retirement/requirements/testing.txt
index 006eabeef4..f7464dfa06 100644
--- a/scripts/user_retirement/requirements/testing.txt
+++ b/scripts/user_retirement/requirements/testing.txt
@@ -14,17 +14,17 @@ attrs==24.2.0
# zeep
backoff==2.2.1
# via -r scripts/user_retirement/requirements/base.txt
-boto3==1.34.154
+boto3==1.35.1
# via
# -r scripts/user_retirement/requirements/base.txt
# moto
-botocore==1.34.154
+botocore==1.35.1
# via
# -r scripts/user_retirement/requirements/base.txt
# boto3
# moto
# s3transfer
-cachetools==5.4.0
+cachetools==5.5.0
# via
# -r scripts/user_retirement/requirements/base.txt
# google-auth
@@ -76,9 +76,9 @@ google-api-core==2.19.1
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-python-client
-google-api-python-client==2.139.0
+google-api-python-client==2.141.0
# via -r scripts/user_retirement/requirements/base.txt
-google-auth==2.32.0
+google-auth==2.34.0
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-core
@@ -126,13 +126,13 @@ markupsafe==2.1.5
# werkzeug
mock==5.1.0
# via -r scripts/user_retirement/requirements/testing.in
-more-itertools==10.3.0
+more-itertools==10.4.0
# via
# -r scripts/user_retirement/requirements/base.txt
# simple-salesforce
moto==4.2.14
# via -r scripts/user_retirement/requirements/testing.in
-newrelic==9.12.0
+newrelic==9.13.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
@@ -200,7 +200,7 @@ pytz==2024.1
# -r scripts/user_retirement/requirements/base.txt
# jenkinsapi
# zeep
-pyyaml==6.0.1
+pyyaml==6.0.2
# via
# -r scripts/user_retirement/requirements/base.txt
# responses
@@ -242,7 +242,7 @@ s3transfer==0.10.2
# boto3
simple-salesforce==1.12.6
# via -r scripts/user_retirement/requirements/base.txt
-simplejson==3.19.2
+simplejson==3.19.3
# via -r scripts/user_retirement/requirements/base.txt
six==1.16.0
# via
From af9ae77bbc35c2ac5b20a487c538d3f9a7fd6fb8 Mon Sep 17 00:00:00 2001
From: Awais Qureshi
Date: Tue, 20 Aug 2024 17:25:17 +0500
Subject: [PATCH 057/242] feat: upgrading simple api to drf compatible.
(#35260)
---
lms/djangoapps/instructor/views/api.py | 125 +++++++++---------
lms/djangoapps/instructor/views/api_urls.py | 2 +-
lms/djangoapps/instructor/views/serializer.py | 31 +++++
3 files changed, 95 insertions(+), 63 deletions(-)
diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py
index ceb699cd90..2813daa8a3 100644
--- a/lms/djangoapps/instructor/views/api.py
+++ b/lms/djangoapps/instructor/views/api.py
@@ -105,7 +105,7 @@ from lms.djangoapps.instructor_task import api as task_api
from lms.djangoapps.instructor_task.api_helper import AlreadyRunningError, QueueConnectionError
from lms.djangoapps.instructor_task.data import InstructorTaskTypes
from lms.djangoapps.instructor_task.models import ReportStore
-from lms.djangoapps.instructor.views.serializer import RoleNameSerializer, UserSerializer
+from lms.djangoapps.instructor.views.serializer import RoleNameSerializer, UserSerializer, AccessSerializer
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort, is_course_cohorted
from openedx.core.djangoapps.course_groups.models import CourseUserGroup
@@ -987,17 +987,8 @@ def bulk_beta_modify_access(request, course_id):
return JsonResponse(response_payload)
-@require_POST
-@ensure_csrf_cookie
-@cache_control(no_cache=True, no_store=True, must_revalidate=True)
-@require_course_permission(permissions.EDIT_COURSE_ACCESS)
-@require_post_params(
- unique_student_identifier="email or username of user to change access",
- rolename="'instructor', 'staff', 'beta', or 'ccx_coach'",
- action="'allow' or 'revoke'"
-)
-@common_exceptions_400
-def modify_access(request, course_id):
+@method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True), name='dispatch')
+class ModifyAccess(APIView):
"""
Modify staff/instructor access of other user.
Requires instructor access.
@@ -1009,67 +1000,77 @@ def modify_access(request, course_id):
rolename is one of ['instructor', 'staff', 'beta', 'ccx_coach']
action is one of ['allow', 'revoke']
"""
- course_id = CourseKey.from_string(course_id)
- course = get_course_with_access(
- request.user, 'instructor', course_id, depth=None
- )
- unique_student_identifier = request.POST.get('unique_student_identifier')
- try:
- user = get_student_from_identifier(unique_student_identifier)
- except User.DoesNotExist:
- response_payload = {
- 'unique_student_identifier': unique_student_identifier,
- 'userDoesNotExist': True,
- }
- return JsonResponse(response_payload)
+ permission_classes = (IsAuthenticated, permissions.InstructorPermission)
+ permission_name = permissions.EDIT_COURSE_ACCESS
+ serializer_class = AccessSerializer
- # Check that user is active, because add_users
- # in common/djangoapps/student/roles.py fails
- # silently when we try to add an inactive user.
- if not user.is_active:
- response_payload = {
- 'unique_student_identifier': user.username,
- 'inactiveUser': True,
- }
- return JsonResponse(response_payload)
+ @method_decorator(ensure_csrf_cookie)
+ def post(self, request, course_id):
+ """
+ Modify staff/instructor access of other user.
+ Requires instructor access.
+ """
+ course_id = CourseKey.from_string(course_id)
+ course = get_course_with_access(
+ request.user, 'instructor', course_id, depth=None
+ )
- rolename = request.POST.get('rolename')
- action = request.POST.get('action')
+ serializer_data = AccessSerializer(data=request.data)
+ if not serializer_data.is_valid():
+ return HttpResponseBadRequest(reason=serializer_data.errors)
- if rolename not in ROLES:
- error = strip_tags(f"unknown rolename '{rolename}'")
- log.error(error)
- return HttpResponseBadRequest(error)
+ user = serializer_data.validated_data.get('unique_student_identifier')
+ if not user:
+ response_payload = {
+ 'unique_student_identifier': request.data.get('unique_student_identifier'),
+ 'userDoesNotExist': True,
+ }
+ return JsonResponse(response_payload)
+
+ if not user.is_active:
+ response_payload = {
+ 'unique_student_identifier': user.username,
+ 'inactiveUser': True,
+ }
+ return JsonResponse(response_payload)
+
+ rolename = serializer_data.data['rolename']
+ action = serializer_data.data['action']
+
+ if rolename not in ROLES:
+ error = strip_tags(f"unknown rolename '{rolename}'")
+ log.error(error)
+ return HttpResponseBadRequest(error)
+
+ # disallow instructors from removing their own instructor access.
+ if rolename == 'instructor' and user == request.user and action != 'allow':
+ response_payload = {
+ 'unique_student_identifier': user.username,
+ 'rolename': rolename,
+ 'action': action,
+ 'removingSelfAsInstructor': True,
+ }
+ return JsonResponse(response_payload)
+
+ if action == 'allow':
+ allow_access(course, user, rolename)
+ if not is_user_enrolled_in_course(user, course_id):
+ CourseEnrollment.enroll(user, course_id)
+ elif action == 'revoke':
+ revoke_access(course, user, rolename)
+ else:
+ return HttpResponseBadRequest(strip_tags(
+ f"unrecognized action u'{action}'"
+ ))
- # disallow instructors from removing their own instructor access.
- if rolename == 'instructor' and user == request.user and action != 'allow':
response_payload = {
'unique_student_identifier': user.username,
'rolename': rolename,
'action': action,
- 'removingSelfAsInstructor': True,
+ 'success': 'yes',
}
return JsonResponse(response_payload)
- if action == 'allow':
- allow_access(course, user, rolename)
- if not is_user_enrolled_in_course(user, course_id):
- CourseEnrollment.enroll(user, course_id)
- elif action == 'revoke':
- revoke_access(course, user, rolename)
- else:
- return HttpResponseBadRequest(strip_tags(
- f"unrecognized action u'{action}'"
- ))
-
- response_payload = {
- 'unique_student_identifier': user.username,
- 'rolename': rolename,
- 'action': action,
- 'success': 'yes',
- }
- return JsonResponse(response_payload)
-
@method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True), name='dispatch')
class ListCourseRoleMembersView(APIView):
diff --git a/lms/djangoapps/instructor/views/api_urls.py b/lms/djangoapps/instructor/views/api_urls.py
index 08b68b0760..599aead573 100644
--- a/lms/djangoapps/instructor/views/api_urls.py
+++ b/lms/djangoapps/instructor/views/api_urls.py
@@ -24,7 +24,7 @@ urlpatterns = [
path('students_update_enrollment', api.students_update_enrollment, name='students_update_enrollment'),
path('register_and_enroll_students', api.RegisterAndEnrollStudents.as_view(), name='register_and_enroll_students'),
path('list_course_role_members', api.ListCourseRoleMembersView.as_view(), name='list_course_role_members'),
- path('modify_access', api.modify_access, name='modify_access'),
+ path('modify_access', api.ModifyAccess.as_view(), name='modify_access'),
path('bulk_beta_modify_access', api.bulk_beta_modify_access, name='bulk_beta_modify_access'),
path('get_problem_responses', api.get_problem_responses, name='get_problem_responses'),
path('get_grading_config', api.get_grading_config, name='get_grading_config'),
diff --git a/lms/djangoapps/instructor/views/serializer.py b/lms/djangoapps/instructor/views/serializer.py
index b4f6f76260..463f05ad45 100644
--- a/lms/djangoapps/instructor/views/serializer.py
+++ b/lms/djangoapps/instructor/views/serializer.py
@@ -4,6 +4,7 @@ from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imp
from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _
from rest_framework import serializers
+from .tools import get_student_from_identifier
from lms.djangoapps.instructor.access import ROLES
@@ -28,3 +29,33 @@ class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'email', 'first_name', 'last_name']
+
+
+class AccessSerializer(serializers.Serializer):
+ """
+ Serializer for managing user access changes.
+ This serializer validates and processes the data required to modify
+ user access within a system.
+ """
+ unique_student_identifier = serializers.CharField(
+ max_length=255,
+ help_text="Email or username of user to change access"
+ )
+ rolename = serializers.CharField(
+ help_text="Role name to assign to the user"
+ )
+ action = serializers.ChoiceField(
+ choices=['allow', 'revoke'],
+ help_text="Action to perform on the user's access"
+ )
+
+ def validate_unique_student_identifier(self, value):
+ """
+ Validate that the unique_student_identifier corresponds to an existing user.
+ """
+ try:
+ user = get_student_from_identifier(value)
+ except User.DoesNotExist:
+ return None
+
+ return user
From 688242abd9937559e655166e6c7fc6339f4e1acc Mon Sep 17 00:00:00 2001
From: Kyle McCormick
Date: Tue, 20 Aug 2024 09:20:26 -0400
Subject: [PATCH 058/242] refactor: remove unused builtinblock webpack entry
points (#35299)
Most blocks built into edx-platform have a pair of webpack entry
points, like this:
* {BlockName}Display # js for student+author+public views
* {BlockName}Editor # js for studio view
Prior to a past build refactoring [1], these entry points were
defined in an auto-genered webpack config file. In order
to simplify the js build process, this config generation
step was removed and the new file webpack.builtinblocks.config.js
was checked directly into edx-platform.
However, during that refactoring, pointless entry
points were introduced for HtmlBlock subclasses
About, CourseInfo, and StaticTab, all of which
just use their superclass's JS.
[1] https://github.com/openedx/edx-platform/issues/32481
---
webpack.builtinblocks.config.js | 36 ---------------------------------
1 file changed, 36 deletions(-)
diff --git a/webpack.builtinblocks.config.js b/webpack.builtinblocks.config.js
index 53ce30a9c0..d86f891dc6 100644
--- a/webpack.builtinblocks.config.js
+++ b/webpack.builtinblocks.config.js
@@ -1,17 +1,5 @@
module.exports = {
entry: {
- AboutBlockDisplay: [
- './xmodule/js/src/xmodule.js',
- './xmodule/js/src/html/display.js',
- './xmodule/js/src/javascript_loader.js',
- './xmodule/js/src/collapsible.js',
- './xmodule/js/src/html/imageModal.js',
- './xmodule/js/common_static/js/vendor/draggabilly.js'
- ],
- AboutBlockEditor: [
- './xmodule/js/src/xmodule.js',
- './xmodule/js/src/html/edit.js'
- ],
AnnotatableBlockDisplay: [
'./xmodule/js/src/xmodule.js',
'./xmodule/js/src/html/display.js',
@@ -33,18 +21,6 @@ module.exports = {
'./xmodule/js/src/xmodule.js',
'./xmodule/js/src/sequence/edit.js'
],
- CourseInfoBlockDisplay: [
- './xmodule/js/src/xmodule.js',
- './xmodule/js/src/html/display.js',
- './xmodule/js/src/javascript_loader.js',
- './xmodule/js/src/collapsible.js',
- './xmodule/js/src/html/imageModal.js',
- './xmodule/js/common_static/js/vendor/draggabilly.js'
- ],
- CourseInfoBlockEditor: [
- './xmodule/js/src/xmodule.js',
- './xmodule/js/src/html/edit.js'
- ],
CustomTagBlockDisplay: './xmodule/js/src/xmodule.js',
CustomTagBlockEditor: [
'./xmodule/js/src/xmodule.js',
@@ -104,18 +80,6 @@ module.exports = {
'./xmodule/js/src/xmodule.js',
'./xmodule/js/src/sequence/edit.js'
],
- StaticTabBlockDisplay: [
- './xmodule/js/src/xmodule.js',
- './xmodule/js/src/html/display.js',
- './xmodule/js/src/javascript_loader.js',
- './xmodule/js/src/collapsible.js',
- './xmodule/js/src/html/imageModal.js',
- './xmodule/js/common_static/js/vendor/draggabilly.js'
- ],
- StaticTabBlockEditor: [
- './xmodule/js/src/xmodule.js',
- './xmodule/js/src/html/edit.js'
- ],
VideoBlockDisplay: [
'./xmodule/js/src/xmodule.js',
'./xmodule/js/src/video/10_main.js'
From 375b9d9286d8b2a407af7db41da53a243234afec Mon Sep 17 00:00:00 2001
From: Muhammad Farhan Khan
Date: Tue, 20 Aug 2024 20:05:02 +0500
Subject: [PATCH 059/242] feat: Expose xmodule xblocks Sass variables as
vanilla CSS variables (#35233)
* feat: Expose xmodule xblocks Sass variables as vanilla CSS variables
* https://github.com/openedx/edx-platform/issues/35173
---
cms/static/sass/studio-main-v1.scss | 2 +
.../static/sass/_builtin-block-variables.scss | 73 +++++++++++++++++++
lms/static/sass/_variables.scss | 2 +
3 files changed, 77 insertions(+)
create mode 100644 common/static/sass/_builtin-block-variables.scss
diff --git a/cms/static/sass/studio-main-v1.scss b/cms/static/sass/studio-main-v1.scss
index ac649970d6..5d0cdda2ea 100644
--- a/cms/static/sass/studio-main-v1.scss
+++ b/cms/static/sass/studio-main-v1.scss
@@ -15,6 +15,8 @@
// +Libs and Resets - *do not edit*
// ====================
+
+@import '_builtin-block-variables';
@import 'bourbon/bourbon'; // lib - bourbon
@import 'vendor/bi-app/bi-app-ltr'; // set the layout for left to right languages
@import 'build-v1'; // shared app style assets/rendering
diff --git a/common/static/sass/_builtin-block-variables.scss b/common/static/sass/_builtin-block-variables.scss
new file mode 100644
index 0000000000..2c567c6fb1
--- /dev/null
+++ b/common/static/sass/_builtin-block-variables.scss
@@ -0,0 +1,73 @@
+/*
+ * In pursuit of decoupling the built-in XBlocks from edx-platform's Sass build
+ * and ensuring comprehensive theming support in the extracted XBlocks,
+ * we need to expose Sass variables as CSS variables.
+ *
+ * Ticket/Issue: https://github.com/openedx/edx-platform/issues/35173
+ */
+@import 'bourbon/bourbon';
+@import 'lms/theme/variables';
+@import 'lms/theme/variables-v1';
+@import 'cms/static/sass/partials/cms/theme/_variables';
+@import 'cms/static/sass/partials/cms/theme/_variables-v1';
+@import 'bootstrap/scss/variables';
+@import 'vendor/bi-app/bi-app-ltr';
+@import 'edx-pattern-library-shims/base/_variables.scss';
+
+:root {
+ --action-primary-active-bg: $action-primary-active-bg;
+ --all-text-inputs: $all-text-inputs;
+ --base-font-size: $base-font-size;
+ --base-line-height: $base-line-height;
+ --baseline: $baseline;
+ --black: $black;
+ --black-t2: $black-t2;
+ --blue: $blue;
+ --blue-d1: $blue-d1;
+ --blue-d2: $blue-d2;
+ --blue-d4: $blue-d4;
+ --body-color: $body-color;
+ --border-color: $border-color;
+ --bp-screen-lg: $bp-screen-lg;
+ --btn-brand-focus-background: $btn-brand-focus-background;
+ --correct: $correct;
+ --danger: $danger;
+ --darkGrey: $darkGrey;
+ --error-color: $error-color;
+ --font-bold: $font-bold;
+ --font-family-sans-serif: $font-family-sans-serif;
+ --general-color-accent: $general-color-accent;
+ --gray: $gray;
+ --gray-300: $gray-300;
+ --gray-d1: $gray-d1;
+ --gray-l2: $gray-l2;
+ --gray-l3: $gray-l3;
+ --gray-l4: $gray-l4;
+ --gray-l6: $gray-l6;
+ --incorrect: $incorrect;
+ --lightGrey: $lightGrey;
+ --lighter-base-font-color: $lighter-base-font-color;
+ --link-color: $link-color;
+ --medium-font-size: $medium-font-size;
+ --partially-correct: $partially-correct;
+ --primary: $primary;
+ --shadow: $shadow;
+ --shadow-l1: $shadow-l1;
+ --sidebar-color: $sidebar-color;
+ --small-font-size: $small-font-size;
+ --static-path: $static-path;
+ --submitted: $submitted;
+ --success: $success;
+ --tmg-f2: $tmg-f2;
+ --tmg-s2: $tmg-s2;
+ --transparent: $transparent;
+ --uxpl-gray-background: $uxpl-gray-background;
+ --uxpl-gray-base: $uxpl-gray-base;
+ --uxpl-gray-dark: $uxpl-gray-dark;
+ --very-light-text: $very-light-text;
+ --warning: $warning;
+ --warning-color: $warning-color;
+ --warning-color-accent: $warning-color-accent;
+ --white: $white;
+ --yellow: $yellow;
+}
diff --git a/lms/static/sass/_variables.scss b/lms/static/sass/_variables.scss
index dff9826b94..e1ccc71426 100644
--- a/lms/static/sass/_variables.scss
+++ b/lms/static/sass/_variables.scss
@@ -1,5 +1,7 @@
// LMS-specific variables
+@import '_builtin-block-variables';
+
$text-width-readability-max: 1080px;
// LMS-only colors
From a8ee9a6ca8c7c52f6c327172ebe1eb5d989c74da Mon Sep 17 00:00:00 2001
From: Muhammad Soban Javed
Date: Wed, 21 Aug 2024 19:17:32 +0500
Subject: [PATCH 060/242] chore: Pin algoliasearch to v3
Recently algoliasearch released the new major version v4 of its Python client and it has multiple breaking changes, which will impact one of the edX platform plugins. So we must make that plugin compatible with algoliasearch v4 before upgrading this package.
Changelog: https://algolia.com/doc/libraries/python/v4/upgrade/
---
requirements/constraints.txt | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 0a95e5c8a4..7aa23a04ba 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -12,6 +12,12 @@
# This file contains all common constraints for edx-repos
-c common_constraints.txt
+# Date: 2024-08-21
+# Description: This is the major upgrade of algoliasearch python client and it will
+# break one of the edX' platform plugin, so we need to make that compatible first.
+# Ticket: https://github.com/openedx/edx-platform/issues/35334
+algoliasearch<4.0.0
+
# As it is not clarified what exact breaking changes will be introduced as per
# the next major release, ensure the installed version is within boundaries.
celery>=5.2.2,<6.0.0
From 30bfbd532c406ebed033f1f3bab3f6d100278883 Mon Sep 17 00:00:00 2001
From: iamsobanjaved <58461728+iamsobanjaved@users.noreply.github.com>
Date: Wed, 21 Aug 2024 14:34:30 +0000
Subject: [PATCH 061/242] feat: Upgrade Python dependency algoliasearch
Commit generated by workflow `openedx/edx-platform/.github/workflows/upgrade-one-python-dependency.yml@refs/heads/master`
---
requirements/edx/base.txt | 16 ++++++----------
requirements/edx/development.txt | 13 +++----------
requirements/edx/doc.txt | 15 +++++----------
requirements/edx/testing.txt | 15 +++++----------
4 files changed, 19 insertions(+), 40 deletions(-)
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 8e0a834436..0dd2b66fb2 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -12,13 +12,14 @@ aiohappyeyeballs==2.4.0
# via aiohttp
aiohttp==3.10.5
# via
- # algoliasearch
# geoip2
# openai
aiosignal==1.3.1
# via aiohttp
-algoliasearch==4.0.2
- # via -r requirements/edx/bundled.in
+algoliasearch==3.0.0
+ # via
+ # -c requirements/edx/../constraints.txt
+ # -r requirements/edx/bundled.in
amqp==5.2.0
# via kombu
analytics-python==1.4.post1
@@ -36,8 +37,6 @@ asgiref==3.8.1
# django-countries
asn1crypto==1.5.1
# via snowflake-connector-python
-async-timeout==4.0.3
- # via algoliasearch
attrs==24.2.0
# via
# -r requirements/edx/kernel.in
@@ -917,9 +916,7 @@ pycryptodomex==3.20.0
# lti-consumer-xblock
# pyjwkest
pydantic==2.8.2
- # via
- # algoliasearch
- # camel-converter
+ # via camel-converter
pydantic-core==2.20.1
# via pydantic
pygments==2.18.0
@@ -982,7 +979,6 @@ pysrt==1.1.2
python-dateutil==2.9.0.post0
# via
# -r requirements/edx/kernel.in
- # algoliasearch
# analytics-python
# botocore
# celery
@@ -1055,6 +1051,7 @@ regex==2024.7.24
requests==2.32.3
# via
# -r requirements/edx/paver.txt
+ # algoliasearch
# analytics-python
# cachecontrol
# django-oauth-toolkit
@@ -1223,7 +1220,6 @@ urllib3==1.26.19
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/paver.txt
- # algoliasearch
# botocore
# elasticsearch
# py2neo
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index 35a171123a..5be8a0abe3 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -25,7 +25,6 @@ aiohttp==3.10.5
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
- # algoliasearch
# geoip2
# openai
aiosignal==1.3.1
@@ -37,8 +36,9 @@ alabaster==1.0.0
# via
# -r requirements/edx/doc.txt
# sphinx
-algoliasearch==4.0.2
+algoliasearch==3.0.0
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
amqp==5.2.0
@@ -86,11 +86,6 @@ astroid==2.13.5
# -r requirements/edx/testing.txt
# pylint
# pylint-celery
-async-timeout==4.0.3
- # via
- # -r requirements/edx/doc.txt
- # -r requirements/edx/testing.txt
- # algoliasearch
attrs==24.2.0
# via
# -r requirements/edx/doc.txt
@@ -1556,7 +1551,6 @@ pydantic==2.8.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
- # algoliasearch
# camel-converter
# fastapi
pydantic-core==2.20.1
@@ -1718,7 +1712,6 @@ python-dateutil==2.9.0.post0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
- # algoliasearch
# analytics-python
# botocore
# celery
@@ -1825,6 +1818,7 @@ requests==2.32.3
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
+ # algoliasearch
# analytics-python
# cachecontrol
# django-oauth-toolkit
@@ -2162,7 +2156,6 @@ urllib3==1.26.19
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
- # algoliasearch
# botocore
# elasticsearch
# py2neo
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 2367be11e0..870587e8d7 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -17,7 +17,6 @@ aiohappyeyeballs==2.4.0
aiohttp==3.10.5
# via
# -r requirements/edx/base.txt
- # algoliasearch
# geoip2
# openai
aiosignal==1.3.1
@@ -26,8 +25,10 @@ aiosignal==1.3.1
# aiohttp
alabaster==1.0.0
# via sphinx
-algoliasearch==4.0.2
- # via -r requirements/edx/base.txt
+algoliasearch==3.0.0
+ # via
+ # -c requirements/edx/../constraints.txt
+ # -r requirements/edx/base.txt
amqp==5.2.0
# via
# -r requirements/edx/base.txt
@@ -56,10 +57,6 @@ asn1crypto==1.5.1
# via
# -r requirements/edx/base.txt
# snowflake-connector-python
-async-timeout==4.0.3
- # via
- # -r requirements/edx/base.txt
- # algoliasearch
attrs==24.2.0
# via
# -r requirements/edx/base.txt
@@ -1104,7 +1101,6 @@ pycryptodomex==3.20.0
pydantic==2.8.2
# via
# -r requirements/edx/base.txt
- # algoliasearch
# camel-converter
pydantic-core==2.20.1
# via
@@ -1182,7 +1178,6 @@ pysrt==1.1.2
python-dateutil==2.9.0.post0
# via
# -r requirements/edx/base.txt
- # algoliasearch
# analytics-python
# botocore
# celery
@@ -1265,6 +1260,7 @@ regex==2024.7.24
requests==2.32.3
# via
# -r requirements/edx/base.txt
+ # algoliasearch
# analytics-python
# cachecontrol
# django-oauth-toolkit
@@ -1507,7 +1503,6 @@ urllib3==1.26.19
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
- # algoliasearch
# botocore
# elasticsearch
# py2neo
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 8b5ef476ff..1f1d120cc0 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -15,15 +15,16 @@ aiohappyeyeballs==2.4.0
aiohttp==3.10.5
# via
# -r requirements/edx/base.txt
- # algoliasearch
# geoip2
# openai
aiosignal==1.3.1
# via
# -r requirements/edx/base.txt
# aiohttp
-algoliasearch==4.0.2
- # via -r requirements/edx/base.txt
+algoliasearch==3.0.0
+ # via
+ # -c requirements/edx/../constraints.txt
+ # -r requirements/edx/base.txt
amqp==5.2.0
# via
# -r requirements/edx/base.txt
@@ -58,10 +59,6 @@ astroid==2.13.5
# via
# pylint
# pylint-celery
-async-timeout==4.0.3
- # via
- # -r requirements/edx/base.txt
- # algoliasearch
attrs==24.2.0
# via
# -r requirements/edx/base.txt
@@ -1174,7 +1171,6 @@ pycryptodomex==3.20.0
pydantic==2.8.2
# via
# -r requirements/edx/base.txt
- # algoliasearch
# camel-converter
# fastapi
pydantic-core==2.20.1
@@ -1298,7 +1294,6 @@ pytest-xdist[psutil]==3.6.1
python-dateutil==2.9.0.post0
# via
# -r requirements/edx/base.txt
- # algoliasearch
# analytics-python
# botocore
# celery
@@ -1382,6 +1377,7 @@ regex==2024.7.24
requests==2.32.3
# via
# -r requirements/edx/base.txt
+ # algoliasearch
# analytics-python
# cachecontrol
# django-oauth-toolkit
@@ -1598,7 +1594,6 @@ urllib3==1.26.19
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
- # algoliasearch
# botocore
# elasticsearch
# py2neo
From 0c2a919c3a1b13aabbbccb4172e5b35b6c69da04 Mon Sep 17 00:00:00 2001
From: Prashant Makwana
Date: Wed, 21 Aug 2024 14:27:45 -0400
Subject: [PATCH 062/242] chore: bumping version to 4.23.9 (#35335)
Co-authored-by: Kira Miller <31229189+kiram15@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 7aa23a04ba..aa45d9944d 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -26,7 +26,7 @@ celery>=5.2.2,<6.0.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==4.23.8
+edx-enterprise==4.23.9
# Stay on LTS version, remove once this is added to common constraint
Django<5.0
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 0dd2b66fb2..873446da13 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -467,7 +467,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.8
+edx-enterprise==4.23.9
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index 5be8a0abe3..4951b16608 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -741,7 +741,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.8
+edx-enterprise==4.23.9
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 870587e8d7..8c9c7bbbb3 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -547,7 +547,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.8
+edx-enterprise==4.23.9
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 1f1d120cc0..87c98537d3 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -571,7 +571,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.8
+edx-enterprise==4.23.9
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
From e4d3eb332caddaf61dc3eb8d4e76074d999873f8 Mon Sep 17 00:00:00 2001
From: Abdullah Qureshi <111448185+arbimaq@users.noreply.github.com>
Date: Thu, 22 Aug 2024 10:42:24 +0500
Subject: [PATCH 063/242] fix: remove deprecated runtime.render_template
---
xmodule/mako_block.py | 9 +--------
1 file changed, 1 insertion(+), 8 deletions(-)
diff --git a/xmodule/mako_block.py b/xmodule/mako_block.py
index abf3a93f38..f9e84d5e19 100644
--- a/xmodule/mako_block.py
+++ b/xmodule/mako_block.py
@@ -46,13 +46,6 @@ class MakoTemplateBlockBase:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- if getattr(self.runtime, 'render_template', None) is None:
- raise TypeError(
- '{runtime} must have a render_template function'
- ' in order to use a MakoDescriptor'.format(
- runtime=self.runtime,
- )
- )
def get_context(self):
"""
@@ -69,7 +62,7 @@ class MakoTemplateBlockBase:
"""
# pylint: disable=no-member
fragment = Fragment(
- self.runtime.render_template(self.mako_template, self.get_context())
+ self.runtime.service(self, 'mako').render_template(self.mako_template, self.get_context())
)
shim_xmodule_js(fragment, self.js_module_name)
return fragment
From f93d16f31ae98ef5cb575ce7d16d06cd9b1953e7 Mon Sep 17 00:00:00 2001
From: Awais Qureshi
Date: Thu, 22 Aug 2024 13:37:45 +0500
Subject: [PATCH 064/242] feat: upgrading simple api to drf compatible ( 3rd
api ) list_email_content (#35111)
* feat: upgrading simple api to drf compatible.
---
lms/djangoapps/instructor/views/api.py | 38 ++++++++++++++-------
lms/djangoapps/instructor/views/api_urls.py | 2 +-
2 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py
index 2813daa8a3..7ca1e70467 100644
--- a/lms/djangoapps/instructor/views/api.py
+++ b/lms/djangoapps/instructor/views/api.py
@@ -2172,23 +2172,35 @@ def list_background_email_tasks(request, course_id):
return JsonResponse(response_payload)
-@require_POST
-@ensure_csrf_cookie
-@cache_control(no_cache=True, no_store=True, must_revalidate=True)
-@require_course_permission(permissions.EMAIL)
-def list_email_content(request, course_id):
+@method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True), name='dispatch')
+class ListEmailContent(APIView):
"""
List the content of bulk emails sent
"""
- course_id = CourseKey.from_string(course_id)
- task_type = InstructorTaskTypes.BULK_COURSE_EMAIL
- # First get tasks list of bulk emails sent
- emails = task_api.get_instructor_task_history(course_id, task_type=task_type)
+ permission_classes = (IsAuthenticated, permissions.InstructorPermission)
+ permission_name = permissions.EMAIL
- response_payload = {
- 'emails': list(map(extract_email_features, emails)),
- }
- return JsonResponse(response_payload)
+ @method_decorator(ensure_csrf_cookie)
+ def post(self, request, course_id):
+ """
+ List the content of bulk emails sent for a specific course.
+
+ Args:
+ request (HttpRequest): The HTTP request object.
+ course_id (str): The ID of the course for which to list the bulk emails.
+
+ Returns:
+ HttpResponse: A response object containing the list of bulk email contents.
+ """
+ course_id = CourseKey.from_string(course_id)
+ task_type = InstructorTaskTypes.BULK_COURSE_EMAIL
+ # First get tasks list of bulk emails sent
+ emails = task_api.get_instructor_task_history(course_id, task_type=task_type)
+
+ response_payload = {
+ 'emails': list(map(extract_email_features, emails)),
+ }
+ return JsonResponse(response_payload)
class InstructorTaskSerializer(serializers.Serializer): # pylint: disable=abstract-method
diff --git a/lms/djangoapps/instructor/views/api_urls.py b/lms/djangoapps/instructor/views/api_urls.py
index 599aead573..c0e12e4675 100644
--- a/lms/djangoapps/instructor/views/api_urls.py
+++ b/lms/djangoapps/instructor/views/api_urls.py
@@ -46,7 +46,7 @@ urlpatterns = [
name='mark_student_can_skip_entrance_exam'),
path('list_instructor_tasks', api.list_instructor_tasks, name='list_instructor_tasks'),
path('list_background_email_tasks', api.list_background_email_tasks, name='list_background_email_tasks'),
- path('list_email_content', api.list_email_content, name='list_email_content'),
+ path('list_email_content', api.ListEmailContent.as_view(), name='list_email_content'),
path('list_forum_members', api.list_forum_members, name='list_forum_members'),
path('update_forum_role_membership', api.update_forum_role_membership, name='update_forum_role_membership'),
path('send_email', api.send_email, name='send_email'),
From 44112aa12dbf916576415ef565029d228f4d311e Mon Sep 17 00:00:00 2001
From: Tobias Macey
Date: Thu, 22 Aug 2024 06:43:15 -0400
Subject: [PATCH 065/242] fix: Respect the authsource kwarg for MongoDB
connections (#35239)
* fix: Respect the authsource kwarg for MongoDB connections
The changes for upgrading to PyMongo 4.4 introduced an authentication
bug in Mongo connections. The `authSource` parameter was being
hard-coded to use the database being connected to. In Mongo the `admin`
db is typically the source of authentication, so unless the user was
explicitly created in the target db then any attempts to connect would
result in authentication failures.
This restores the behavior of allowing for the lowercased `authsource`
kwarg to be used for the `authSource` connection parameter, while
otherwise respecting the operator's configuration parameters.
* fix: Respect the authsource kwarg for MongoDB connections
The changes for upgrading to PyMongo 4.4 introduced an authentication
bug in Mongo connections. The `authSource` parameter was being
hard-coded to use the database being connected to. In Mongo the `admin`
db is typically the source of authentication, so unless the user was
explicitly created in the target db then any attempts to connect would
result in authentication failures.
This restores the behavior of allowing for the lowercased `authsource`
kwarg to be used for the `authSource` connection parameter, while
otherwise respecting the operator's configuration parameters.
---
xmodule/mongo_utils.py | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/xmodule/mongo_utils.py b/xmodule/mongo_utils.py
index b86abd28b4..5aecbfc405 100644
--- a/xmodule/mongo_utils.py
+++ b/xmodule/mongo_utils.py
@@ -30,8 +30,11 @@ def connect_to_mongodb(
handles AutoReconnect errors by retrying read operations, since these exceptions
typically indicate a temporary step-down condition for MongoDB.
"""
- # If the MongoDB server uses a separate authentication database that should be specified here
- auth_source = kwargs.get('authsource', '') or None
+ # If the MongoDB server uses a separate authentication database that should be specified here.
+ # Convert the lowercased authsource parameter to the camel-cased authSource expected by MongoClient.
+ auth_source = db
+ if auth_source_key := {'authSource', 'authsource'}.intersection(set(kwargs.keys())):
+ auth_source = kwargs.pop(auth_source_key.pop()) or db
# sanitize a kwarg which may be present and is no longer expected
# AED 2020-03-02 TODO: Remove this when 'auth_source' will no longer exist in kwargs
@@ -63,7 +66,7 @@ def connect_to_mongodb(
}
if user is not None and password is not None and not db.startswith('test_'):
- connection_params.update({'username': user, 'password': password, 'authSource': db})
+ connection_params.update({'username': user, 'password': password, 'authSource': auth_source})
mongo_conn = pymongo.MongoClient(**connection_params)
From 72d060cbeb1a02c740658ac086e05383e110b877 Mon Sep 17 00:00:00 2001
From: Nathan Sprenkle
Date: Thu, 22 Aug 2024 12:34:30 -0400
Subject: [PATCH 066/242] feat: update XBlock to 5.1.0 (#35325)
---
requirements/constraints.txt | 4 ----
requirements/edx/base.txt | 3 +--
requirements/edx/development.txt | 3 +--
requirements/edx/doc.txt | 3 +--
requirements/edx/testing.txt | 3 +--
5 files changed, 4 insertions(+), 12 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index aa45d9944d..146d860510 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -142,7 +142,3 @@ django-storages<1.14.4
# We are pinning this until after all the smaller migrations get handled and then we can migrate this all at once.
# Ticket to unpin: https://github.com/edx/edx-arch-experiments/issues/760
social-auth-app-django<=5.4.1
-
-# Xblock==5.0.0 changed how entrypoints were loaded, breaking a workaround for overriding blocks.
-# See ticket: https://github.com/openedx/XBlock/issues/777
-xblock[django]==4.0.1
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 873446da13..48b31cad03 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -1258,9 +1258,8 @@ webob==1.8.8
# xblock
wrapt==1.16.0
# via -r requirements/edx/paver.txt
-xblock[django]==4.0.1
+xblock[django]==5.1.0
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
# acid-xblock
# crowdsourcehinter-xblock
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index 4951b16608..a4825209b6 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -2231,9 +2231,8 @@ wrapt==1.16.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# astroid
-xblock[django]==4.0.1
+xblock[django]==5.1.0
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# acid-xblock
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 8c9c7bbbb3..d4bc6c14d3 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -1549,9 +1549,8 @@ webob==1.8.8
# xblock
wrapt==1.16.0
# via -r requirements/edx/base.txt
-xblock[django]==4.0.1
+xblock[django]==5.1.0
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# acid-xblock
# crowdsourcehinter-xblock
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 87c98537d3..6527b45b56 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -1646,9 +1646,8 @@ wrapt==1.16.0
# via
# -r requirements/edx/base.txt
# astroid
-xblock[django]==4.0.1
+xblock[django]==5.1.0
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# acid-xblock
# crowdsourcehinter-xblock
From 6071992281437ae0eaa57c66755a74290ab2953f Mon Sep 17 00:00:00 2001
From: Deborah Kaplan
Date: Thu, 22 Aug 2024 14:56:51 -0400
Subject: [PATCH 067/242] feat: lint this file (#35348)
I'm about to make a bunch of changes to this file, and before I do I'm
saving it and letting the linter reformatted to our current code style
standards, so that code reviewers won't have to read a mix of lint and
code changes.
FIXES: APER-3554
---
openedx/core/djangoapps/user_api/views.py | 41 ++++++++++++-----------
1 file changed, 22 insertions(+), 19 deletions(-)
diff --git a/openedx/core/djangoapps/user_api/views.py b/openedx/core/djangoapps/user_api/views.py
index b4fcc68db6..d52493556a 100644
--- a/openedx/core/djangoapps/user_api/views.py
+++ b/openedx/core/djangoapps/user_api/views.py
@@ -1,6 +1,5 @@
"""HTTP end-points for the User API. """
-
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.http import HttpResponse
from django.utils.decorators import method_decorator
@@ -16,21 +15,22 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from openedx.core.djangoapps.django_comment_common.models import Role
-from openedx.core.lib.api.view_utils import require_post_params
from openedx.core.djangoapps.user_api.models import UserPreference
from openedx.core.djangoapps.user_api.preferences.api import get_country_time_zones, update_email_opt_in
from openedx.core.djangoapps.user_api.serializers import (
CountryTimeZoneSerializer,
UserPreferenceSerializer,
- UserSerializer
+ UserSerializer,
)
from openedx.core.lib.api.permissions import ApiKeyHeaderPermission
+from openedx.core.lib.api.view_utils import require_post_params
class UserViewSet(viewsets.ReadOnlyModelViewSet):
"""
DRF class for interacting with the User ORM object
"""
+
permission_classes = (ApiKeyHeaderPermission,)
queryset = User.objects.all().prefetch_related("preferences").select_related("profile")
serializer_class = UserSerializer
@@ -42,6 +42,7 @@ class ForumRoleUsersListView(generics.ListAPIView):
"""
Forum roles are represented by a list of user dicts
"""
+
permission_classes = (ApiKeyHeaderPermission,)
serializer_class = UserSerializer
paginate_by = 10
@@ -51,10 +52,10 @@ class ForumRoleUsersListView(generics.ListAPIView):
"""
Return a list of users with the specified role/course pair
"""
- name = self.kwargs['name']
- course_id_string = self.request.query_params.get('course_id')
+ name = self.kwargs["name"]
+ course_id_string = self.request.query_params.get("course_id")
if not course_id_string:
- raise ParseError('course_id must be specified')
+ raise ParseError("course_id must be specified")
course_id = CourseKey.from_string(course_id_string)
role = Role.objects.get_or_create(course_id=course_id, name=name)[0]
users = role.users.prefetch_related("preferences").select_related("profile").all()
@@ -65,6 +66,7 @@ class UserPreferenceViewSet(viewsets.ReadOnlyModelViewSet):
"""
DRF class for interacting with the UserPreference ORM
"""
+
permission_classes = (ApiKeyHeaderPermission,)
queryset = UserPreference.objects.all()
filter_backends = (DjangoFilterBackend,)
@@ -78,26 +80,30 @@ class PreferenceUsersListView(generics.ListAPIView):
"""
DRF class for listing a user's preferences
"""
+
permission_classes = (ApiKeyHeaderPermission,)
serializer_class = UserSerializer
paginate_by = 10
paginate_by_param = "page_size"
def get_queryset(self):
- return User.objects.filter(
- preferences__key=self.kwargs["pref_key"]
- ).prefetch_related("preferences").select_related("profile")
+ return (
+ User.objects.filter(preferences__key=self.kwargs["pref_key"])
+ .prefetch_related("preferences")
+ .select_related("profile")
+ )
class UpdateEmailOptInPreference(APIView):
- """View for updating the email opt in preference. """
+ """View for updating the email opt in preference."""
+
authentication_classes = (SessionAuthenticationAllowInactiveUser,)
permission_classes = (IsAuthenticated,)
@method_decorator(require_post_params(["course_id", "email_opt_in"]))
@method_decorator(ensure_csrf_cookie)
def post(self, request):
- """ Post function for updating the email opt in preference.
+ """Post function for updating the email opt in preference.
Allows the modification or creation of the email opt in preference at an
organizational level.
@@ -111,17 +117,13 @@ class UpdateEmailOptInPreference(APIView):
assume False.
"""
- course_id = request.data['course_id']
+ course_id = request.data["course_id"]
try:
org = locator.CourseLocator.from_string(course_id).org
except InvalidKeyError:
- return HttpResponse(
- status=400,
- content=f"No course '{course_id}' found",
- content_type="text/plain"
- )
+ return HttpResponse(status=400, content=f"No course '{course_id}' found", content_type="text/plain")
# Only check for true. All other values are False.
- email_opt_in = request.data['email_opt_in'].lower() == 'true'
+ email_opt_in = request.data["email_opt_in"].lower() == "true"
update_email_opt_in(request.user, org, email_opt_in)
return HttpResponse(status=status.HTTP_200_OK)
@@ -152,9 +154,10 @@ class CountryTimeZoneListView(generics.ListAPIView):
* time_zone: The name of the time zone.
* description: The display version of the time zone
"""
+
serializer_class = CountryTimeZoneSerializer
paginator = None
def get_queryset(self):
- country_code = self.request.GET.get('country_code', None)
+ country_code = self.request.GET.get("country_code", None)
return get_country_time_zones(country_code)
From 36a3b0ba8178aa479464b9eccfb9f9b0e5e0240e Mon Sep 17 00:00:00 2001
From: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>
Date: Thu, 22 Aug 2024 15:49:21 -0400
Subject: [PATCH 068/242] Revert "fix: update course discussion config before
course load (#35219)" (#35349)
This reverts commit 5c0942481ce292a90f86259bd223d66e7ceffe9f.
---
cms/djangoapps/contentstore/views/course.py | 5 -----
cms/djangoapps/contentstore/views/tests/test_course_index.py | 4 ++--
2 files changed, 2 insertions(+), 7 deletions(-)
diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py
index 4647e4fdcc..9f6cfb7c43 100644
--- a/cms/djangoapps/contentstore/views/course.py
+++ b/cms/djangoapps/contentstore/views/course.py
@@ -58,7 +58,6 @@ from common.djangoapps.util.json_request import JsonResponse, JsonResponseBadReq
from common.djangoapps.util.string_utils import _has_non_ascii_characters
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.credit.tasks import update_credit_course_requirements
-from openedx.core.djangoapps.discussions.tasks import update_discussions_settings_from_course
from openedx.core.djangoapps.models.course_details import CourseDetails
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangolib.js_utils import dump_js_escaped_json
@@ -303,10 +302,6 @@ def course_handler(request, course_key_string=None):
else:
return HttpResponseBadRequest()
elif request.method == 'GET': # assume html
- # Update course discussion settings, sometimes the course discussion settings are not updated
- # when the course is created, so we need to update them here.
- course_key = CourseKey.from_string(course_key_string)
- update_discussions_settings_from_course(course_key)
if course_key_string is None:
return redirect(reverse('home'))
else:
diff --git a/cms/djangoapps/contentstore/views/tests/test_course_index.py b/cms/djangoapps/contentstore/views/tests/test_course_index.py
index 30e02214a1..c3dcfe5305 100644
--- a/cms/djangoapps/contentstore/views/tests/test_course_index.py
+++ b/cms/djangoapps/contentstore/views/tests/test_course_index.py
@@ -717,8 +717,8 @@ class TestCourseOutline(CourseTestCase):
"""
Test to check number of queries made to mysql and mongo
"""
- with self.assertNumQueries(32, table_ignorelist=WAFFLE_TABLES):
- with check_mongo_calls(5):
+ with self.assertNumQueries(29, table_ignorelist=WAFFLE_TABLES):
+ with check_mongo_calls(3):
self.client.get_html(reverse_course_url('course_handler', self.course.id))
From 5fbcc794cfb49d30e9f4eca6536234817b57b7f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=B4mulo=20Penido?=
Date: Fri, 23 Aug 2024 02:03:31 -0300
Subject: [PATCH 069/242] feat: add collections app from openedx-learning
(#35312)
---
cms/envs/common.py | 1 +
lms/envs/common.py | 1 +
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
7 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/cms/envs/common.py b/cms/envs/common.py
index be837c5189..45a8e97f3e 100644
--- a/cms/envs/common.py
+++ b/cms/envs/common.py
@@ -1880,6 +1880,7 @@ INSTALLED_APPS = [
'openedx_events',
# Learning Core Apps, used by v2 content libraries (content_libraries app)
+ "openedx_learning.apps.authoring.collections",
"openedx_learning.apps.authoring.components",
"openedx_learning.apps.authoring.contents",
"openedx_learning.apps.authoring.publishing",
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 18d07afd71..04a1753838 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -3394,6 +3394,7 @@ INSTALLED_APPS = [
'openedx_events',
# Learning Core Apps, used by v2 content libraries (content_libraries app)
+ "openedx_learning.apps.authoring.collections",
"openedx_learning.apps.authoring.components",
"openedx_learning.apps.authoring.contents",
"openedx_learning.apps.authoring.publishing",
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 146d860510..74263b5f71 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -93,7 +93,7 @@ libsass==0.10.0
click==8.1.6
# pinning this version to avoid updates while the library is being developed
-openedx-learning==0.10.1
+openedx-learning==0.11.1
# Open AI version 1.0.0 dropped support for openai.ChatCompletion which is currently in use in enterprise.
openai<=0.28.1
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 48b31cad03..27de7847f2 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -823,7 +823,7 @@ openedx-filters==1.9.0
# -r requirements/edx/kernel.in
# lti-consumer-xblock
# ora2
-openedx-learning==0.10.1
+openedx-learning==0.11.1
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index a4825209b6..0bdc8144ee 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -1372,7 +1372,7 @@ openedx-filters==1.9.0
# -r requirements/edx/testing.txt
# lti-consumer-xblock
# ora2
-openedx-learning==0.10.1
+openedx-learning==0.11.1
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index d4bc6c14d3..91b30d81df 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -982,7 +982,7 @@ openedx-filters==1.9.0
# -r requirements/edx/base.txt
# lti-consumer-xblock
# ora2
-openedx-learning==0.10.1
+openedx-learning==0.11.1
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 6527b45b56..2092c93548 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -1033,7 +1033,7 @@ openedx-filters==1.9.0
# -r requirements/edx/base.txt
# lti-consumer-xblock
# ora2
-openedx-learning==0.10.1
+openedx-learning==0.11.1
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
From 71f410eb4072257df8fdd99c0200c27bdee0d112 Mon Sep 17 00:00:00 2001
From: Muhammad Adeel Tajamul
<77053848+muhammadadeeltajamul@users.noreply.github.com>
Date: Sun, 25 Aug 2024 23:24:05 -0700
Subject: [PATCH 070/242] feat: added snowflake events for email digest
unsubscribe (#35329)
---
.../core/djangoapps/notifications/email/utils.py | 2 ++
openedx/core/djangoapps/notifications/events.py | 15 +++++++++++++++
2 files changed, 17 insertions(+)
diff --git a/openedx/core/djangoapps/notifications/email/utils.py b/openedx/core/djangoapps/notifications/email/utils.py
index da288750bb..e36c435e8a 100644
--- a/openedx/core/djangoapps/notifications/email/utils.py
+++ b/openedx/core/djangoapps/notifications/email/utils.py
@@ -19,6 +19,7 @@ from openedx.core.djangoapps.notifications.base_notification import (
)
from openedx.core.djangoapps.notifications.config.waffle import ENABLE_EMAIL_NOTIFICATIONS
from openedx.core.djangoapps.notifications.email_notifications import EmailCadence
+from openedx.core.djangoapps.notifications.events import notification_preference_unsubscribe_event
from openedx.core.djangoapps.notifications.models import (
CourseNotificationPreference,
get_course_notification_preference_config_version
@@ -395,3 +396,4 @@ def update_user_preferences_from_patch(encrypted_username, encrypted_patch):
if pref_value else EmailCadence.NEVER
type_prefs['email_cadence'] = cadence_value
preference.save()
+ notification_preference_unsubscribe_event(user)
diff --git a/openedx/core/djangoapps/notifications/events.py b/openedx/core/djangoapps/notifications/events.py
index fb50e13494..91b12075a8 100644
--- a/openedx/core/djangoapps/notifications/events.py
+++ b/openedx/core/djangoapps/notifications/events.py
@@ -10,6 +10,7 @@ NOTIFICATION_READ = 'edx.notifications.read'
NOTIFICATION_APP_ALL_READ = 'edx.notifications.app_all_read'
NOTIFICATION_PREFERENCES_UPDATED = 'edx.notifications.preferences.updated'
NOTIFICATION_TRAY_OPENED = 'edx.notifications.tray_opened'
+NOTIFICATION_PREFERENCE_UNSUBSCRIBE = 'edx.notifications.preferences.one_click_unsubscribe'
def get_user_forums_roles(user, course_id):
@@ -155,3 +156,17 @@ def notification_tray_opened_event(user, unseen_notifications_count):
'unseen_notifications_count': unseen_notifications_count,
}
)
+
+
+def notification_preference_unsubscribe_event(user):
+ """
+ Emits an event when user clicks on one-click-unsubscribe url
+ """
+ event_data = {
+ 'user_id': user.id,
+ 'username': user.username,
+ 'event_type': 'email_digest_unsubscribe'
+ }
+ with tracker.get_tracker().context(NOTIFICATION_PREFERENCE_UNSUBSCRIBE, event_data):
+ tracker.emit(NOTIFICATION_PREFERENCE_UNSUBSCRIBE, event_data)
+ segment.track(user.id, NOTIFICATION_PREFERENCE_UNSUBSCRIBE, event_data)
From 66f3a0803c99cdc14ca809d20cd98a0ee4140783 Mon Sep 17 00:00:00 2001
From: Ahtisham Shahid
Date: Mon, 26 Aug 2024 13:13:47 +0500
Subject: [PATCH 071/242] feat: added email content in misc notifications
(#35341)
---
.../rest_api/discussions_notifications.py | 65 ++++++++++++--
lms/djangoapps/discussion/rest_api/tasks.py | 4 +-
.../tests/test_discussions_notifications.py | 84 ++++++++++++++++++-
.../discussion/rest_api/tests/test_tasks.py | 67 +++++++++++++--
.../discussion/rest_api/tests/utils.py | 3 +-
lms/djangoapps/discussion/signals/handlers.py | 3 +-
6 files changed, 209 insertions(+), 17 deletions(-)
diff --git a/lms/djangoapps/discussion/rest_api/discussions_notifications.py b/lms/djangoapps/discussion/rest_api/discussions_notifications.py
index 21b1e27fcd..96e392c35d 100644
--- a/lms/djangoapps/discussion/rest_api/discussions_notifications.py
+++ b/lms/djangoapps/discussion/rest_api/discussions_notifications.py
@@ -3,7 +3,10 @@ Discussion notifications sender util.
"""
import re
+from bs4 import BeautifulSoup
from django.conf import settings
+from django.utils.text import Truncator
+
from lms.djangoapps.discussion.django_comment_client.permissions import get_team
from openedx_events.learning.data import UserNotificationData, CourseNotificationData
from openedx_events.learning.signals import USER_NOTIFICATION_REQUESTED, COURSE_NOTIFICATION_REQUESTED
@@ -27,13 +30,24 @@ class DiscussionNotificationSender:
Class to send notifications to users who are subscribed to the thread.
"""
- def __init__(self, thread, course, creator, parent_id=None):
+ def __init__(self, thread, course, creator, parent_id=None, comment_id=None):
self.thread = thread
self.course = course
self.creator = creator
self.parent_id = parent_id
+ self.comment_id = comment_id
self.parent_response = None
+ self.comment = None
self._get_parent_response()
+ self._get_comment()
+
+ def _get_comment(self):
+ """
+ Get comment object
+ """
+ if not self.comment_id:
+ return
+ self.comment = Comment(id=self.comment_id).retrieve()
def _send_notification(self, user_ids, notification_type, extra_context=None):
"""
@@ -99,7 +113,10 @@ class DiscussionNotificationSender:
there is a response to the main thread.
"""
if not self.parent_id and self.creator.id != int(self.thread.user_id):
- self._send_notification([self.thread.user_id], "new_response")
+ context = {
+ 'email_content': clean_thread_html_body(self.comment.body),
+ }
+ self._send_notification([self.thread.user_id], "new_response", extra_context=context)
def _response_and_thread_has_same_creator(self) -> bool:
"""
@@ -136,6 +153,7 @@ class DiscussionNotificationSender:
context = {
"author_name": str(author_name),
"author_pronoun": str(author_pronoun),
+ "email_content": clean_thread_html_body(self.comment.body),
}
self._send_notification([self.thread.user_id], "new_comment", extra_context=context)
@@ -149,7 +167,14 @@ class DiscussionNotificationSender:
self.creator.id != int(self.parent_response.user_id) and not
self._response_and_thread_has_same_creator()
):
- self._send_notification([self.parent_response.user_id], "new_comment_on_response")
+ context = {
+ "email_content": clean_thread_html_body(self.comment.body),
+ }
+ self._send_notification(
+ [self.parent_response.user_id],
+ "new_comment_on_response",
+ extra_context=context
+ )
def _check_if_subscriber_is_not_thread_or_content_creator(self, subscriber_id) -> bool:
"""
@@ -190,7 +215,12 @@ class DiscussionNotificationSender:
# Remove duplicate users from the list of users to send notification
users = list(set(users))
if not self.parent_id:
- self._send_notification(users, "response_on_followed_post")
+ self._send_notification(
+ users,
+ "response_on_followed_post",
+ extra_context={
+ "email_content": clean_thread_html_body(self.comment.body),
+ })
else:
author_name = f"{self.parent_response.username}'s"
# use 'their' if comment author is also response author.
@@ -206,6 +236,7 @@ class DiscussionNotificationSender:
extra_context={
"author_name": str(author_name),
"author_pronoun": str(author_pronoun),
+ "email_content": clean_thread_html_body(self.comment.body),
}
)
@@ -289,7 +320,8 @@ class DiscussionNotificationSender:
]
context = {
'username': self.creator.username,
- 'post_title': self.thread.title
+ 'post_title': self.thread.title,
+ "email_content": clean_thread_html_body(self.thread.body),
}
self._send_course_wide_notification(notification_type, audience_filters, context)
@@ -339,3 +371,26 @@ def is_discussion_cohorted(course_key_str):
def remove_html_tags(text):
clean = re.compile('<.*?>')
return re.sub(clean, '', text)
+
+
+def clean_thread_html_body(html_body):
+ """
+ Get post body with tags removed and limited to 500 characters
+ """
+ html_body = BeautifulSoup(Truncator(html_body).chars(500, html=True), 'html.parser')
+
+ tags_to_remove = [
+ "a", "link", # Link Tags
+ "img", "picture", "source", # Image Tags
+ "video", "track", # Video Tags
+ "audio", # Audio Tags
+ "embed", "object", "iframe", # Embedded Content
+ "script"
+ ]
+
+ # Remove the specified tags while keeping their content
+ for tag in tags_to_remove:
+ for match in html_body.find_all(tag):
+ match.unwrap()
+
+ return str(html_body)
diff --git a/lms/djangoapps/discussion/rest_api/tasks.py b/lms/djangoapps/discussion/rest_api/tasks.py
index 45bf41fe90..a87fafd4ca 100644
--- a/lms/djangoapps/discussion/rest_api/tasks.py
+++ b/lms/djangoapps/discussion/rest_api/tasks.py
@@ -33,7 +33,7 @@ def send_thread_created_notification(thread_id, course_key_str, user_id):
@shared_task
@set_code_owner_attribute
-def send_response_notifications(thread_id, course_key_str, user_id, parent_id=None):
+def send_response_notifications(thread_id, course_key_str, user_id, comment_id, parent_id=None):
"""
Send notifications to users who are subscribed to the thread.
"""
@@ -43,7 +43,7 @@ def send_response_notifications(thread_id, course_key_str, user_id, parent_id=No
thread = Thread(id=thread_id).retrieve()
user = User.objects.get(id=user_id)
course = get_course_with_access(user, 'load', course_key, check_if_enrolled=True)
- notification_sender = DiscussionNotificationSender(thread, course, user, parent_id)
+ notification_sender = DiscussionNotificationSender(thread, course, user, parent_id, comment_id)
notification_sender.send_new_comment_notification()
notification_sender.send_new_response_notification()
notification_sender.send_new_comment_on_response_notification()
diff --git a/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py b/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py
index b4d3f3d18a..f1a71fd123 100644
--- a/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py
+++ b/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py
@@ -1,13 +1,14 @@
"""
Unit tests for the DiscussionNotificationSender class
"""
-
+import re
import unittest
from unittest.mock import MagicMock, patch
import pytest
-from lms.djangoapps.discussion.rest_api.discussions_notifications import DiscussionNotificationSender
+from lms.djangoapps.discussion.rest_api.discussions_notifications import DiscussionNotificationSender, \
+ clean_thread_html_body
@patch('lms.djangoapps.discussion.rest_api.discussions_notifications.DiscussionNotificationSender'
@@ -88,3 +89,82 @@ class TestDiscussionNotificationSender(unittest.TestCase):
self.notification_sender.send_reported_content_notification()
self._assert_send_notification_called_with(mock_send_notification, 'thread')
+
+
+class TestCleanThreadHtmlBody(unittest.TestCase):
+ """
+ Tests for the clean_thread_html_body function
+ """
+
+ def test_html_tags_removal(self):
+ """
+ Test that the clean_thread_html_body function removes unwanted HTML tags
+ """
+ html_body = """
+ This is a link to a page.
+ Here is an image:
+ Embedded video:
+ Script test:
+ Some other content that should remain.
+ """
+ expected_output = ("This is a link to a page.
"
+ "Here is an image:
"
+ "Embedded video:
"
+ "Script test: alert('hello');
"
+ "Some other content that should remain.
")
+
+ result = clean_thread_html_body(html_body)
+
+ def normalize_html(text):
+ """
+ Normalize the output by removing extra whitespace, newlines, and spaces between tags
+ """
+ text = re.sub(r'\s+', ' ', text).strip() # Replace any sequence of whitespace with a single space
+ text = re.sub(r'>\s+<', '><', text) # Remove spaces between HTML tags
+ return text
+
+ normalized_result = normalize_html(result)
+ normalized_expected_output = normalize_html(expected_output)
+
+ self.assertEqual(normalized_result, normalized_expected_output)
+
+ def test_truncate_html_body(self):
+ """
+ Test that the clean_thread_html_body function truncates the HTML body to 500 characters
+ """
+ html_body = """
+ This is a long text that should be truncated to 500 characters.
+ """ * 20 # Repeat to exceed 500 characters
+
+ result = clean_thread_html_body(html_body)
+ self.assertGreaterEqual(500, len(result))
+
+ def test_no_tags_to_remove(self):
+ """
+ Test that the clean_thread_html_body function does not remove any tags if there are no unwanted tags
+ """
+ html_body = "This paragraph has no tags to remove.
"
+ expected_output = "This paragraph has no tags to remove.
"
+
+ result = clean_thread_html_body(html_body)
+ self.assertEqual(result, expected_output)
+
+ def test_empty_html_body(self):
+ """
+ Test that the clean_thread_html_body function returns an empty string if the input is an empty string
+ """
+ html_body = ""
+ expected_output = ""
+
+ result = clean_thread_html_body(html_body)
+ self.assertEqual(result, expected_output)
+
+ def test_only_script_tag(self):
+ """
+ Test that the clean_thread_html_body function removes the script tag and its content
+ """
+ html_body = ""
+ expected_output = "alert('Hello');"
+
+ result = clean_thread_html_body(html_body)
+ self.assertEqual(result.strip(), expected_output)
diff --git a/lms/djangoapps/discussion/rest_api/tests/test_tasks.py b/lms/djangoapps/discussion/rest_api/tests/test_tasks.py
index 28cfe3395c..8efd5cd49c 100644
--- a/lms/djangoapps/discussion/rest_api/tests/test_tasks.py
+++ b/lms/djangoapps/discussion/rest_api/tests/test_tasks.py
@@ -273,6 +273,17 @@ class TestSendResponseNotifications(DiscussionAPIViewTestMixin, ModuleStoreTestC
})
self._register_subscriptions_endpoint()
+ self.comment = ThreadMock(thread_id=4, creator=self.user_2, title='test comment', body='comment body')
+ self.register_get_comment_response(
+ {
+ 'id': self.comment.id,
+ 'thread_id': self.thread.id,
+ 'parent_id': None,
+ 'user_id': self.comment.user_id,
+ 'body': self.comment.body,
+ }
+ )
+
def test_basic(self):
"""
Left empty intentionally. This test case is inherited from DiscussionAPIViewTestMixin
@@ -292,7 +303,13 @@ class TestSendResponseNotifications(DiscussionAPIViewTestMixin, ModuleStoreTestC
# Post the form or do what it takes to send the signal
- send_response_notifications(self.thread.id, str(self.course.id), self.user_2.id, parent_id=None)
+ send_response_notifications(
+ self.thread.id,
+ str(self.course.id),
+ self.user_2.id,
+ self.comment.id,
+ parent_id=None
+ )
self.assertEqual(handler.call_count, 2)
args = handler.call_args_list[0][1]['notification_data']
self.assertEqual([int(user_id) for user_id in args.user_ids], [self.user_1.id])
@@ -300,6 +317,7 @@ class TestSendResponseNotifications(DiscussionAPIViewTestMixin, ModuleStoreTestC
expected_context = {
'replier_name': self.user_2.username,
'post_title': 'test thread',
+ 'email_content': self.comment.body,
'course_name': self.course.display_name,
'sender_id': self.user_2.id
}
@@ -325,7 +343,13 @@ class TestSendResponseNotifications(DiscussionAPIViewTestMixin, ModuleStoreTestC
'user_id': self.thread_2.user_id
})
- send_response_notifications(self.thread.id, str(self.course.id), self.user_3.id, parent_id=self.thread_2.id)
+ send_response_notifications(
+ self.thread.id,
+ str(self.course.id),
+ self.user_3.id,
+ self.comment.id,
+ parent_id=self.thread_2.id
+ )
# check if 2 call are made to the handler i.e. one for the response creator and one for the thread creator
self.assertEqual(handler.call_count, 2)
@@ -337,6 +361,7 @@ class TestSendResponseNotifications(DiscussionAPIViewTestMixin, ModuleStoreTestC
expected_context = {
'replier_name': self.user_3.username,
'post_title': self.thread.title,
+ 'email_content': self.comment.body,
'author_name': 'dummy\'s',
'author_pronoun': 'dummy\'s',
'course_name': self.course.display_name,
@@ -355,6 +380,7 @@ class TestSendResponseNotifications(DiscussionAPIViewTestMixin, ModuleStoreTestC
expected_context = {
'replier_name': self.user_3.username,
'post_title': self.thread.title,
+ 'email_content': self.comment.body,
'course_name': self.course.display_name,
'sender_id': self.user_3.id
}
@@ -372,7 +398,13 @@ class TestSendResponseNotifications(DiscussionAPIViewTestMixin, ModuleStoreTestC
"""
handler = mock.Mock()
USER_NOTIFICATION_REQUESTED.connect(handler)
- send_response_notifications(self.thread.id, str(self.course.id), self.user_1.id, parent_id=None)
+
+ send_response_notifications(
+ self.thread.id,
+ str(self.course.id),
+ self.user_1.id,
+ self.comment.id, parent_id=None
+ )
self.assertEqual(handler.call_count, 1)
def test_comment_creators_own_response(self):
@@ -389,7 +421,13 @@ class TestSendResponseNotifications(DiscussionAPIViewTestMixin, ModuleStoreTestC
'user_id': self.thread_3.user_id
})
- send_response_notifications(self.thread.id, str(self.course.id), self.user_3.id, parent_id=self.thread_2.id)
+ send_response_notifications(
+ self.thread.id,
+ str(self.course.id),
+ self.user_3.id,
+ parent_id=self.thread_2.id,
+ comment_id=self.comment.id
+ )
# check if 1 call is made to the handler i.e. for the thread creator
self.assertEqual(handler.call_count, 2)
@@ -404,6 +442,7 @@ class TestSendResponseNotifications(DiscussionAPIViewTestMixin, ModuleStoreTestC
'author_pronoun': 'your',
'course_name': self.course.display_name,
'sender_id': self.user_3.id,
+ 'email_content': self.comment.body
}
self.assertDictEqual(args_comment.context, expected_context)
self.assertEqual(
@@ -429,7 +468,13 @@ class TestSendResponseNotifications(DiscussionAPIViewTestMixin, ModuleStoreTestC
USER_NOTIFICATION_REQUESTED.connect(handler)
# Post the form or do what it takes to send the signal
- notification_sender = DiscussionNotificationSender(self.thread, self.course, self.user_2, parent_id=parent_id)
+ notification_sender = DiscussionNotificationSender(
+ self.thread,
+ self.course,
+ self.user_2,
+ parent_id=parent_id,
+ comment_id=self.comment.id
+ )
notification_sender.send_response_on_followed_post_notification()
self.assertEqual(handler.call_count, 1)
args = handler.call_args[1]['notification_data']
@@ -439,6 +484,7 @@ class TestSendResponseNotifications(DiscussionAPIViewTestMixin, ModuleStoreTestC
expected_context = {
'replier_name': self.user_2.username,
'post_title': 'test thread',
+ 'email_content': self.comment.body,
'course_name': self.course.display_name,
'sender_id': self.user_2.id,
}
@@ -516,6 +562,7 @@ class TestSendCommentNotification(DiscussionAPIViewTestMixin, ModuleStoreTestCas
thread = ThreadMock(thread_id=1, creator=self.user_1, title='test thread')
response = ThreadMock(thread_id=2, creator=self.user_2, title='test response')
+ comment = ThreadMock(thread_id=3, creator=self.user_2, title='test comment', body='comment body')
self.register_get_thread_response({
'id': thread.id,
'course_id': str(self.course.id),
@@ -530,12 +577,20 @@ class TestSendCommentNotification(DiscussionAPIViewTestMixin, ModuleStoreTestCas
'thread_id': thread.id,
'user_id': response.user_id
})
+ self.register_get_comment_response({
+ 'id': comment.id,
+ 'parent_id': response.id,
+ 'user_id': comment.user_id,
+ 'body': comment.body
+ })
self.register_get_subscriptions(1, {})
- send_response_notifications(thread.id, str(self.course.id), self.user_2.id, parent_id=response.id)
+ send_response_notifications(thread.id, str(self.course.id), self.user_2.id, parent_id=response.id,
+ comment_id=comment.id)
handler.assert_called_once()
context = handler.call_args[1]['notification_data'].context
self.assertEqual(context['author_name'], 'dummy\'s')
self.assertEqual(context['author_pronoun'], 'their')
+ self.assertEqual(context['email_content'], comment.body)
@override_waffle_flag(ENABLE_NOTIFICATIONS, active=True)
diff --git a/lms/djangoapps/discussion/rest_api/tests/utils.py b/lms/djangoapps/discussion/rest_api/tests/utils.py
index 989fd63855..27e34705f5 100644
--- a/lms/djangoapps/discussion/rest_api/tests/utils.py
+++ b/lms/djangoapps/discussion/rest_api/tests/utils.py
@@ -675,12 +675,13 @@ class ThreadMock(object):
A mock thread object
"""
- def __init__(self, thread_id, creator, title, parent_id=None):
+ def __init__(self, thread_id, creator, title, parent_id=None, body=''):
self.id = thread_id
self.user_id = str(creator.id)
self.username = creator.username
self.title = title
self.parent_id = parent_id
+ self.body = body
def url_with_id(self, params):
return f"http://example.com/{params['id']}"
diff --git a/lms/djangoapps/discussion/signals/handlers.py b/lms/djangoapps/discussion/signals/handlers.py
index 35288cdbd9..2aa7d36456 100644
--- a/lms/djangoapps/discussion/signals/handlers.py
+++ b/lms/djangoapps/discussion/signals/handlers.py
@@ -176,8 +176,9 @@ def create_comment_created_notification(*args, **kwargs):
comment = kwargs['post']
thread_id = comment.attributes['thread_id']
parent_id = comment.attributes['parent_id']
+ comment_id = comment.attributes['id']
course_key_str = comment.attributes['course_id']
- send_response_notifications.apply_async(args=[thread_id, course_key_str, user.id, parent_id])
+ send_response_notifications.apply_async(args=[thread_id, course_key_str, user.id, comment_id, parent_id])
@receiver(signals.comment_endorsed)
From 353dc34d9c0f23f3e3e5ff0d024b5b3de0ccf44d Mon Sep 17 00:00:00 2001
From: Mudassir Hafeez
Date: Mon, 26 Aug 2024 15:53:27 +0500
Subject: [PATCH 072/242] chore: provide logo url from backend for batch
enrollment email (#35138)
* chore: provide logo url from backend for batch enrollment email
---
lms/djangoapps/instructor/enrollment.py | 2 ++
.../instructor/tests/test_enrollment.py | 16 ++++++++++++++++
.../ace_common/edx_ace/common/base_body.html | 2 +-
3 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/lms/djangoapps/instructor/enrollment.py b/lms/djangoapps/instructor/enrollment.py
index 300543def6..896d0deadc 100644
--- a/lms/djangoapps/instructor/enrollment.py
+++ b/lms/djangoapps/instructor/enrollment.py
@@ -34,6 +34,7 @@ from common.djangoapps.track.event_transaction_utils import (
get_event_transaction_id,
set_event_transaction_type
)
+from lms.djangoapps.branding.api import get_logo_url_for_email
from lms.djangoapps.courseware.models import StudentModule
from lms.djangoapps.grades.api import constants as grades_constants
from lms.djangoapps.grades.api import disconnect_submissions_signal_receiver
@@ -489,6 +490,7 @@ def get_email_params(course, auto_enroll, secure=True, course_key=None, display_
'contact_mailing_address': contact_mailing_address,
'platform_name': platform_name,
'site_configuration_values': configuration_helpers.get_current_site_configuration_values(),
+ 'logo_url': get_logo_url_for_email(),
}
return email_params
diff --git a/lms/djangoapps/instructor/tests/test_enrollment.py b/lms/djangoapps/instructor/tests/test_enrollment.py
index 59ccfac6ca..741f57ef6d 100644
--- a/lms/djangoapps/instructor/tests/test_enrollment.py
+++ b/lms/djangoapps/instructor/tests/test_enrollment.py
@@ -23,6 +23,7 @@ from xmodule.capa.tests.response_xml_factory import MultipleChoiceResponseXMLFac
from common.djangoapps.student.models import CourseEnrollment, CourseEnrollmentAllowed, anonymous_id_for_user
from common.djangoapps.student.roles import CourseCcxCoachRole
from common.djangoapps.student.tests.factories import AdminFactory, UserFactory
+from lms.djangoapps.branding.api import get_logo_url_for_email
from lms.djangoapps.ccx.tests.factories import CcxFactory
from lms.djangoapps.course_blocks.api import get_course_blocks
from lms.djangoapps.courseware.models import StudentModule
@@ -940,6 +941,7 @@ class TestGetEmailParams(SharedModuleStoreTestCase):
)
cls.course_about_url = cls.course_url + 'about'
cls.registration_url = f'https://{site}/register'
+ cls.logo_url = get_logo_url_for_email()
def test_normal_params(self):
# For a normal site, what do we expect to get for the URLs?
@@ -950,6 +952,7 @@ class TestGetEmailParams(SharedModuleStoreTestCase):
assert result['course_about_url'] == self.course_about_url
assert result['registration_url'] == self.registration_url
assert result['course_url'] == self.course_url
+ assert result['logo_url'] == self.logo_url
def test_marketing_params(self):
# For a site with a marketing front end, what do we expect to get for the URLs?
@@ -962,6 +965,19 @@ class TestGetEmailParams(SharedModuleStoreTestCase):
assert result['course_about_url'] is None
assert result['registration_url'] == self.registration_url
assert result['course_url'] == self.course_url
+ assert result['logo_url'] == self.logo_url
+
+ @patch('lms.djangoapps.instructor.enrollment.get_logo_url_for_email', return_value='https://www.logo.png')
+ def test_logo_url_params(self, mock_get_logo_url_for_email):
+ # Verify that the logo_url is correctly set in the email params
+ result = get_email_params(self.course, False)
+
+ assert result['auto_enroll'] is False
+ assert result['course_about_url'] == self.course_about_url
+ assert result['registration_url'] == self.registration_url
+ assert result['course_url'] == self.course_url
+ mock_get_logo_url_for_email.assert_called_once()
+ assert result['logo_url'] == 'https://www.logo.png'
@ddt.ddt
diff --git a/themes/red-theme/lms/templates/ace_common/edx_ace/common/base_body.html b/themes/red-theme/lms/templates/ace_common/edx_ace/common/base_body.html
index 8d51b16498..9319217aa4 100644
--- a/themes/red-theme/lms/templates/ace_common/edx_ace/common/base_body.html
+++ b/themes/red-theme/lms/templates/ace_common/edx_ace/common/base_body.html
@@ -63,7 +63,7 @@
From 0f177e466639bc8e0dc4acece00a7e82e7ae6001 Mon Sep 17 00:00:00 2001
From: Deborah Kaplan
Date: Mon, 26 Aug 2024 10:20:03 -0400
Subject: [PATCH 073/242] feat: linting only (#35370)
Editing this file is going to cause a lot of automatic linting, so lint-only commit for nicer code review.
---
.../djangoapps/user_api/accounts/views.py | 649 +++++++++---------
1 file changed, 316 insertions(+), 333 deletions(-)
diff --git a/openedx/core/djangoapps/user_api/accounts/views.py b/openedx/core/djangoapps/user_api/accounts/views.py
index 720b3ba96a..103c5bf24f 100644
--- a/openedx/core/djangoapps/user_api/accounts/views.py
+++ b/openedx/core/djangoapps/user_api/accounts/views.py
@@ -21,7 +21,6 @@ from django.utils.translation import gettext as _
from edx_ace import ace
from edx_ace.recipient import Recipient
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
-from openedx.core.lib.api.authentication import BearerAuthentication
from edx_rest_framework_extensions.auth.session.authentication import SessionAuthenticationAllowInactiveUser
from enterprise.models import EnterpriseCourseEnrollment, EnterpriseCustomerUser, PendingEnterpriseCustomerUser
from integrated_channels.degreed.models import DegreedLearnerDataTransmissionAudit
@@ -50,9 +49,10 @@ from common.djangoapps.student.models import ( # lint-amnesty, pylint: disable=
get_retired_email_by_email,
get_retired_username_by_username,
is_email_retired,
- is_username_retired
+ is_username_retired,
)
from common.djangoapps.student.models_api import confirm_name_change, do_name_change_request, get_pending_name_change
+from lms.djangoapps.certificates.api import clear_pii_from_certificate_records_for_user
from openedx.core.djangoapps.ace_common.template_context import get_base_template_context
from openedx.core.djangoapps.api_admin.models import ApiAccessRequest
from openedx.core.djangoapps.course_groups.models import UnregisteredLearnerCohortAssignments
@@ -64,9 +64,8 @@ from openedx.core.djangoapps.user_api import accounts
from openedx.core.djangoapps.user_api.accounts.image_helpers import get_profile_image_names, set_has_profile_image
from openedx.core.djangoapps.user_api.accounts.utils import handle_retirement_cancellation
from openedx.core.djangoapps.user_authn.exceptions import AuthFailedError
-from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser
+from openedx.core.lib.api.authentication import BearerAuthentication, BearerAuthenticationAllowInactiveUser
from openedx.core.lib.api.parsers import MergePatchParser
-from lms.djangoapps.certificates.api import clear_pii_from_certificate_records_for_user
from ..errors import AccountUpdateError, AccountValidationError, UserNotAuthorized, UserNotFound
from ..message_types import DeletionNotificationMessage
@@ -75,7 +74,7 @@ from ..models import (
RetirementStateError,
UserOrgTag,
UserRetirementPartnerReportingStatus,
- UserRetirementStatus
+ UserRetirementStatus,
)
from .api import get_account_settings, update_account_settings
from .permissions import (
@@ -83,13 +82,13 @@ from .permissions import (
CanDeactivateUser,
CanGetAccountInfo,
CanReplaceUsername,
- CanRetireUser
+ CanRetireUser,
)
from .serializers import (
PendingNameChangeSerializer,
UserRetirementPartnerReportSerializer,
UserRetirementStatusSerializer,
- UserSearchEmailSerializer
+ UserSearchEmailSerializer,
)
from .signals import USER_RETIRE_LMS_CRITICAL, USER_RETIRE_LMS_MISC, USER_RETIRE_MAILINGS
from .utils import create_retirement_request_and_deactivate_account, username_suffix_generator
@@ -97,16 +96,16 @@ from .utils import create_retirement_request_and_deactivate_account, username_su
log = logging.getLogger(__name__)
USER_PROFILE_PII = {
- 'name': '',
- 'meta': '',
- 'location': '',
- 'year_of_birth': None,
- 'gender': None,
- 'mailing_address': None,
- 'city': None,
- 'country': None,
- 'bio': None,
- 'phone_number': None,
+ "name": "",
+ "meta": "",
+ "location": "",
+ "year_of_birth": None,
+ "gender": None,
+ "mailing_address": None,
+ "city": None,
+ "country": None,
+ "bio": None,
+ "phone_number": None,
}
@@ -118,12 +117,9 @@ def request_requires_username(function):
@wraps(function)
def wrapper(self, request): # pylint: disable=missing-docstring
- username = request.data.get('username', None)
+ username = request.data.get("username", None)
if not username:
- return Response(
- status=status.HTTP_404_NOT_FOUND,
- data={'message': 'The user was not specified.'}
- )
+ return Response(status=status.HTTP_404_NOT_FOUND, data={"message": "The user was not specified."})
return function(self, request)
return wrapper
@@ -131,177 +127,183 @@ def request_requires_username(function):
class AccountViewSet(ViewSet):
"""
- **Use Cases**
+ **Use Cases**
- Get or update a user's account information. Updates are supported
- only through merge patch.
+ Get or update a user's account information. Updates are supported
+ only through merge patch.
- **Example Requests**
+ **Example Requests**
- GET /api/user/v1/me[?view=shared]
- GET /api/user/v1/accounts?usernames={username1,username2}[?view=shared]
- GET /api/user/v1/accounts?email={user_email}
- GET /api/user/v1/accounts/{username}/[?view=shared]
+ GET /api/user/v1/me[?view=shared]
+ GET /api/user/v1/accounts?usernames={username1,username2}[?view=shared]
+ GET /api/user/v1/accounts?email={user_email}
+ GET /api/user/v1/accounts/{username}/[?view=shared]
- PATCH /api/user/v1/accounts/{username}/{"key":"value"} "application/merge-patch+json"
+ PATCH /api/user/v1/accounts/{username}/{"key":"value"} "application/merge-patch+json"
- POST /api/user/v1/accounts/search_emails "application/json"
+ POST /api/user/v1/accounts/search_emails "application/json"
- **Notes for PATCH requests to /accounts endpoints**
- * Requested updates to social_links are automatically merged with
- previously set links. That is, any newly introduced platforms are
- add to the previous list. Updated links to pre-existing platforms
- replace their values in the previous list. Pre-existing platforms
- can be removed by setting the value of the social_link to an
- empty string ("").
+ **Notes for PATCH requests to /accounts endpoints**
+ * Requested updates to social_links are automatically merged with
+ previously set links. That is, any newly introduced platforms are
+ add to the previous list. Updated links to pre-existing platforms
+ replace their values in the previous list. Pre-existing platforms
+ can be removed by setting the value of the social_link to an
+ empty string ("").
- **Response Values for GET requests to the /me endpoint**
- If the user is not logged in, an HTTP 401 "Not Authorized" response
- is returned.
+ **Response Values for GET requests to the /me endpoint**
+ If the user is not logged in, an HTTP 401 "Not Authorized" response
+ is returned.
- Otherwise, an HTTP 200 "OK" response is returned. The response
- contains the following value:
+ Otherwise, an HTTP 200 "OK" response is returned. The response
+ contains the following value:
- * username: The username associated with the account.
+ * username: The username associated with the account.
- **Response Values for GET requests to /accounts endpoints**
+ **Response Values for GET requests to /accounts endpoints**
- If no user exists with the specified username, or email, an HTTP 404 "Not
- Found" response is returned.
+ If no user exists with the specified username, or email, an HTTP 404 "Not
+ Found" response is returned.
- If the user makes the request for her own account, or makes a
- request for another account and has "is_staff" access, an HTTP 200
- "OK" response is returned. The response contains the following
- values.
+ If the user makes the request for her own account, or makes a
+ request for another account and has "is_staff" access, an HTTP 200
+ "OK" response is returned. The response contains the following
+ values.
- * id: numerical lms user id in db
- * activation_key: auto-genrated activation key when signed up via email
- * bio: null or textual representation of user biographical
- information ("about me").
- * country: An ISO 3166 country code or null.
- * date_joined: The date the account was created, in the string
- format provided by datetime. For example, "2014-08-26T17:52:11Z".
- * last_login: The latest date the user logged in, in the string datetime format.
- * email: Email address for the user. New email addresses must be confirmed
- via a confirmation email, so GET does not reflect the change until
- the address has been confirmed.
- * secondary_email: A secondary email address for the user. Unlike
- the email field, GET will reflect the latest update to this field
- even if changes have yet to be confirmed.
- * verified_name: Approved verified name of the learner present in name affirmation plugin
- * gender: One of the following values:
+ * id: numerical lms user id in db
+ * activation_key: auto-genrated activation key when signed up via email
+ * bio: null or textual representation of user biographical
+ information ("about me").
+ * country: An ISO 3166 country code or null.
+ * date_joined: The date the account was created, in the string
+ format provided by datetime. For example, "2014-08-26T17:52:11Z".
+ * last_login: The latest date the user logged in, in the string datetime format.
+ * email: Email address for the user. New email addresses must be confirmed
+ via a confirmation email, so GET does not reflect the change until
+ the address has been confirmed.
+ * secondary_email: A secondary email address for the user. Unlike
+ the email field, GET will reflect the latest update to this field
+ even if changes have yet to be confirmed.
+ * verified_name: Approved verified name of the learner present in name affirmation plugin
+ * gender: One of the following values:
- * null
- * "f"
- * "m"
- * "o"
+ * null
+ * "f"
+ * "m"
+ * "o"
- * goals: The textual representation of the user's goals, or null.
- * is_active: Boolean representation of whether a user is active.
- * language: The user's preferred language, or null.
- * language_proficiencies: Array of language preferences. Each
- preference is a JSON object with the following keys:
+ * goals: The textual representation of the user's goals, or null.
+ * is_active: Boolean representation of whether a user is active.
+ * language: The user's preferred language, or null.
+ * language_proficiencies: Array of language preferences. Each
+ preference is a JSON object with the following keys:
- * "code": string ISO 639-1 language code e.g. "en".
+ * "code": string ISO 639-1 language code e.g. "en".
- * level_of_education: One of the following values:
+ * level_of_education: One of the following values:
- * "p": PhD or Doctorate
- * "m": Master's or professional degree
- * "b": Bachelor's degree
- * "a": Associate's degree
- * "hs": Secondary/high school
- * "jhs": Junior secondary/junior high/middle school
- * "el": Elementary/primary school
- * "none": None
- * "o": Other
- * null: The user did not enter a value
+ * "p": PhD or Doctorate
+ * "m": Master's or professional degree
+ * "b": Bachelor's degree
+ * "a": Associate's degree
+ * "hs": Secondary/high school
+ * "jhs": Junior secondary/junior high/middle school
+ * "el": Elementary/primary school
+ * "none": None
+ * "o": Other
+ * null: The user did not enter a value
- * mailing_address: The textual representation of the user's mailing
- address, or null.
- * name: The full name of the user.
- * profile_image: A JSON representation of a user's profile image
- information. This representation has the following keys.
+ * mailing_address: The textual representation of the user's mailing
+ address, or null.
+ * name: The full name of the user.
+ * profile_image: A JSON representation of a user's profile image
+ information. This representation has the following keys.
- * "has_image": Boolean indicating whether the user has a profile
- image.
- * "image_url_*": Absolute URL to various sizes of a user's
- profile image, where '*' matches a representation of the
- corresponding image size, such as 'small', 'medium', 'large',
- and 'full'. These are configurable via PROFILE_IMAGE_SIZES_MAP.
+ * "has_image": Boolean indicating whether the user has a profile
+ image.
+ * "image_url_*": Absolute URL to various sizes of a user's
+ profile image, where '*' matches a representation of the
+ corresponding image size, such as 'small', 'medium', 'large',
+ and 'full'. These are configurable via PROFILE_IMAGE_SIZES_MAP.
- * requires_parental_consent: True if the user is a minor
- requiring parental consent.
- * social_links: Array of social links, sorted alphabetically by
- "platform". Each preference is a JSON object with the following keys:
+ * requires_parental_consent: True if the user is a minor
+ requiring parental consent.
+ * social_links: Array of social links, sorted alphabetically by
+ "platform". Each preference is a JSON object with the following keys:
- * "platform": A particular social platform, ex: 'facebook'
- * "social_link": The link to the user's profile on the particular platform
+ * "platform": A particular social platform, ex: 'facebook'
+ * "social_link": The link to the user's profile on the particular platform
- * username: The username associated with the account.
- * year_of_birth: The year the user was born, as an integer, or null.
+ * username: The username associated with the account.
+ * year_of_birth: The year the user was born, as an integer, or null.
- * account_privacy: The user's setting for sharing her personal
- profile. Possible values are "all_users", "private", or "custom".
- If "custom", the user has selectively chosen a subset of shareable
- fields to make visible to others via the User Preferences API.
+ * account_privacy: The user's setting for sharing her personal
+ profile. Possible values are "all_users", "private", or "custom".
+ If "custom", the user has selectively chosen a subset of shareable
+ fields to make visible to others via the User Preferences API.
- * phone_number: The phone number for the user. String of numbers with
- an optional `+` sign at the start.
+ * phone_number: The phone number for the user. String of numbers with
+ an optional `+` sign at the start.
- * pending_name_change: If the user has an active name change request, returns the
- requested name.
+ * pending_name_change: If the user has an active name change request, returns the
+ requested name.
- For all text fields, plain text instead of HTML is supported. The
- data is stored exactly as specified. Clients must HTML escape
- rendered values to avoid script injections.
+ For all text fields, plain text instead of HTML is supported. The
+ data is stored exactly as specified. Clients must HTML escape
+ rendered values to avoid script injections.
- If a user who does not have "is_staff" access requests account
- information for a different user, only a subset of these fields is
- returned. The returned fields depend on the
- ACCOUNT_VISIBILITY_CONFIGURATION configuration setting and the
- visibility preference of the user for whom data is requested.
+ If a user who does not have "is_staff" access requests account
+ information for a different user, only a subset of these fields is
+ returned. The returned fields depend on the
+ ACCOUNT_VISIBILITY_CONFIGURATION configuration setting and the
+ visibility preference of the user for whom data is requested.
- Note that a user can view which account fields they have shared
- with other users by requesting their own username and providing
- the "view=shared" URL parameter.
+ Note that a user can view which account fields they have shared
+ with other users by requesting their own username and providing
+ the "view=shared" URL parameter.
- **Response Values for PATCH**
+ **Response Values for PATCH**
- Users can only modify their own account information. If the
- requesting user does not have the specified username and has staff
- access, the request returns an HTTP 403 "Forbidden" response. If
- the requesting user does not have staff access, the request
- returns an HTTP 404 "Not Found" response to avoid revealing the
- existence of the account.
+ Users can only modify their own account information. If the
+ requesting user does not have the specified username and has staff
+ access, the request returns an HTTP 403 "Forbidden" response. If
+ the requesting user does not have staff access, the request
+ returns an HTTP 404 "Not Found" response to avoid revealing the
+ existence of the account.
- If no user exists with the specified username, an HTTP 404 "Not
- Found" response is returned.
+ If no user exists with the specified username, an HTTP 404 "Not
+ Found" response is returned.
- If "application/merge-patch+json" is not the specified content
- type, a 415 "Unsupported Media Type" response is returned.
+ If "application/merge-patch+json" is not the specified content
+ type, a 415 "Unsupported Media Type" response is returned.
- If validation errors prevent the update, this method returns a 400
- "Bad Request" response that includes a "field_errors" field that
- lists all error messages.
+ If validation errors prevent the update, this method returns a 400
+ "Bad Request" response that includes a "field_errors" field that
+ lists all error messages.
- If a failure at the time of the update prevents the update, a 400
- "Bad Request" error is returned. The JSON collection contains
- specific errors.
+ If a failure at the time of the update prevents the update, a 400
+ "Bad Request" error is returned. The JSON collection contains
+ specific errors.
- If the update is successful, updated user account data is returned.
+ If the update is successful, updated user account data is returned.
"""
+
authentication_classes = (
- JwtAuthentication, BearerAuthenticationAllowInactiveUser, SessionAuthenticationAllowInactiveUser
+ JwtAuthentication,
+ BearerAuthenticationAllowInactiveUser,
+ SessionAuthenticationAllowInactiveUser,
)
permission_classes = (permissions.IsAuthenticated, CanGetAccountInfo)
- parser_classes = (JSONParser, MergePatchParser,)
+ parser_classes = (
+ JSONParser,
+ MergePatchParser,
+ )
def get(self, request):
"""
GET /api/user/v1/me
"""
- return Response({'username': request.user.username})
+ return Response({"username": request.user.username})
def list(self, request):
"""
@@ -309,13 +311,13 @@ class AccountViewSet(ViewSet):
GET /api/user/v1/accounts?email={user_email} (Staff Only)
GET /api/user/v1/accounts?lms_user_id={lms_user_id} (Staff Only)
"""
- usernames = request.GET.get('username')
- user_email = request.GET.get('email')
- lms_user_id = request.GET.get('lms_user_id')
+ usernames = request.GET.get("username")
+ user_email = request.GET.get("email")
+ lms_user_id = request.GET.get("lms_user_id")
search_usernames = []
if usernames:
- search_usernames = usernames.strip(',').split(',')
+ search_usernames = usernames.strip(",").split(",")
elif user_email:
if is_email_retired(user_email):
can_cancel_retirement = True
@@ -325,22 +327,20 @@ class AccountViewSet(ViewSet):
retirement_status = UserRetirementStatus.objects.get(
created__gt=earliest_datetime,
created__lt=datetime.datetime.now(pytz.UTC),
- original_email=user_email
+ original_email=user_email,
)
retirement_id = retirement_status.id
except UserRetirementStatus.DoesNotExist:
can_cancel_retirement = False
context = {
- 'error_msg': accounts.RETIRED_EMAIL_MSG,
- 'can_cancel_retirement': can_cancel_retirement,
- 'retirement_id': retirement_id
+ "error_msg": accounts.RETIRED_EMAIL_MSG,
+ "can_cancel_retirement": can_cancel_retirement,
+ "retirement_id": retirement_id,
}
- return Response(
- context, status=status.HTTP_404_NOT_FOUND
- )
- user_email = user_email.strip('')
+ return Response(context, status=status.HTTP_404_NOT_FOUND)
+ user_email = user_email.strip("")
try:
user = User.objects.get(email=user_email)
except (UserNotFound, User.DoesNotExist):
@@ -355,9 +355,7 @@ class AccountViewSet(ViewSet):
return Response(status=status.HTTP_400_BAD_REQUEST)
search_usernames = [user.username]
try:
- account_settings = get_account_settings(
- request, search_usernames, view=request.query_params.get('view')
- )
+ account_settings = get_account_settings(request, search_usernames, view=request.query_params.get("view"))
except UserNotFound:
return Response(status=status.HTTP_404_NOT_FOUND)
@@ -386,23 +384,15 @@ class AccountViewSet(ViewSet):
"""
if not request.user.is_staff:
return Response(
- {
- 'developer_message': 'not_found',
- 'user_message': 'Not Found'
- },
- status=status.HTTP_404_NOT_FOUND
+ {"developer_message": "not_found", "user_message": "Not Found"}, status=status.HTTP_404_NOT_FOUND
)
try:
- user_emails = request.data['emails']
+ user_emails = request.data["emails"]
except KeyError as error:
- error_message = f'{error} field is required'
+ error_message = f"{error} field is required"
return Response(
- {
- 'developer_message': error_message,
- 'user_message': error_message
- },
- status=status.HTTP_400_BAD_REQUEST
+ {"developer_message": error_message, "user_message": error_message}, status=status.HTTP_400_BAD_REQUEST
)
users = User.objects.filter(email__in=user_emails)
data = UserSearchEmailSerializer(users, many=True).data
@@ -413,8 +403,7 @@ class AccountViewSet(ViewSet):
GET /api/user/v1/accounts/{username}/
"""
try:
- account_settings = get_account_settings(
- request, [username], view=request.query_params.get('view'))
+ account_settings = get_account_settings(request, [username], view=request.query_params.get("view"))
except UserNotFound:
return Response(status=status.HTTP_404_NOT_FOUND)
@@ -443,11 +432,8 @@ class AccountViewSet(ViewSet):
return Response({"field_errors": err.field_errors}, status=status.HTTP_400_BAD_REQUEST)
except AccountUpdateError as err:
return Response(
- {
- "developer_message": err.developer_message,
- "user_message": err.user_message
- },
- status=status.HTTP_400_BAD_REQUEST
+ {"developer_message": err.developer_message, "user_message": err.user_message},
+ status=status.HTTP_400_BAD_REQUEST,
)
return Response(account_settings)
@@ -457,6 +443,7 @@ class NameChangeView(ViewSet):
"""
Viewset to manage profile name change requests.
"""
+
permission_classes = (permissions.IsAuthenticated,)
def create(self, request):
@@ -472,10 +459,10 @@ class NameChangeView(ViewSet):
}
"""
user = request.user
- new_name = request.data.get('name', None)
- rationale = f'Name change requested through account API by {user.username}'
+ new_name = request.data.get("name", None)
+ rationale = f"Name change requested through account API by {user.username}"
- serializer = PendingNameChangeSerializer(data={'new_name': new_name})
+ serializer = PendingNameChangeSerializer(data={"new_name": new_name})
if serializer.is_valid():
pending_name_change = do_name_change_request(user, new_name, rationale)[0]
@@ -483,8 +470,8 @@ class NameChangeView(ViewSet):
return Response(status=status.HTTP_201_CREATED)
else:
return Response(
- {'new_name': 'The profile name given was identical to the current name.'},
- status=status.HTTP_400_BAD_REQUEST
+ {"new_name": "The profile name given was identical to the current name."},
+ status=status.HTTP_400_BAD_REQUEST,
)
return Response(status=status.HTTP_400_BAD_REQUEST, data=serializer.errors)
@@ -514,6 +501,7 @@ class AccountDeactivationView(APIView):
Account deactivation viewset. Currently only supports POST requests.
Only admins can deactivate accounts.
"""
+
permission_classes = (permissions.IsAuthenticated, CanDeactivateUser)
def post(self, request, username):
@@ -559,6 +547,7 @@ class DeactivateLogoutView(APIView):
- Log the user out
- Create a row in the retirement table for that user
"""
+
# BearerAuthentication is added here to support account deletion
# from the mobile app until it moves to JWT Auth.
# See mobile roadmap issue https://github.com/openedx/edx-platform/issues/33307.
@@ -575,7 +564,7 @@ class DeactivateLogoutView(APIView):
# Ensure the account deletion is not disable
enable_account_deletion = configuration_helpers.get_value(
- 'ENABLE_ACCOUNT_DELETION', settings.FEATURES.get('ENABLE_ACCOUNT_DELETION', False)
+ "ENABLE_ACCOUNT_DELETION", settings.FEATURES.get("ENABLE_ACCOUNT_DELETION", False)
)
if not enable_account_deletion:
@@ -595,11 +584,9 @@ class DeactivateLogoutView(APIView):
# Send notification email to user
site = Site.objects.get_current()
notification_context = get_base_template_context(site)
- notification_context.update({'full_name': request.user.profile.name})
+ notification_context.update({"full_name": request.user.profile.name})
language_code = request.user.preferences.model.get_value(
- request.user,
- LANGUAGE_KEY,
- default=settings.LANGUAGE_CODE
+ request.user, LANGUAGE_KEY, default=settings.LANGUAGE_CODE
)
notification = DeletionNotificationMessage().personalize(
recipient=Recipient(lms_user_id=0, email_address=user_email),
@@ -608,22 +595,20 @@ class DeactivateLogoutView(APIView):
)
ace.send(notification)
except Exception as exc:
- log.exception('Error sending out deletion notification email')
+ log.exception("Error sending out deletion notification email")
raise exc
# Log the user out.
logout(request)
return Response(status=status.HTTP_204_NO_CONTENT)
except KeyError:
- log.exception(f'Username not specified {request.user}')
- return Response('Username not specified.', status=status.HTTP_404_NOT_FOUND)
+ log.exception(f"Username not specified {request.user}")
+ return Response("Username not specified.", status=status.HTTP_404_NOT_FOUND)
except user_model.DoesNotExist:
log.exception(f'The user "{request.user.username}" does not exist.')
- return Response(
- f'The user "{request.user.username}" does not exist.', status=status.HTTP_404_NOT_FOUND
- )
+ return Response(f'The user "{request.user.username}" does not exist.', status=status.HTTP_404_NOT_FOUND)
except Exception as exc: # pylint: disable=broad-except
- log.exception(f'500 error deactivating account {exc}')
+ log.exception(f"500 error deactivating account {exc}")
return Response(str(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR)
def _verify_user_password(self, request):
@@ -636,7 +621,7 @@ class DeactivateLogoutView(APIView):
"""
try:
self._check_excessive_login_attempts(request.user)
- user = authenticate(username=request.user.username, password=request.POST['password'], request=request)
+ user = authenticate(username=request.user.username, password=request.POST["password"], request=request)
if user:
if LoginFailures.is_feature_enabled():
LoginFailures.clear_lockout_counter(user)
@@ -644,9 +629,7 @@ class DeactivateLogoutView(APIView):
else:
self._handle_failed_authentication(request.user)
except AuthFailedError as err:
- log.exception(
- f"The user password to deactivate was incorrect. {request.user.username}"
- )
+ log.exception(f"The user password to deactivate was incorrect. {request.user.username}")
return Response(str(err), status=status.HTTP_403_FORBIDDEN)
except Exception as err: # pylint: disable=broad-except
return Response(f"Could not verify user password: {err}", status=status.HTTP_400_BAD_REQUEST)
@@ -657,8 +640,9 @@ class DeactivateLogoutView(APIView):
"""
if user and LoginFailures.is_feature_enabled():
if LoginFailures.is_user_locked_out(user):
- raise AuthFailedError(_('This account has been temporarily locked due '
- 'to excessive login failures. Try again later.'))
+ raise AuthFailedError(
+ _("This account has been temporarily locked due to excessive login failures. Try again later.")
+ )
def _handle_failed_authentication(self, user):
"""
@@ -667,7 +651,7 @@ class DeactivateLogoutView(APIView):
if user and LoginFailures.is_feature_enabled():
LoginFailures.increment_lockout_counter(user)
- raise AuthFailedError(_('Email or password is incorrect.'))
+ raise AuthFailedError(_("Email or password is incorrect."))
def _set_unusable_password(user):
@@ -684,15 +668,19 @@ class AccountRetirementPartnerReportView(ViewSet):
Provides API endpoints for managing partner reporting of retired
users.
"""
- DELETION_COMPLETED_KEY = 'deletion_completed'
- ORGS_CONFIG_KEY = 'orgs_config'
- ORGS_CONFIG_ORG_KEY = 'org'
- ORGS_CONFIG_FIELD_HEADINGS_KEY = 'field_headings'
- ORIGINAL_EMAIL_KEY = 'original_email'
- ORIGINAL_NAME_KEY = 'original_name'
- STUDENT_ID_KEY = 'student_id'
- permission_classes = (permissions.IsAuthenticated, CanRetireUser,)
+ DELETION_COMPLETED_KEY = "deletion_completed"
+ ORGS_CONFIG_KEY = "orgs_config"
+ ORGS_CONFIG_ORG_KEY = "org"
+ ORGS_CONFIG_FIELD_HEADINGS_KEY = "field_headings"
+ ORIGINAL_EMAIL_KEY = "original_email"
+ ORIGINAL_NAME_KEY = "original_name"
+ STUDENT_ID_KEY = "student_id"
+
+ permission_classes = (
+ permissions.IsAuthenticated,
+ CanRetireUser,
+ )
parser_classes = (JSONParser,)
serializer_class = UserRetirementStatusSerializer
@@ -706,7 +694,7 @@ class AccountRetirementPartnerReportView(ViewSet):
org = enrollment.course_id.org
# Org can conceivably be blank or this bogus default value
- if org and org != 'outdated_entry':
+ if org and org != "outdated_entry":
orgs.add(org)
return orgs
@@ -718,9 +706,9 @@ class AccountRetirementPartnerReportView(ViewSet):
that are not already being processed and updates their status
to indicate they are currently being processed.
"""
- retirement_statuses = UserRetirementPartnerReportingStatus.objects.filter(
- is_being_processed=False
- ).order_by('id')
+ retirement_statuses = UserRetirementPartnerReportingStatus.objects.filter(is_being_processed=False).order_by(
+ "id"
+ )
retirements = []
for retirement_status in retirement_statuses:
@@ -737,12 +725,12 @@ class AccountRetirementPartnerReportView(ViewSet):
Get the retirement for this retirement_status. The retirement info will be included in the partner report.
"""
retirement = {
- 'user_id': retirement_status.user.pk,
- 'original_username': retirement_status.original_username,
+ "user_id": retirement_status.user.pk,
+ "original_username": retirement_status.original_username,
AccountRetirementPartnerReportView.ORIGINAL_EMAIL_KEY: retirement_status.original_email,
AccountRetirementPartnerReportView.ORIGINAL_NAME_KEY: retirement_status.original_name,
- 'orgs': self._get_orgs_for_user(retirement_status.user),
- 'created': retirement_status.created,
+ "orgs": self._get_orgs_for_user(retirement_status.user),
+ "created": retirement_status.created,
}
return retirement
@@ -761,7 +749,7 @@ class AccountRetirementPartnerReportView(ViewSet):
Creates a UserRetirementPartnerReportingStatus object for the given user
as part of the retirement pipeline.
"""
- username = request.data['username']
+ username = request.data["username"]
try:
retirement = UserRetirementStatus.get_retirement_for_retirement_action(username)
@@ -771,10 +759,10 @@ class AccountRetirementPartnerReportView(ViewSet):
UserRetirementPartnerReportingStatus.objects.get_or_create(
user=retirement.user,
defaults={
- 'original_username': retirement.original_username,
- 'original_email': retirement.original_email,
- 'original_name': retirement.original_name
- }
+ "original_username": retirement.original_username,
+ "original_email": retirement.original_email,
+ "original_name": retirement.original_name,
+ },
)
return Response(status=status.HTTP_204_NO_CONTENT)
@@ -790,14 +778,13 @@ class AccountRetirementPartnerReportView(ViewSet):
Deletes UserRetirementPartnerReportingStatus objects for a list of users
that have been reported on.
"""
- usernames = [u['original_username'] for u in request.data]
+ usernames = [u["original_username"] for u in request.data]
if not usernames:
- return Response('No original_usernames given.', status=status.HTTP_400_BAD_REQUEST)
+ return Response("No original_usernames given.", status=status.HTTP_400_BAD_REQUEST)
retirement_statuses = UserRetirementPartnerReportingStatus.objects.filter(
- is_being_processed=True,
- original_username__in=usernames
+ is_being_processed=True, original_username__in=usernames
)
# Need to de-dupe usernames that differ only by case to find the exact right match
@@ -809,15 +796,15 @@ class AccountRetirementPartnerReportView(ViewSet):
# to disambiguate them in Python, which will respect case in the comparison.
if len(usernames) != len(retirement_statuses_clean):
return Response(
- '{} original_usernames given, {} found!\n'
- 'Given usernames:\n{}\n'
- 'Found UserRetirementReportingStatuses:\n{}'.format(
+ "{} original_usernames given, {} found!\n"
+ "Given usernames:\n{}\n"
+ "Found UserRetirementReportingStatuses:\n{}".format(
len(usernames),
len(retirement_statuses_clean),
usernames,
- ', '.join([rs.original_username for rs in retirement_statuses_clean])
+ ", ".join([rs.original_username for rs in retirement_statuses_clean]),
),
- status=status.HTTP_400_BAD_REQUEST
+ status=status.HTTP_400_BAD_REQUEST,
)
retirement_statuses.delete()
@@ -829,7 +816,11 @@ class CancelAccountRetirementStatusView(ViewSet):
"""
Provides API endpoints for canceling retirement process for a user's account.
"""
- permission_classes = (permissions.IsAuthenticated, CanCancelUserRetirement,)
+
+ permission_classes = (
+ permissions.IsAuthenticated,
+ CanCancelUserRetirement,
+ )
def cancel_retirement(self, request):
"""
@@ -839,26 +830,23 @@ class CancelAccountRetirementStatusView(ViewSet):
This also handles the top level error handling, and permissions.
"""
try:
- retirement_id = request.data['retirement_id']
+ retirement_id = request.data["retirement_id"]
except KeyError:
- return Response(
- status=status.HTTP_400_BAD_REQUEST,
- data={'message': 'retirement_id must be specified.'}
- )
+ return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": "retirement_id must be specified."})
try:
retirement = UserRetirementStatus.objects.get(id=retirement_id)
except UserRetirementStatus.DoesNotExist:
- return Response(data={"message": 'Retirement does not exist!'}, status=status.HTTP_400_BAD_REQUEST)
+ return Response(data={"message": "Retirement does not exist!"}, status=status.HTTP_400_BAD_REQUEST)
- if retirement.current_state.state_name != 'PENDING':
+ if retirement.current_state.state_name != "PENDING":
return Response(
status=status.HTTP_400_BAD_REQUEST,
data={
"message": f"Retirement requests can only be cancelled for users in the PENDING state. Current "
- f"request state for '{retirement.original_username}': "
- f"{retirement.current_state.state_name}"
- }
+ f"request state for '{retirement.original_username}': "
+ f"{retirement.current_state.state_name}"
+ },
)
handle_retirement_cancellation(retirement)
@@ -870,7 +858,11 @@ class AccountRetirementStatusView(ViewSet):
"""
Provides API endpoints for managing the user retirement process.
"""
- permission_classes = (permissions.IsAuthenticated, CanRetireUser,)
+
+ permission_classes = (
+ permissions.IsAuthenticated,
+ CanRetireUser,
+ )
parser_classes = (JSONParser,)
serializer_class = UserRetirementStatusSerializer
@@ -883,37 +875,35 @@ class AccountRetirementStatusView(ViewSet):
created in the retirement queue at least `cool_off_days` ago.
"""
try:
- cool_off_days = int(request.GET['cool_off_days'])
+ cool_off_days = int(request.GET["cool_off_days"])
if cool_off_days < 0:
- raise RetirementStateError('Invalid argument for cool_off_days, must be greater than 0.')
+ raise RetirementStateError("Invalid argument for cool_off_days, must be greater than 0.")
- states = request.GET.getlist('states')
+ states = request.GET.getlist("states")
if not states:
raise RetirementStateError('Param "states" required with at least one state.')
state_objs = RetirementState.objects.filter(state_name__in=states)
if state_objs.count() != len(states):
found = [s.state_name for s in state_objs]
- raise RetirementStateError(f'Unknown state. Requested: {states} Found: {found}')
+ raise RetirementStateError(f"Unknown state. Requested: {states} Found: {found}")
- limit = request.GET.get('limit')
+ limit = request.GET.get("limit")
if limit:
try:
limit_count = int(limit)
except ValueError:
return Response(
- f'Limit could not be parsed: {limit}, please ensure this is an integer',
- status=status.HTTP_400_BAD_REQUEST
+ f"Limit could not be parsed: {limit}, please ensure this is an integer",
+ status=status.HTTP_400_BAD_REQUEST,
)
earliest_datetime = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=cool_off_days)
- retirements = UserRetirementStatus.objects.select_related(
- 'user', 'current_state', 'last_state'
- ).filter(
- current_state__in=state_objs, created__lt=earliest_datetime
- ).order_by(
- 'id'
+ retirements = (
+ UserRetirementStatus.objects.select_related("user", "current_state", "last_state")
+ .filter(current_state__in=state_objs, created__lt=earliest_datetime)
+ .order_by("id")
)
if limit:
retirements = retirements[:limit_count]
@@ -921,10 +911,9 @@ class AccountRetirementStatusView(ViewSet):
return Response(serializer.data)
# This should only occur on the int() conversion of cool_off_days at this point
except ValueError:
- return Response('Invalid cool_off_days, should be integer.', status=status.HTTP_400_BAD_REQUEST)
+ return Response("Invalid cool_off_days, should be integer.", status=status.HTTP_400_BAD_REQUEST)
except KeyError as exc:
- return Response(f'Missing required parameter: {str(exc)}',
- status=status.HTTP_400_BAD_REQUEST)
+ return Response(f"Missing required parameter: {str(exc)}", status=status.HTTP_400_BAD_REQUEST)
except RetirementStateError as exc:
return Response(str(exc), status=status.HTTP_400_BAD_REQUEST)
@@ -939,36 +928,33 @@ class AccountRetirementStatusView(ViewSet):
so to get one day you would set both dates to that day.
"""
try:
- start_date = datetime.datetime.strptime(request.GET['start_date'], '%Y-%m-%d').replace(tzinfo=pytz.UTC)
- end_date = datetime.datetime.strptime(request.GET['end_date'], '%Y-%m-%d').replace(tzinfo=pytz.UTC)
+ start_date = datetime.datetime.strptime(request.GET["start_date"], "%Y-%m-%d").replace(tzinfo=pytz.UTC)
+ end_date = datetime.datetime.strptime(request.GET["end_date"], "%Y-%m-%d").replace(tzinfo=pytz.UTC)
now = datetime.datetime.now(pytz.UTC)
if start_date > now or end_date > now or start_date > end_date:
- raise RetirementStateError('Dates must be today or earlier, and start must be earlier than end.')
+ raise RetirementStateError("Dates must be today or earlier, and start must be earlier than end.")
# Add a day to make sure we get all the way to 23:59:59.999, this is compared "lt" in the query
# not "lte".
end_date += datetime.timedelta(days=1)
- state = request.GET['state']
+ state = request.GET["state"]
state_obj = RetirementState.objects.get(state_name=state)
- retirements = UserRetirementStatus.objects.select_related(
- 'user', 'current_state', 'last_state', 'user__profile'
- ).filter(
- current_state=state_obj, created__lt=end_date, created__gte=start_date
- ).order_by(
- 'id'
+ retirements = (
+ UserRetirementStatus.objects.select_related("user", "current_state", "last_state", "user__profile")
+ .filter(current_state=state_obj, created__lt=end_date, created__gte=start_date)
+ .order_by("id")
)
serializer = UserRetirementStatusSerializer(retirements, many=True)
return Response(serializer.data)
# This should only occur on the datetime conversion of the start / end dates.
except ValueError as exc:
- return Response(f'Invalid start or end date: {str(exc)}', status=status.HTTP_400_BAD_REQUEST)
+ return Response(f"Invalid start or end date: {str(exc)}", status=status.HTTP_400_BAD_REQUEST)
except KeyError as exc:
- return Response(f'Missing required parameter: {str(exc)}',
- status=status.HTTP_400_BAD_REQUEST)
+ return Response(f"Missing required parameter: {str(exc)}", status=status.HTTP_400_BAD_REQUEST)
except RetirementState.DoesNotExist:
- return Response('Unknown retirement state.', status=status.HTTP_400_BAD_REQUEST)
+ return Response("Unknown retirement state.", status=status.HTTP_400_BAD_REQUEST)
except RetirementStateError as exc:
return Response(str(exc), status=status.HTTP_400_BAD_REQUEST)
@@ -980,9 +966,9 @@ class AccountRetirementStatusView(ViewSet):
"""
try:
user = get_potentially_retired_user_by_username(username)
- retirement = UserRetirementStatus.objects.select_related(
- 'user', 'current_state', 'last_state'
- ).get(user=user)
+ retirement = UserRetirementStatus.objects.select_related("user", "current_state", "last_state").get(
+ user=user
+ )
serializer = UserRetirementStatusSerializer(instance=retirement)
return Response(serializer.data)
except (UserRetirementStatus.DoesNotExist, User.DoesNotExist):
@@ -1008,7 +994,7 @@ class AccountRetirementStatusView(ViewSet):
The content type for this request is 'application/json'.
"""
try:
- username = request.data['username']
+ username = request.data["username"]
retirements = UserRetirementStatus.objects.filter(original_username=username)
# During a narrow window learners were able to re-use a username that had been retired if
@@ -1049,20 +1035,19 @@ class AccountRetirementStatusView(ViewSet):
Deletes a batch of retirement requests by username.
"""
try:
- usernames = request.data['usernames']
+ usernames = request.data["usernames"]
if not isinstance(usernames, list):
- raise TypeError('Usernames should be an array.')
+ raise TypeError("Usernames should be an array.")
- complete_state = RetirementState.objects.get(state_name='COMPLETE')
+ complete_state = RetirementState.objects.get(state_name="COMPLETE")
retirements = UserRetirementStatus.objects.filter(
- original_username__in=usernames,
- current_state=complete_state
+ original_username__in=usernames, current_state=complete_state
)
# Sanity check that they're all valid usernames in the right state
if len(usernames) != len(retirements):
- raise UserRetirementStatus.DoesNotExist('Not all usernames exist in the COMPLETE state.')
+ raise UserRetirementStatus.DoesNotExist("Not all usernames exist in the COMPLETE state.")
retirements.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
@@ -1076,7 +1061,11 @@ class LMSAccountRetirementView(ViewSet):
"""
Provides an API endpoint for retiring a user in the LMS.
"""
- permission_classes = (permissions.IsAuthenticated, CanRetireUser,)
+
+ permission_classes = (
+ permissions.IsAuthenticated,
+ CanRetireUser,
+ )
parser_classes = (JSONParser,)
@request_requires_username
@@ -1093,13 +1082,13 @@ class LMSAccountRetirementView(ViewSet):
Retires the user with the given username in the LMS.
"""
- username = request.data['username']
+ username = request.data["username"]
try:
retirement = UserRetirementStatus.get_retirement_for_retirement_action(username)
RevisionPluginRevision.retire_user(retirement.user)
ArticleRevision.retire_user(retirement.user)
- PendingNameChange.delete_by_user_value(retirement.user, field='user')
+ PendingNameChange.delete_by_user_value(retirement.user, field="user")
ManualEnrollmentAudit.retire_manual_enrollments(retirement.user, retirement.retired_email)
CreditRequest.retire_user(retirement)
@@ -1115,7 +1104,7 @@ class LMSAccountRetirementView(ViewSet):
sender=self.__class__,
email=retirement.original_email,
new_email=retirement.retired_email,
- user=retirement.user
+ user=retirement.user,
)
except UserRetirementStatus.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
@@ -1131,7 +1120,11 @@ class AccountRetirementView(ViewSet):
"""
Provides API endpoint for retiring a user.
"""
- permission_classes = (permissions.IsAuthenticated, CanRetireUser,)
+
+ permission_classes = (
+ permissions.IsAuthenticated,
+ CanRetireUser,
+ )
parser_classes = (JSONParser,)
@request_requires_username
@@ -1148,7 +1141,7 @@ class AccountRetirementView(ViewSet):
Retires the user with the given username. This includes retiring this username, the associated email address,
and any other PII associated with this user.
"""
- username = request.data['username']
+ username = request.data["username"]
try:
retirement_status = UserRetirementStatus.get_retirement_for_retirement_action(username)
@@ -1173,18 +1166,18 @@ class AccountRetirementView(ViewSet):
self.retire_entitlement_support_detail(user)
# Retire misc. models that may contain PII of this user
- PendingEmailChange.delete_by_user_value(user, field='user')
- UserOrgTag.delete_by_user_value(user, field='user')
+ PendingEmailChange.delete_by_user_value(user, field="user")
+ UserOrgTag.delete_by_user_value(user, field="user")
# Retire any objects linked to the user via their original email
- CourseEnrollmentAllowed.delete_by_user_value(original_email, field='email')
- UnregisteredLearnerCohortAssignments.delete_by_user_value(original_email, field='email')
+ CourseEnrollmentAllowed.delete_by_user_value(original_email, field="email")
+ UnregisteredLearnerCohortAssignments.delete_by_user_value(original_email, field="email")
# This signal allows code in higher points of LMS to retire the user as necessary
USER_RETIRE_LMS_CRITICAL.send(sender=self.__class__, user=user)
- user.first_name = ''
- user.last_name = ''
+ user.first_name = ""
+ user.last_name = ""
user.is_active = False
user.username = retired_username
user.save()
@@ -1227,24 +1220,20 @@ class AccountRetirementView(ViewSet):
@staticmethod
def retire_sapsf_data_transmission(user): # lint-amnesty, pylint: disable=missing-function-docstring
for ent_user in EnterpriseCustomerUser.objects.filter(user_id=user.id):
- for enrollment in EnterpriseCourseEnrollment.objects.filter(
- enterprise_customer_user=ent_user
- ):
+ for enrollment in EnterpriseCourseEnrollment.objects.filter(enterprise_customer_user=ent_user):
audits = SapSuccessFactorsLearnerDataTransmissionAudit.objects.filter(
enterprise_course_enrollment_id=enrollment.id
)
- audits.update(sapsf_user_id='')
+ audits.update(sapsf_user_id="")
@staticmethod
def retire_degreed_data_transmission(user): # lint-amnesty, pylint: disable=missing-function-docstring
for ent_user in EnterpriseCustomerUser.objects.filter(user_id=user.id):
- for enrollment in EnterpriseCourseEnrollment.objects.filter(
- enterprise_customer_user=ent_user
- ):
+ for enrollment in EnterpriseCourseEnrollment.objects.filter(enterprise_customer_user=ent_user):
audits = DegreedLearnerDataTransmissionAudit.objects.filter(
enterprise_course_enrollment_id=enrollment.id
)
- audits.update(degreed_user_email='')
+ audits.update(degreed_user_email="")
@staticmethod
def retire_user_from_pending_enterprise_customer_user(user, retired_email):
@@ -1256,7 +1245,7 @@ class AccountRetirementView(ViewSet):
Updates all CourseEntitleSupportDetail records for the given user to have an empty ``comments`` field.
"""
for entitlement in CourseEntitlement.objects.filter(user_id=user.id):
- entitlement.courseentitlementsupportdetail_set.all().update(comments='')
+ entitlement.courseentitlementsupportdetail_set.all().update(comments="")
@staticmethod
def clear_pii_from_certificate_records(user):
@@ -1279,6 +1268,7 @@ class UsernameReplacementView(APIView):
This API will be called first, before calling the APIs in other services as this
one handles the checks on the usernames provided.
"""
+
permission_classes = (permissions.IsAuthenticated, CanReplaceUsername)
def post(self, request):
@@ -1320,16 +1310,16 @@ class UsernameReplacementView(APIView):
# (model_name, column_name)
MODELS_WITH_USERNAME = (
- ('auth.user', 'username'),
- ('consent.DataSharingConsent', 'username'),
- ('consent.HistoricalDataSharingConsent', 'username'),
- ('credit.CreditEligibility', 'username'),
- ('credit.CreditRequest', 'username'),
- ('credit.CreditRequirementStatus', 'username'),
- ('user_api.UserRetirementPartnerReportingStatus', 'original_username'),
- ('user_api.UserRetirementStatus', 'original_username')
+ ("auth.user", "username"),
+ ("consent.DataSharingConsent", "username"),
+ ("consent.HistoricalDataSharingConsent", "username"),
+ ("credit.CreditEligibility", "username"),
+ ("credit.CreditRequest", "username"),
+ ("credit.CreditRequirementStatus", "username"),
+ ("user_api.UserRetirementPartnerReportingStatus", "original_username"),
+ ("user_api.UserRetirementStatus", "original_username"),
)
- UNIQUE_SUFFIX_LENGTH = getattr(settings, 'SOCIAL_AUTH_UUID_LENGTH', 4)
+ UNIQUE_SUFFIX_LENGTH = getattr(settings, "SOCIAL_AUTH_UUID_LENGTH", 4)
username_mappings = request.data.get("username_mappings")
replacement_locations = self._load_models(MODELS_WITH_USERNAME)
@@ -1344,9 +1334,7 @@ class UsernameReplacementView(APIView):
desired_username = list(username_pair.values())[0]
new_username = self._generate_unique_username(desired_username, suffix_length=UNIQUE_SUFFIX_LENGTH)
successfully_replaced = self._replace_username_for_all_models(
- current_username,
- new_username,
- replacement_locations
+ current_username, new_username, replacement_locations
)
if successfully_replaced:
successful_replacements.append({current_username: new_username})
@@ -1354,14 +1342,11 @@ class UsernameReplacementView(APIView):
failed_replacements.append({current_username: new_username})
return Response(
status=status.HTTP_200_OK,
- data={
- "successful_replacements": successful_replacements,
- "failed_replacements": failed_replacements
- }
+ data={"successful_replacements": successful_replacements, "failed_replacements": failed_replacements},
)
def _load_models(self, models_with_fields):
- """ Takes tuples that contain a model path and returns the list with a loaded version of the model """
+ """Takes tuples that contain a model path and returns the list with a loaded version of the model"""
try:
replacement_locations = [(apps.get_model(model), column) for (model, column) in models_with_fields]
except LookupError:
@@ -1370,7 +1355,7 @@ class UsernameReplacementView(APIView):
return replacement_locations
def _has_valid_schema(self, post_data):
- """ Verifies the data is a list of objects with a single key:value pair """
+ """Verifies the data is a list of objects with a single key:value pair"""
if not isinstance(post_data, list):
return False
for obj in post_data:
@@ -1389,7 +1374,7 @@ class UsernameReplacementView(APIView):
while True:
if User.objects.filter(username=new_username).exists():
# adding a dash between user-supplied and system-generated values to avoid weird combinations
- new_username = desired_username + '-' + username_suffix_generator(suffix_length)
+ new_username = desired_username + "-" + username_suffix_generator(suffix_length)
else:
break
return new_username
@@ -1404,10 +1389,8 @@ class UsernameReplacementView(APIView):
try:
with transaction.atomic():
num_rows_changed = 0
- for (model, column) in replacement_locations:
- num_rows_changed += model.objects.filter(
- **{column: current_username}
- ).update(
+ for model, column in replacement_locations:
+ num_rows_changed += model.objects.filter(**{column: current_username}).update(
**{column: new_username}
)
except Exception as exc: # pylint: disable=broad-except
@@ -1416,7 +1399,7 @@ class UsernameReplacementView(APIView):
current_username,
new_username,
model.__class__.__name__, # Retrieves the model name that it failed on
- exc
+ exc,
)
return False
if num_rows_changed == 0:
From b30318af6a7e52b4401df7e74955b1c24df16eb3 Mon Sep 17 00:00:00 2001
From: michaelroytman
Date: Mon, 12 Aug 2024 16:55:45 -0400
Subject: [PATCH 074/242] feat: add VerificationAttempt model to verify_student
application
This commits adds a VerificationAttempt model to store implementation and provider agnostic information about identity verification attempts in the platform.
---
.../0001_extending_identity_verification.rst | 65 +++++++++++++++++++
.../migrations/0015_verificationattempt.py | 33 ++++++++++
lms/djangoapps/verify_student/models.py | 25 +++++++
lms/djangoapps/verify_student/statuses.py | 21 ++++++
4 files changed, 144 insertions(+)
create mode 100644 lms/djangoapps/verify_student/docs/decisions/0001_extending_identity_verification.rst
create mode 100644 lms/djangoapps/verify_student/migrations/0015_verificationattempt.py
create mode 100644 lms/djangoapps/verify_student/statuses.py
diff --git a/lms/djangoapps/verify_student/docs/decisions/0001_extending_identity_verification.rst b/lms/djangoapps/verify_student/docs/decisions/0001_extending_identity_verification.rst
new file mode 100644
index 0000000000..08735188fc
--- /dev/null
+++ b/lms/djangoapps/verify_student/docs/decisions/0001_extending_identity_verification.rst
@@ -0,0 +1,65 @@
+0001. Extending Identity Verification
+#####################################
+
+Status
+******
+
+**Accepted** *2024-08-26*
+
+Context
+*******
+
+The backend implementation of identity verification (IDV) is in the `verify_student Django application`_. The
+`verify_student Django application`_ also contains a frontend user experience for performing photo IDV via an
+integration with Software Secure. There is also a `React-based implementation of this flow`_ in the
+`frontend-app-account MFE`_, so the frontend user experience stored in the `verify_student Django application`_ is often
+called the "legacy flow".
+
+The current architecture of the `verify_student Django application`_ requires that any additional implementations of IDV
+are stored in the application. For example, the Software Secure integration is stored in this application even though
+it is a custom integration that the Open edX community does not use.
+
+Different Open edX operators have different IDV needs. There is currently no way to add additional IDV implementations
+to the platform without committing them to the core. The `verify_student Django application`_ needs enhanced
+extensibility mechanisms to enable per-deployment integration of IDV implementations without modifying the core.
+
+Decision
+********
+
+* We will support the integration of additional implementations of IDV through the use of Python plugins into the
+ platform.
+* We will add a ``VerificationAttempt`` model, which will store generic, implementation-agnostic information about an
+ IDV attempt.
+* We will expose a simple Python API to write and update instances of the ``VerificationAttempt`` model. This will
+ enable plugins to publish information about their IDV attempts to the platform.
+* The ``VerificationAttempt`` model will be integrated into the `verify_student Django application`_, particularly into
+ the `IDVerificationService`_.
+* We will emit Open edX events for each status change of a ``VerificationAttempt``.
+* We will add an Open edX filter hook to change the URL of the photo IDV frontend.
+
+Consequences
+************
+
+* It will become possible for Open edX operators to implement and integrate any additional forms of IDV necessary for
+ their deployment.
+* The `verify_student Django application`_ will contain both concrete implementations of forms of IDV (i.e. manual, SSO,
+ Software Secure, etc.) and a generic, extensible implementation. The work to deprecate and remove the Software Secure
+ integration and to transition the other existing forms of IDV (i.e. manual and SSO) to Django plugins will occur
+ independently of the improvements to extensibility described in this decision.
+
+Rejected Alternatives
+*********************
+
+We considered introducing a ``fetch_verification_attempts`` filter hook to allow plugins to expose additional
+``VerificationAttempts`` to the platform in lieu of an additional model. However, doing database queries via filter
+hooks can cause unpredictable performance problems, and this has been a pain point for Open edX.
+
+References
+**********
+`[Proposal] Add Extensibility Mechanisms to IDV to Enable Integration of New IDV Vendor Persona `_
+`Add Extensibility Mechanisms to IDV to Enable Integration of New IDV Vendor Persona `_
+
+.. _frontend-app-account MFE: https://github.com/openedx/frontend-app-account
+.. _IDVerificationService: https://github.com/openedx/edx-platform/blob/master/lms/djangoapps/verify_student/services.py#L55
+.. _React-based implementation of this flow: https://github.com/openedx/frontend-app-account/tree/master/src/id-verification
+.. _verify_student Django application: https://github.com/openedx/edx-platform/tree/master/lms/djangoapps/verify_student
diff --git a/lms/djangoapps/verify_student/migrations/0015_verificationattempt.py b/lms/djangoapps/verify_student/migrations/0015_verificationattempt.py
new file mode 100644
index 0000000000..3f01047f9f
--- /dev/null
+++ b/lms/djangoapps/verify_student/migrations/0015_verificationattempt.py
@@ -0,0 +1,33 @@
+# Generated by Django 4.2.15 on 2024-08-26 14:05
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+import model_utils.fields
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('verify_student', '0014_remove_softwaresecurephotoverification_expiry_date'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='VerificationAttempt',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
+ ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
+ ('name', models.CharField(blank=True, max_length=255)),
+ ('status', models.CharField(choices=[('created', 'created'), ('pending', 'pending'), ('approved', 'approved'), ('denied', 'denied')], max_length=64)),
+ ('expiration_datetime', models.DateTimeField(blank=True, null=True)),
+ ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/lms/djangoapps/verify_student/models.py b/lms/djangoapps/verify_student/models.py
index f7750a4cd6..903d80bf92 100644
--- a/lms/djangoapps/verify_student/models.py
+++ b/lms/djangoapps/verify_student/models.py
@@ -31,6 +31,7 @@ from django.utils.timezone import now
from django.utils.translation import gettext_lazy
from model_utils import Choices
from model_utils.models import StatusModel, TimeStampedModel
+from lms.djangoapps.verify_student.statuses import VerificationAttemptStatus
from opaque_keys.edx.django.models import CourseKeyField
from lms.djangoapps.verify_student.ssencrypt import (
@@ -1189,3 +1190,27 @@ class SSPVerificationRetryConfig(ConfigurationModel): # pylint: disable=model-m
def __str__(self):
return str(self.arguments)
+
+
+class VerificationAttempt(TimeStampedModel):
+ """
+ The model represents impelementation-agnostic information about identity verification (IDV) attempts.
+
+ Plugins that implement forms of IDV can store information about IDV attempts in this model for use across
+ the platform.
+ """
+ user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE)
+ name = models.CharField(blank=True, max_length=255)
+
+ STATUS_CHOICES = [
+ VerificationAttemptStatus.created,
+ VerificationAttemptStatus.pending,
+ VerificationAttemptStatus.approved,
+ VerificationAttemptStatus.denied,
+ ]
+ status = models.CharField(max_length=64, choices=[(status, status) for status in STATUS_CHOICES])
+
+ expiration_datetime = models.DateTimeField(
+ null=True,
+ blank=True,
+ )
diff --git a/lms/djangoapps/verify_student/statuses.py b/lms/djangoapps/verify_student/statuses.py
new file mode 100644
index 0000000000..b55a9042e0
--- /dev/null
+++ b/lms/djangoapps/verify_student/statuses.py
@@ -0,0 +1,21 @@
+"""
+Status enums for verify_student.
+"""
+
+
+class VerificationAttemptStatus:
+ """This class describes valid statuses for a verification attempt to be in."""
+
+ # This is the initial state of a verification attempt, before a learner has started IDV.
+ created = "created"
+
+ # A verification attempt is pending when it has been started but has not yet been completed.
+ pending = "pending"
+
+ # A verification attempt is approved when it has been approved by some mechanism (e.g. automatic review, manual
+ # review, etc).
+ approved = "approved"
+
+ # A verification attempt is denied when it has been denied by some mechanism (e.g. automatic review, manual review,
+ # etc).
+ denied = "denied"
From a9d6d4b20d0e15280cb312e9fd98ad3956bbc0e8 Mon Sep 17 00:00:00 2001
From: MueezKhan246 <93375917+MueezKhan246@users.noreply.github.com>
Date: Mon, 26 Aug 2024 17:40:07 +0000
Subject: [PATCH 075/242] feat: Upgrade Python dependency edx-enterprise
added encrypted columns for user credentials for SAP config
Commit generated by workflow `openedx/edx-platform/.github/workflows/upgrade-one-python-dependency.yml@refs/heads/master`
---
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 74263b5f71..b7c7bebc71 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -26,7 +26,7 @@ celery>=5.2.2,<6.0.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==4.23.9
+edx-enterprise==4.23.13
# Stay on LTS version, remove once this is added to common constraint
Django<5.0
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 27de7847f2..1ed691bf5a 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -467,7 +467,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.9
+edx-enterprise==4.23.13
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index 0bdc8144ee..0f88bad4b5 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -741,7 +741,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.9
+edx-enterprise==4.23.13
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 91b30d81df..663e09b3d6 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -547,7 +547,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.9
+edx-enterprise==4.23.13
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 2092c93548..e8ed020e51 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -571,7 +571,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.9
+edx-enterprise==4.23.13
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
From 275d4d989fd0a6ba5033448eca3228c42d572a42 Mon Sep 17 00:00:00 2001
From: Awais Qureshi
Date: Tue, 27 Aug 2024 14:23:59 +0500
Subject: [PATCH 076/242] feat: show_student_extensions upgrading api to drf
compatible ( 9th ) (#35148)
* feat: upgrading simple api to drf compatible.
---
lms/djangoapps/instructor/views/api.py | 54 +++++++++++++++----
lms/djangoapps/instructor/views/api_urls.py | 2 +-
lms/djangoapps/instructor/views/serializer.py | 18 +++++++
3 files changed, 62 insertions(+), 12 deletions(-)
diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py
index 7ca1e70467..1aa40b5e33 100644
--- a/lms/djangoapps/instructor/views/api.py
+++ b/lms/djangoapps/instructor/views/api.py
@@ -105,7 +105,9 @@ from lms.djangoapps.instructor_task import api as task_api
from lms.djangoapps.instructor_task.api_helper import AlreadyRunningError, QueueConnectionError
from lms.djangoapps.instructor_task.data import InstructorTaskTypes
from lms.djangoapps.instructor_task.models import ReportStore
-from lms.djangoapps.instructor.views.serializer import RoleNameSerializer, UserSerializer, AccessSerializer
+from lms.djangoapps.instructor.views.serializer import (
+ AccessSerializer, RoleNameSerializer, ShowStudentExtensionSerializer, UserSerializer
+)
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort, is_course_cohorted
from openedx.core.djangoapps.course_groups.models import CourseUserGroup
@@ -2979,20 +2981,50 @@ def show_unit_extensions(request, course_id):
return JsonResponse(dump_block_extensions(course, unit))
-@handle_dashboard_error
-@require_POST
-@ensure_csrf_cookie
-@cache_control(no_cache=True, no_store=True, must_revalidate=True)
-@require_course_permission(permissions.GIVE_STUDENT_EXTENSION)
-@require_post_params('student')
-def show_student_extensions(request, course_id):
+@method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True), name='dispatch')
+class ShowStudentExtensions(APIView):
"""
Shows all of the due date extensions granted to a particular student in a
particular course.
"""
- student = require_student_from_identifier(request.POST.get('student'))
- course = get_course_by_id(CourseKey.from_string(course_id))
- return JsonResponse(dump_student_extensions(course, student))
+ permission_classes = (IsAuthenticated, permissions.InstructorPermission)
+ serializer_class = ShowStudentExtensionSerializer
+ permission_name = permissions.GIVE_STUDENT_EXTENSION
+
+ @method_decorator(ensure_csrf_cookie)
+ def post(self, request, course_id):
+ """
+ Handles POST requests to retrieve due date extensions for a specific student
+ within a specified course.
+
+ Parameters:
+ - `request`: The HTTP request object containing user-submitted data.
+ - `course_id`: The ID of the course for which the extensions are being queried.
+
+ Data expected in the request:
+ - `student`: A required field containing the identifier of the student for whom
+ the due date extensions are being retrieved. This data is extracted from the
+ request body.
+
+ Returns:
+ - A JSON response containing the details of the due date extensions granted to
+ the specified student in the specified course.
+ """
+ data = {
+ 'student': request.data.get('student')
+ }
+ serializer_data = self.serializer_class(data=data)
+
+ if not serializer_data.is_valid():
+ return HttpResponseBadRequest(reason=serializer_data.errors)
+
+ student = serializer_data.validated_data.get('student')
+ if not student:
+ response_payload = f'Could not find student matching identifier: {request.data.get("student")}'
+ return JsonResponse({'error': response_payload}, status=400)
+
+ course = get_course_by_id(CourseKey.from_string(course_id))
+ return Response(dump_student_extensions(course, student))
def _split_input_list(str_list):
diff --git a/lms/djangoapps/instructor/views/api_urls.py b/lms/djangoapps/instructor/views/api_urls.py
index c0e12e4675..18da0b63b2 100644
--- a/lms/djangoapps/instructor/views/api_urls.py
+++ b/lms/djangoapps/instructor/views/api_urls.py
@@ -53,7 +53,7 @@ urlpatterns = [
path('change_due_date', api.change_due_date, name='change_due_date'),
path('reset_due_date', api.reset_due_date, name='reset_due_date'),
path('show_unit_extensions', api.show_unit_extensions, name='show_unit_extensions'),
- path('show_student_extensions', api.show_student_extensions, name='show_student_extensions'),
+ path('show_student_extensions', api.ShowStudentExtensions.as_view(), name='show_student_extensions'),
# proctored exam downloads...
path('get_proctored_exam_results', api.get_proctored_exam_results, name='get_proctored_exam_results'),
diff --git a/lms/djangoapps/instructor/views/serializer.py b/lms/djangoapps/instructor/views/serializer.py
index 463f05ad45..0697bed683 100644
--- a/lms/djangoapps/instructor/views/serializer.py
+++ b/lms/djangoapps/instructor/views/serializer.py
@@ -59,3 +59,21 @@ class AccessSerializer(serializers.Serializer):
return None
return user
+
+
+class ShowStudentExtensionSerializer(serializers.Serializer):
+ """
+ Serializer for validating and processing the student identifier.
+ """
+ student = serializers.CharField(write_only=True, required=True)
+
+ def validate_student(self, value):
+ """
+ Validate that the student corresponds to an existing user.
+ """
+ try:
+ user = get_student_from_identifier(value)
+ except User.DoesNotExist:
+ return None
+
+ return user
From f0d8d5262c03ff5d8ee891fed59bb06678d7a002 Mon Sep 17 00:00:00 2001
From: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>
Date: Tue, 27 Aug 2024 09:06:28 -0400
Subject: [PATCH 077/242] fix: add aria-current attributes to video captions
(#35371)
This PR fixes the accessibility issues associated with only visually treating the current transcript line. The current implementation for the transcript panel bolds the text that is actively being spoken or skipped to. However, there is no aria attribute present to convey this change to assistive technology. This change impacts learners and course authors.
---
xmodule/js/src/tabs/tabs-aggregator.js | 4 ++--
xmodule/js/src/video/09_video_caption.js | 5 +++--
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/xmodule/js/src/tabs/tabs-aggregator.js b/xmodule/js/src/tabs/tabs-aggregator.js
index 83baca4cf6..da982b6afc 100644
--- a/xmodule/js/src/tabs/tabs-aggregator.js
+++ b/xmodule/js/src/tabs/tabs-aggregator.js
@@ -63,8 +63,8 @@
if ($.isFunction(onSwitchFunction)) {
onSwitchFunction();
}
- this.$tabs.removeClass('current');
- $currentTarget.addClass('current');
+ this.$tabs.attr('aria-current', 'false').removeClass('current');
+ $currentTarget.attr('aria-current', 'true').addClass('current');
/*
Tabs are implemeted like anchors. Therefore we can use hash to find
diff --git a/xmodule/js/src/video/09_video_caption.js b/xmodule/js/src/video/09_video_caption.js
index 37e3923067..f5db26514b 100644
--- a/xmodule/js/src/video/09_video_caption.js
+++ b/xmodule/js/src/video/09_video_caption.js
@@ -1096,12 +1096,13 @@
if (typeof this.currentIndex !== 'undefined') {
this.subtitlesEl
.find('li.current')
+ .attr('aria-current', 'false')
.removeClass('current');
- }
-
+ }
this.subtitlesEl
.find("span[data-index='" + newIndex + "']")
.parent()
+ .attr('aria-current', 'true')
.addClass('current');
this.currentIndex = newIndex;
From 2e77e65d1c034a7e929d284c99f8bc5b0b1f0025 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Chris=20Ch=C3=A1vez?=
Date: Tue, 27 Aug 2024 11:43:29 -0500
Subject: [PATCH 078/242] feat: Problem types added as filterable attribute
[FC-0059] (#35242)
---
openedx/core/djangoapps/content/search/api.py | 1 +
openedx/core/djangoapps/content/search/documents.py | 1 +
2 files changed, 2 insertions(+)
diff --git a/openedx/core/djangoapps/content/search/api.py b/openedx/core/djangoapps/content/search/api.py
index 4262b8a7b1..f59b9de371 100644
--- a/openedx/core/djangoapps/content/search/api.py
+++ b/openedx/core/djangoapps/content/search/api.py
@@ -323,6 +323,7 @@ def rebuild_index(status_cb: Callable[[str], None] | None = None) -> None:
Fields.type,
Fields.access_id,
Fields.last_published,
+ Fields.content + "." + Fields.problem_types,
])
# Mark which attributes are used for keyword search, in order of importance:
client.index(temp_index_name).update_searchable_attributes([
diff --git a/openedx/core/djangoapps/content/search/documents.py b/openedx/core/djangoapps/content/search/documents.py
index 032023f97c..57720e54d0 100644
--- a/openedx/core/djangoapps/content/search/documents.py
+++ b/openedx/core/djangoapps/content/search/documents.py
@@ -31,6 +31,7 @@ class Fields:
created = "created"
last_published = "last_published"
block_type = "block_type"
+ problem_types = "problem_types"
context_key = "context_key"
org = "org"
access_id = "access_id" # .models.SearchAccess.id
From 9124e7bca442725c23a6a1acf230d7e587305223 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=B4mulo=20Penido?=
Date: Tue, 27 Aug 2024 14:31:13 -0300
Subject: [PATCH 079/242] feat: allow update xblock fields without passing data
(#35320)
---
openedx/core/djangoapps/xblock/rest_api/views.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/openedx/core/djangoapps/xblock/rest_api/views.py b/openedx/core/djangoapps/xblock/rest_api/views.py
index 8c2d16839a..9972e7463b 100644
--- a/openedx/core/djangoapps/xblock/rest_api/views.py
+++ b/openedx/core/djangoapps/xblock/rest_api/views.py
@@ -226,7 +226,9 @@ class BlockFieldsView(APIView):
old_metadata = block.get_explicitly_set_fields_by_scope(Scope.settings)
old_content = block.get_explicitly_set_fields_by_scope(Scope.content)
- block.data = data
+ # only update data if it was passed
+ if data is not None:
+ block.data = data
# update existing metadata with submitted metadata (which can be partial)
# IMPORTANT NOTE: if the client passed 'null' (None) for a piece of metadata that means 'remove it'.
From c65478e487e98042af99bbab3243efb9abfaad74 Mon Sep 17 00:00:00 2001
From: Navin Karkera
Date: Tue, 27 Aug 2024 23:15:46 +0530
Subject: [PATCH 080/242] feat: index library collections in studio meilisearch
index (#35324)
---
openedx/core/djangoapps/content/search/api.py | 101 +++++++++++++-----
.../djangoapps/content/search/documents.py | 38 +++++++
.../content/search/tests/test_api.py | 42 +++++++-
.../content/search/tests/test_documents.py | 35 +++++-
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
9 files changed, 192 insertions(+), 34 deletions(-)
diff --git a/openedx/core/djangoapps/content/search/api.py b/openedx/core/djangoapps/content/search/api.py
index f59b9de371..9fb49b24b6 100644
--- a/openedx/core/djangoapps/content/search/api.py
+++ b/openedx/core/djangoapps/content/search/api.py
@@ -19,6 +19,7 @@ from meilisearch.errors import MeilisearchError
from meilisearch.models.task import TaskInfo
from opaque_keys.edx.keys import UsageKey
from opaque_keys.edx.locator import LibraryLocatorV2
+from openedx_learning.api import authoring as authoring_api
from common.djangoapps.student.roles import GlobalStaff
from rest_framework.request import Request
from common.djangoapps.student.role_helpers import get_course_roles
@@ -31,8 +32,9 @@ from .documents import (
Fields,
meili_id_from_opaque_key,
searchable_doc_for_course_block,
+ searchable_doc_for_collection,
searchable_doc_for_library_block,
- searchable_doc_tags
+ searchable_doc_tags,
)
log = logging.getLogger(__name__)
@@ -294,12 +296,16 @@ def rebuild_index(status_cb: Callable[[str], None] | None = None) -> None:
status_cb("Counting courses...")
num_courses = CourseOverview.objects.count()
+ # Get the list of collections
+ status_cb("Counting collections...")
+ num_collections = authoring_api.get_collections().count()
+
# Some counters so we can track our progress as indexing progresses:
- num_contexts = num_courses + num_libraries
+ num_contexts = num_courses + num_libraries + num_collections
num_contexts_done = 0 # How many courses/libraries we've indexed
num_blocks_done = 0 # How many individual components/XBlocks we've indexed
- status_cb(f"Found {num_courses} courses and {num_libraries} libraries.")
+ status_cb(f"Found {num_courses} courses, {num_libraries} libraries and {num_collections} collections.")
with _using_temp_index(status_cb) as temp_index_name:
############## Configure the index ##############
@@ -332,6 +338,7 @@ def rebuild_index(status_cb: Callable[[str], None] | None = None) -> None:
Fields.block_id,
Fields.content,
Fields.tags,
+ Fields.description,
# If we don't list the following sub-fields _explicitly_, they're only sometimes searchable - that is, they
# are searchable only if at least one document in the index has a value. If we didn't list them here and,
# say, there were no tags.level3 tags in the index, the client would get an error if trying to search for
@@ -363,8 +370,8 @@ def rebuild_index(status_cb: Callable[[str], None] | None = None) -> None:
############## Libraries ##############
status_cb("Indexing libraries...")
- for lib_key in lib_keys:
- status_cb(f"{num_contexts_done + 1}/{num_contexts}. Now indexing library {lib_key}")
+
+ def index_library(lib_key: str) -> list:
docs = []
for component in lib_api.get_library_components(lib_key):
try:
@@ -375,48 +382,88 @@ def rebuild_index(status_cb: Callable[[str], None] | None = None) -> None:
docs.append(doc)
except Exception as err: # pylint: disable=broad-except
status_cb(f"Error indexing library component {component}: {err}")
- finally:
- num_blocks_done += 1
if docs:
try:
# Add all the docs in this library at once (usually faster than adding one at a time):
_wait_for_meili_task(client.index(temp_index_name).add_documents(docs))
except (TypeError, KeyError, MeilisearchError) as err:
status_cb(f"Error indexing library {lib_key}: {err}")
+ return docs
+ for lib_key in lib_keys:
+ status_cb(f"{num_contexts_done + 1}/{num_contexts}. Now indexing library {lib_key}")
+ lib_docs = index_library(lib_key)
+ num_blocks_done += len(lib_docs)
num_contexts_done += 1
############## Courses ##############
status_cb("Indexing courses...")
# To reduce memory usage on large instances, split up the CourseOverviews into pages of 1,000 courses:
+
+ def index_course(course: CourseOverview) -> list:
+ docs = []
+ # Pre-fetch the course with all of its children:
+ course = store.get_course(course.id, depth=None)
+
+ def add_with_children(block):
+ """ Recursively index the given XBlock/component """
+ doc = searchable_doc_for_course_block(block)
+ doc.update(searchable_doc_tags(block.usage_key))
+ docs.append(doc) # pylint: disable=cell-var-from-loop
+ _recurse_children(block, add_with_children) # pylint: disable=cell-var-from-loop
+
+ # Index course children
+ _recurse_children(course, add_with_children)
+
+ if docs:
+ # Add all the docs in this course at once (usually faster than adding one at a time):
+ _wait_for_meili_task(client.index(temp_index_name).add_documents(docs))
+ return docs
+
paginator = Paginator(CourseOverview.objects.only('id', 'display_name'), 1000)
for p in paginator.page_range:
for course in paginator.page(p).object_list:
status_cb(
f"{num_contexts_done + 1}/{num_contexts}. Now indexing course {course.display_name} ({course.id})"
)
- docs = []
-
- # Pre-fetch the course with all of its children:
- course = store.get_course(course.id, depth=None)
-
- def add_with_children(block):
- """ Recursively index the given XBlock/component """
- doc = searchable_doc_for_course_block(block)
- doc.update(searchable_doc_tags(block.usage_key))
- docs.append(doc) # pylint: disable=cell-var-from-loop
- _recurse_children(block, add_with_children) # pylint: disable=cell-var-from-loop
-
- # Index course children
- _recurse_children(course, add_with_children)
-
- if docs:
- # Add all the docs in this course at once (usually faster than adding one at a time):
- _wait_for_meili_task(client.index(temp_index_name).add_documents(docs))
+ course_docs = index_course(course)
num_contexts_done += 1
- num_blocks_done += len(docs)
+ num_blocks_done += len(course_docs)
- status_cb(f"Done! {num_blocks_done} blocks indexed across {num_contexts_done} courses and libraries.")
+ ############## Collections ##############
+ status_cb("Indexing collections...")
+
+ def index_collection_batch(batch, num_contexts_done) -> int:
+ docs = []
+ for collection in batch:
+ status_cb(
+ f"{num_contexts_done + 1}/{num_contexts}. "
+ f"Now indexing collection {collection.title} ({collection.id})"
+ )
+ try:
+ doc = searchable_doc_for_collection(collection)
+ # Uncomment below line once collections are tagged.
+ # doc.update(searchable_doc_tags(collection.id))
+ docs.append(doc)
+ except Exception as err: # pylint: disable=broad-except
+ status_cb(f"Error indexing collection {collection}: {err}")
+ finally:
+ num_contexts_done += 1
+
+ if docs:
+ try:
+ # Add docs in batch of 100 at once (usually faster than adding one at a time):
+ _wait_for_meili_task(client.index(temp_index_name).add_documents(docs))
+ except (TypeError, KeyError, MeilisearchError) as err:
+ status_cb(f"Error indexing collection batch {p}: {err}")
+ return num_contexts_done
+
+ # To reduce memory usage on large instances, split up the Collections into pages of 100 collections:
+ paginator = Paginator(authoring_api.get_collections(enabled=True), 100)
+ for p in paginator.page_range:
+ num_contexts_done = index_collection_batch(paginator.page(p).object_list, num_contexts_done)
+
+ status_cb(f"Done! {num_blocks_done} blocks indexed across {num_contexts_done} courses, collections and libraries.")
def upsert_xblock_index_doc(usage_key: UsageKey, recursive: bool = True) -> None:
diff --git a/openedx/core/djangoapps/content/search/documents.py b/openedx/core/djangoapps/content/search/documents.py
index 57720e54d0..a45b37ab2a 100644
--- a/openedx/core/djangoapps/content/search/documents.py
+++ b/openedx/core/djangoapps/content/search/documents.py
@@ -13,6 +13,7 @@ from openedx.core.djangoapps.content.search.models import SearchAccess
from openedx.core.djangoapps.content_libraries import api as lib_api
from openedx.core.djangoapps.content_tagging import api as tagging_api
from openedx.core.djangoapps.xblock import api as xblock_api
+from openedx_learning.api.authoring_models import LearningPackage
log = logging.getLogger(__name__)
@@ -27,6 +28,7 @@ class Fields:
type = "type" # DocType.course_block or DocType.library_block (see below)
block_id = "block_id" # The block_id part of the usage key. Sometimes human-readable, sometimes a random hex ID
display_name = "display_name"
+ description = "description"
modified = "modified"
created = "created"
last_published = "last_published"
@@ -66,6 +68,7 @@ class DocType:
"""
course_block = "course_block"
library_block = "library_block"
+ collection = "collection"
def meili_id_from_opaque_key(usage_key: UsageKey) -> str:
@@ -276,3 +279,38 @@ def searchable_doc_for_course_block(block) -> dict:
doc.update(_fields_from_block(block))
return doc
+
+
+def searchable_doc_for_collection(collection) -> dict:
+ """
+ Generate a dictionary document suitable for ingestion into a search engine
+ like Meilisearch or Elasticsearch, so that the given collection can be
+ found using faceted search.
+ """
+ doc = {
+ Fields.id: collection.id,
+ Fields.type: DocType.collection,
+ Fields.display_name: collection.title,
+ Fields.description: collection.description,
+ Fields.created: collection.created.timestamp(),
+ Fields.modified: collection.modified.timestamp(),
+ # Add related learning_package.key as context_key by default.
+ # If related contentlibrary is found, it will override this value below.
+ # Mostly contentlibrary.library_key == learning_package.key
+ Fields.context_key: collection.learning_package.key,
+ }
+ # Just in case learning_package is not related to a library
+ try:
+ context_key = collection.learning_package.contentlibrary.library_key
+ org = str(context_key.org)
+ doc.update({
+ Fields.context_key: str(context_key),
+ Fields.org: org,
+ })
+ except LearningPackage.contentlibrary.RelatedObjectDoesNotExist:
+ log.warning(f"Related library not found for {collection}")
+ doc[Fields.access_id] = _meili_access_id_from_context_key(doc[Fields.context_key])
+ # Add the breadcrumbs.
+ doc[Fields.breadcrumbs] = [{"display_name": collection.learning_package.title}]
+
+ return doc
diff --git a/openedx/core/djangoapps/content/search/tests/test_api.py b/openedx/core/djangoapps/content/search/tests/test_api.py
index e8616cee60..5498177003 100644
--- a/openedx/core/djangoapps/content/search/tests/test_api.py
+++ b/openedx/core/djangoapps/content/search/tests/test_api.py
@@ -6,12 +6,13 @@ from __future__ import annotations
import copy
from datetime import datetime, timezone
-from unittest.mock import MagicMock, call, patch
+from unittest.mock import MagicMock, Mock, call, patch
from opaque_keys.edx.keys import UsageKey
import ddt
from django.test import override_settings
from freezegun import freeze_time
+from openedx_learning.api import authoring as authoring_api
from organizations.tests.factories import OrganizationFactory
from common.djangoapps.student.tests.factories import UserFactory
@@ -174,6 +175,28 @@ class TestSearchApi(ModuleStoreTestCase):
tagging_api.add_tag_to_taxonomy(self.taxonomyB, "three")
tagging_api.add_tag_to_taxonomy(self.taxonomyB, "four")
+ # Create a collection:
+ self.learning_package = authoring_api.get_learning_package_by_key(self.library.key)
+ self.collection_dict = {
+ 'id': 1,
+ 'type': 'collection',
+ 'display_name': 'my_collection',
+ 'description': 'my collection description',
+ 'context_key': 'lib:org1:lib',
+ 'org': 'org1',
+ 'created': created_date.timestamp(),
+ 'modified': created_date.timestamp(),
+ "access_id": lib_access.id,
+ 'breadcrumbs': [{'display_name': 'Library'}]
+ }
+ with freeze_time(created_date):
+ self.collection = authoring_api.create_collection(
+ learning_package_id=self.learning_package.id,
+ title="my_collection",
+ created_by=None,
+ description="my collection description"
+ )
+
@override_settings(MEILISEARCH_ENABLED=False)
def test_reindex_meilisearch_disabled(self, mock_meilisearch):
with self.assertRaises(RuntimeError):
@@ -199,10 +222,27 @@ class TestSearchApi(ModuleStoreTestCase):
[
call([doc_sequential, doc_vertical]),
call([doc_problem1, doc_problem2]),
+ call([self.collection_dict]),
],
any_order=True,
)
+ @override_settings(MEILISEARCH_ENABLED=True)
+ @patch(
+ "openedx.core.djangoapps.content.search.api.searchable_doc_for_collection",
+ Mock(side_effect=Exception("Failed to generate document")),
+ )
+ def test_reindex_meilisearch_collection_error(self, mock_meilisearch):
+
+ mock_logger = Mock()
+ api.rebuild_index(mock_logger)
+ assert call(
+ [self.collection_dict]
+ ) not in mock_meilisearch.return_value.index.return_value.add_documents.mock_calls
+ mock_logger.assert_any_call(
+ f"Error indexing collection {self.collection}: Failed to generate document"
+ )
+
@override_settings(MEILISEARCH_ENABLED=True)
def test_reindex_meilisearch_library_block_error(self, mock_meilisearch):
diff --git a/openedx/core/djangoapps/content/search/tests/test_documents.py b/openedx/core/djangoapps/content/search/tests/test_documents.py
index e853fd4252..6140411705 100644
--- a/openedx/core/djangoapps/content/search/tests/test_documents.py
+++ b/openedx/core/djangoapps/content/search/tests/test_documents.py
@@ -1,8 +1,12 @@
"""
Tests for the Studio content search documents (what gets stored in the index)
"""
+from datetime import datetime, timezone
from organizations.models import Organization
+from freezegun import freeze_time
+from openedx_learning.api import authoring as authoring_api
+
from openedx.core.djangoapps.content_tagging import api as tagging_api
from openedx.core.djangolib.testing.utils import skip_unless_cms
from xmodule.modulestore.django import modulestore
@@ -11,10 +15,12 @@ from xmodule.modulestore.tests.factories import BlockFactory, ToyCourseFactory
try:
# This import errors in the lms because content.search is not an installed app there.
- from ..documents import searchable_doc_for_course_block, searchable_doc_tags
+ from ..documents import searchable_doc_for_course_block, searchable_doc_tags, searchable_doc_for_collection
from ..models import SearchAccess
except RuntimeError:
searchable_doc_for_course_block = lambda x: x
+ searchable_doc_tags = lambda x: x
+ searchable_doc_for_collection = lambda x: x
SearchAccess = {}
@@ -198,3 +204,30 @@ class StudioDocumentsTest(SharedModuleStoreTestCase):
"content": {},
# This video has no tags.
}
+
+ def test_collection_with_no_library(self):
+ created_date = datetime(2023, 4, 5, 6, 7, 8, tzinfo=timezone.utc)
+ with freeze_time(created_date):
+ learning_package = authoring_api.create_learning_package(
+ key="course-v1:edX+toy+2012_Fall",
+ title="some learning_package",
+ description="some description",
+ )
+ collection = authoring_api.create_collection(
+ learning_package_id=learning_package.id,
+ title="my_collection",
+ created_by=None,
+ description="my collection description"
+ )
+ doc = searchable_doc_for_collection(collection)
+ assert doc == {
+ "id": collection.id,
+ "type": "collection",
+ "display_name": collection.title,
+ "description": collection.description,
+ "context_key": learning_package.key,
+ "access_id": self.toy_course_access_id,
+ "breadcrumbs": [{"display_name": learning_package.title}],
+ "created": created_date.timestamp(),
+ "modified": created_date.timestamp(),
+ }
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index b7c7bebc71..8c7e19a18a 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -93,7 +93,7 @@ libsass==0.10.0
click==8.1.6
# pinning this version to avoid updates while the library is being developed
-openedx-learning==0.11.1
+openedx-learning==0.11.2
# Open AI version 1.0.0 dropped support for openai.ChatCompletion which is currently in use in enterprise.
openai<=0.28.1
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 1ed691bf5a..640ccbe982 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -823,7 +823,7 @@ openedx-filters==1.9.0
# -r requirements/edx/kernel.in
# lti-consumer-xblock
# ora2
-openedx-learning==0.11.1
+openedx-learning==0.11.2
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index 0f88bad4b5..9ae45b7ff7 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -1372,7 +1372,7 @@ openedx-filters==1.9.0
# -r requirements/edx/testing.txt
# lti-consumer-xblock
# ora2
-openedx-learning==0.11.1
+openedx-learning==0.11.2
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 663e09b3d6..d315955694 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -982,7 +982,7 @@ openedx-filters==1.9.0
# -r requirements/edx/base.txt
# lti-consumer-xblock
# ora2
-openedx-learning==0.11.1
+openedx-learning==0.11.2
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index e8ed020e51..fcb7db05a5 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -1033,7 +1033,7 @@ openedx-filters==1.9.0
# -r requirements/edx/base.txt
# lti-consumer-xblock
# ora2
-openedx-learning==0.11.1
+openedx-learning==0.11.2
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
From 60e4449f220c5078984c73b0397a89aaf193cef3 Mon Sep 17 00:00:00 2001
From: Muhammad Farhan Khan
Date: Wed, 28 Aug 2024 12:41:43 +0500
Subject: [PATCH 081/242] chore: Convert word cloud block sass variables to css
variables
---
xmodule/assets/word_cloud/_display.scss | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/xmodule/assets/word_cloud/_display.scss b/xmodule/assets/word_cloud/_display.scss
index 0b8940ab9a..4f7320380a 100644
--- a/xmodule/assets/word_cloud/_display.scss
+++ b/xmodule/assets/word_cloud/_display.scss
@@ -4,7 +4,7 @@
@import 'lms/theme/variables-v1';
.input-cloud {
- margin: ($baseline/4);
+ margin: calc((var(--baseline)/4));
}
.result_cloud_section {
From ea596d6a25e6608305be535e933146af015b4ec5 Mon Sep 17 00:00:00 2001
From: Blue
Date: Wed, 28 Aug 2024 14:58:14 +0500
Subject: [PATCH 082/242] fix: convert totalRegistrationTime to snake case
(#35333)
* fix: convert totalRegistrationTime to snake case
Description:
Convert totalRegistrationTime to snake case
VAN-1816
* fix: link issue
---------
Co-authored-by: Ahtesham Quraish
Co-authored-by: Syed Sajjad Hussain Shah
---
openedx/core/djangoapps/user_authn/views/register.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/openedx/core/djangoapps/user_authn/views/register.py b/openedx/core/djangoapps/user_authn/views/register.py
index 17ca1d4f2a..ab57687d2c 100644
--- a/openedx/core/djangoapps/user_authn/views/register.py
+++ b/openedx/core/djangoapps/user_authn/views/register.py
@@ -390,7 +390,9 @@ def _track_user_registration(user, profile, params, third_party_provider, regist
'is_year_of_birth_selected': bool(profile.year_of_birth),
'is_education_selected': bool(profile.level_of_education_display),
'is_goal_set': bool(profile.goals),
- 'total_registration_time': round(float(params.get('totalRegistrationTime', '0'))),
+ 'total_registration_time': round(
+ float(params.get('total_registration_time') or params.get('totalRegistrationTime') or 0)
+ ),
'activation_key': registration.activation_key if registration else None,
'host': params.get('host', ''),
'app_name': params.get('app_name', ''),
From 1c2b804ef7eb9d1ae98a01cb3bf33ced7c952149 Mon Sep 17 00:00:00 2001
From: Robert Raposa
Date: Thu, 29 Aug 2024 08:27:47 -0400
Subject: [PATCH 083/242] feat: DEPR USE-JWT-COOKIE header (#35393)
This repo is no longer using USE-JWT-COOKIE header,
since it has the required edx-drf-extensions>10.2.0,
where it was fully removed.
This is final clean-up for this repo.
See "[DEPR]: USE-JWT-COOKIE header" for more details:
- https://github.com/openedx/edx-drf-extensions/issues/371
---
cms/envs/common.py | 1 -
lms/envs/common.py | 4 +---
openedx/core/djangoapps/user_authn/tests/test_cookies.py | 5 -----
3 files changed, 1 insertion(+), 9 deletions(-)
diff --git a/cms/envs/common.py b/cms/envs/common.py
index 45a8e97f3e..dc94334c2e 100644
--- a/cms/envs/common.py
+++ b/cms/envs/common.py
@@ -2544,7 +2544,6 @@ if FEATURES.get('ENABLE_CORS_HEADERS'):
# because that decision might happen in a later config file. (The headers to
# allow is an application logic, and not site policy.)
CORS_ALLOW_HEADERS = corsheaders_default_headers + (
- 'use-jwt-cookie',
'content-range',
'content-disposition',
)
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 04a1753838..428c02951b 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -3686,9 +3686,7 @@ if FEATURES.get('ENABLE_CORS_HEADERS'):
# Set CORS_ALLOW_HEADERS regardless of whether we've enabled ENABLE_CORS_HEADERS
# because that decision might happen in a later config file. (The headers to
# allow is an application logic, and not site policy.)
-CORS_ALLOW_HEADERS = corsheaders_default_headers + (
- 'use-jwt-cookie',
-)
+CORS_ALLOW_HEADERS = corsheaders_default_headers
# Default cache expiration for the cross-domain proxy HTML page.
# This is a static page that can be iframed into an external page
diff --git a/openedx/core/djangoapps/user_authn/tests/test_cookies.py b/openedx/core/djangoapps/user_authn/tests/test_cookies.py
index a90f20f194..8a7841b3b9 100644
--- a/openedx/core/djangoapps/user_authn/tests/test_cookies.py
+++ b/openedx/core/djangoapps/user_authn/tests/test_cookies.py
@@ -74,9 +74,6 @@ class CookieTests(TestCase):
for key, val in response.cookies.items()
}
- def _set_use_jwt_cookie_header(self, request):
- request.META['HTTP_USE_JWT_COOKIE'] = 'true'
-
def _assert_recreate_jwt_from_cookies(self, response, can_recreate):
"""
If can_recreate is True, verifies that a JWT can be properly recreated
@@ -133,7 +130,6 @@ class CookieTests(TestCase):
@patch.dict("django.conf.settings.FEATURES", {"DISABLE_SET_JWT_COOKIES_FOR_TESTS": False})
def test_set_logged_in_jwt_cookies(self):
setup_login_oauth_client()
- self._set_use_jwt_cookie_header(self.request)
response = cookies_api.set_logged_in_cookies(self.request, HttpResponse(), self.user)
self._assert_cookies_present(response, cookies_api.ALL_LOGGED_IN_COOKIE_NAMES)
self._assert_consistent_expires(response, num_of_unique_expires=2)
@@ -153,7 +149,6 @@ class CookieTests(TestCase):
@patch.dict("django.conf.settings.FEATURES", {"DISABLE_SET_JWT_COOKIES_FOR_TESTS": False})
def test_refresh_jwt_cookies(self):
setup_login_oauth_client()
- self._set_use_jwt_cookie_header(self.request)
response = cookies_api.get_response_with_refreshed_jwt_cookies(self.request, self.user)
data = json.loads(response.content.decode('utf8').replace("'", '"'))
assert data['success'] is True
From 481a50717a34f6ed0241433b9d1db685f7d17b01 Mon Sep 17 00:00:00 2001
From: Muhammad Adeel Tajamul
<77053848+muhammadadeeltajamul@users.noreply.github.com>
Date: Thu, 29 Aug 2024 06:07:57 -0700
Subject: [PATCH 084/242] feat: updated content in course_update notification
(#35373)
---
.../contentstore/tests/test_utils.py | 34 +++++++++++++++++++
cms/djangoapps/contentstore/utils.py | 30 ++++++++++++++--
.../notifications/base_notification.py | 3 +-
3 files changed, 63 insertions(+), 4 deletions(-)
diff --git a/cms/djangoapps/contentstore/tests/test_utils.py b/cms/djangoapps/contentstore/tests/test_utils.py
index 450040c803..9c478ddfe5 100644
--- a/cms/djangoapps/contentstore/tests/test_utils.py
+++ b/cms/djangoapps/contentstore/tests/test_utils.py
@@ -9,6 +9,7 @@ import ddt
from django.conf import settings
from django.test import TestCase
from django.test.utils import override_settings
+from edx_toggles.toggles.testutils import override_waffle_flag
from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import CourseLocator, LibraryLocator
from path import Path as path
@@ -19,7 +20,11 @@ from user_tasks.models import UserTaskArtifact, UserTaskStatus
from cms.djangoapps.contentstore import utils
from cms.djangoapps.contentstore.tasks import ALL_ALLOWED_XBLOCKS, validate_course_olx
from cms.djangoapps.contentstore.tests.utils import TEST_DATA_DIR, CourseTestCase
+from cms.djangoapps.contentstore.utils import send_course_update_notification
+from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.tests.factories import GlobalStaffFactory, InstructorFactory, UserFactory
+from openedx.core.djangoapps.notifications.config.waffle import ENABLE_NOTIFICATIONS
+from openedx.core.djangoapps.notifications.models import CourseNotificationPreference, Notification
from openedx.core.djangoapps.site_configuration.tests.test_util import with_site_configuration_context
from xmodule.modulestore import ModuleStoreEnum # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order
@@ -927,3 +932,32 @@ class UpdateCourseDetailsTests(ModuleStoreTestCase):
utils.update_course_details(mock_request, self.course.id, payload, None)
mock_update.assert_called_once_with(self.course.id, payload, mock_request.user)
+
+
+@override_waffle_flag(ENABLE_NOTIFICATIONS, active=True)
+class CourseUpdateNotificationTests(ModuleStoreTestCase):
+ """
+ Unit tests for the course_update notification.
+ """
+
+ def setUp(self):
+ """
+ Setup the test environment.
+ """
+ super().setUp()
+ self.user = UserFactory()
+ self.course = CourseFactory.create(org='testorg', number='testcourse', run='testrun')
+ CourseNotificationPreference.objects.create(user_id=self.user.id, course_id=self.course.id)
+
+ def test_course_update_notification_sent(self):
+ """
+ Test that the course_update notification is sent.
+ """
+ user = UserFactory()
+ CourseEnrollment.enroll(user=user, course_key=self.course.id)
+ assert Notification.objects.all().count() == 0
+ content = "content
"
+ send_course_update_notification(self.course.id, content, self.user)
+ assert Notification.objects.all().count() == 1
+ notification = Notification.objects.first()
+ assert notification.content == "content
"
diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py
index 631ceeb270..17e94112ba 100644
--- a/cms/djangoapps/contentstore/utils.py
+++ b/cms/djangoapps/contentstore/utils.py
@@ -11,16 +11,19 @@ from datetime import datetime, timezone
from urllib.parse import quote_plus
from uuid import uuid4
+from bs4 import BeautifulSoup
from django.conf import settings
from django.core.exceptions import ValidationError
from django.urls import reverse
from django.utils import translation
+from django.utils.text import Truncator
from django.utils.translation import gettext as _
from eventtracking import tracker
from help_tokens.core import HelpUrlExpert
from lti_consumer.models import CourseAllowPIISharingInLTIFlag
from opaque_keys.edx.keys import CourseKey, UsageKey
from opaque_keys.edx.locator import LibraryLocator
+
from openedx.core.lib.teams_config import CONTENT_GROUPS_FOR_TEAMS, TEAM_SCHEME
from openedx_events.content_authoring.data import DuplicatedXBlockData
from openedx_events.content_authoring.signals import XBLOCK_DUPLICATED
@@ -2239,11 +2242,34 @@ def track_course_update_event(course_key, user, course_update_content=None):
tracker.emit(event_name, event_data)
+def clean_html_body(html_body):
+ """
+ Get html body, remove tags and limit to 500 characters
+ """
+ html_body = BeautifulSoup(Truncator(html_body).chars(500, html=True), 'html.parser')
+
+ tags_to_remove = [
+ "a", "link", # Link Tags
+ "img", "picture", "source", # Image Tags
+ "video", "track", # Video Tags
+ "audio", # Audio Tags
+ "embed", "object", "iframe", # Embedded Content
+ "script"
+ ]
+
+ # Remove the specified tags while keeping their content
+ for tag in tags_to_remove:
+ for match in html_body.find_all(tag):
+ match.unwrap()
+
+ return str(html_body)
+
+
def send_course_update_notification(course_key, content, user):
"""
Send course update notification
"""
- text_content = re.sub(r"(\s| |//)+", " ", html_to_text(content))
+ text_content = re.sub(r"(\s| |//)+", " ", clean_html_body(content))
course = modulestore().get_course(course_key)
extra_context = {
'author_id': user.id,
@@ -2252,7 +2278,7 @@ def send_course_update_notification(course_key, content, user):
notification_data = CourseNotificationData(
course_key=course_key,
content_context={
- "course_update_content": text_content if len(text_content.strip()) < 10 else "Click here to view",
+ "course_update_content": text_content,
**extra_context,
},
notification_type="course_updates",
diff --git a/openedx/core/djangoapps/notifications/base_notification.py b/openedx/core/djangoapps/notifications/base_notification.py
index 2c696ec60d..a417d45405 100644
--- a/openedx/core/djangoapps/notifications/base_notification.py
+++ b/openedx/core/djangoapps/notifications/base_notification.py
@@ -181,8 +181,7 @@ COURSE_NOTIFICATION_TYPES = {
'push': True,
'email_cadence': EmailCadence.DAILY,
'non_editable': [],
- 'content_template': _('<{p}>You have a new course update: '
- '<{strong}>{course_update_content}{strong}>{p}>'),
+ 'content_template': _('<{p}><{strong}>{course_update_content}{strong}>{p}>'),
'content_context': {
'course_update_content': 'Course update',
},
From b323e65eebe339b0a9c4386e5504f7b411a0dba5 Mon Sep 17 00:00:00 2001
From: Muhammad Adeel Tajamul
<77053848+muhammadadeeltajamul@users.noreply.github.com>
Date: Thu, 29 Aug 2024 06:08:20 -0700
Subject: [PATCH 085/242] feat: added content block in email notification
template (#35391)
---
.../djangoapps/notifications/email/utils.py | 15 +++++++++++++++
.../notifications/digest_content.html | 19 ++++++++++---------
2 files changed, 25 insertions(+), 9 deletions(-)
diff --git a/openedx/core/djangoapps/notifications/email/utils.py b/openedx/core/djangoapps/notifications/email/utils.py
index e36c435e8a..582e867d62 100644
--- a/openedx/core/djangoapps/notifications/email/utils.py
+++ b/openedx/core/djangoapps/notifications/email/utils.py
@@ -4,6 +4,7 @@ Email Notifications Utils
import datetime
import json
+from bs4 import BeautifulSoup
from django.conf import settings
from django.contrib.auth import get_user_model
from django.shortcuts import get_object_or_404
@@ -195,6 +196,18 @@ def get_time_ago(datetime_obj):
return f"{days_diff}d"
+def add_zero_margin_to_root(html_string):
+ """
+ Adds to zero margin to root element of html string
+ """
+ soup = BeautifulSoup(html_string, 'html.parser')
+ element = soup.find()
+ if not element:
+ return html_string
+ element['style'] = "margin: 0;"
+ return str(soup)
+
+
def add_additional_attributes_to_notifications(notifications, courses_data=None):
"""
Add attributes required for email content to notification instance
@@ -214,6 +227,8 @@ def add_additional_attributes_to_notifications(notifications, courses_data=None)
notification.course_name = course_info.get('name', '')
notification.icon = get_icon_url_for_notification_type(notification_type)
notification.time_ago = get_time_ago(notification.created)
+ notification.email_content = add_zero_margin_to_root(notification.content)
+ notification.details = add_zero_margin_to_root(notification.content_context.get('email_content', ''))
return notifications
diff --git a/openedx/core/djangoapps/notifications/templates/notifications/digest_content.html b/openedx/core/djangoapps/notifications/templates/notifications/digest_content.html
index 13ac89d4ec..f2e239bb7e 100644
--- a/openedx/core/djangoapps/notifications/templates/notifications/digest_content.html
+++ b/openedx/core/djangoapps/notifications/templates/notifications/digest_content.html
@@ -1,6 +1,3 @@
-
{% for notification_app in email_content %}
{{ notification_app.title }}
@@ -32,11 +29,15 @@
/>
-
- {{ notification.content | truncatechars_html:600 | safe }}
-
-
-
+
+ {{ notification.email_content | truncatechars_html:600 | safe }}
+
+ {% if notification.details %}
+
+ {{ notification.details | safe }}
+
+ {% endif %}
+
{{ notification.course_name }}
{{ "·"|safe }}
@@ -47,7 +48,7 @@
View
-
+
{% endfor %}
From 1a431b6a26b8659ef267aa06660716e690286834 Mon Sep 17 00:00:00 2001
From: Awais Qureshi
Date: Thu, 29 Aug 2024 18:37:17 +0500
Subject: [PATCH 086/242] feat: get_students_who_may_enroll update api to DRF (
8th ) (#35174)
* feat: upgrading simple api to drf compatible.
---
lms/djangoapps/instructor/views/api.py | 47 +++++++++++++--------
lms/djangoapps/instructor/views/api_urls.py | 2 +-
2 files changed, 30 insertions(+), 19 deletions(-)
diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py
index 1aa40b5e33..0255e26e2a 100644
--- a/lms/djangoapps/instructor/views/api.py
+++ b/lms/djangoapps/instructor/views/api.py
@@ -37,6 +37,7 @@ from edx_when.api import get_date_for_block
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey, UsageKey
from openedx.core.djangoapps.course_groups.cohorts import get_cohort_by_name
+from rest_framework.exceptions import MethodNotAllowed
from rest_framework import serializers, status # lint-amnesty, pylint: disable=wrong-import-order
from rest_framework.permissions import IsAdminUser, IsAuthenticated # lint-amnesty, pylint: disable=wrong-import-order
from rest_framework.response import Response # lint-amnesty, pylint: disable=wrong-import-order
@@ -1510,28 +1511,38 @@ def get_students_features(request, course_id, csv=False): # pylint: disable=red
return JsonResponse({"status": success_status})
-@transaction.non_atomic_requests
-@require_POST
-@ensure_csrf_cookie
-@cache_control(no_cache=True, no_store=True, must_revalidate=True)
-@require_course_permission(permissions.CAN_RESEARCH)
-@common_exceptions_400
-def get_students_who_may_enroll(request, course_id):
+@method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True), name='dispatch')
+@method_decorator(transaction.non_atomic_requests, name='dispatch')
+class GetStudentsWhoMayEnroll(DeveloperErrorViewMixin, APIView):
"""
Initiate generation of a CSV file containing information about
- students who may enroll in a course.
-
- Responds with JSON
- {"status": "... status message ..."}
-
"""
- course_key = CourseKey.from_string(course_id)
- query_features = ['email']
- report_type = _('enrollment')
- task_api.submit_calculate_may_enroll_csv(request, course_key, query_features)
- success_status = SUCCESS_MESSAGE_TEMPLATE.format(report_type=report_type)
+ permission_classes = (IsAuthenticated, permissions.InstructorPermission)
+ permission_name = permissions.CAN_RESEARCH
- return JsonResponse({"status": success_status})
+ @method_decorator(ensure_csrf_cookie)
+ @method_decorator(transaction.non_atomic_requests)
+ def post(self, request, course_id):
+ """
+ Initiate generation of a CSV file containing information about
+ students who may enroll in a course.
+
+ Responds with JSON
+ {"status": "... status message ..."}
+ """
+ course_key = CourseKey.from_string(course_id)
+ query_features = ['email']
+ report_type = _('enrollment')
+ try:
+ task_api.submit_calculate_may_enroll_csv(request, course_key, query_features)
+ success_status = SUCCESS_MESSAGE_TEMPLATE.format(report_type=report_type)
+ except Exception as e:
+ raise self.api_error(status.HTTP_400_BAD_REQUEST, str(e), 'Requested task is already running')
+
+ return JsonResponse({"status": success_status})
+
+ def get(self, request, *args, **kwargs):
+ raise MethodNotAllowed('GET')
def _cohorts_csv_validator(file_storage, file_to_validate):
diff --git a/lms/djangoapps/instructor/views/api_urls.py b/lms/djangoapps/instructor/views/api_urls.py
index 18da0b63b2..f25ea56c2e 100644
--- a/lms/djangoapps/instructor/views/api_urls.py
+++ b/lms/djangoapps/instructor/views/api_urls.py
@@ -30,7 +30,7 @@ urlpatterns = [
path('get_grading_config', api.get_grading_config, name='get_grading_config'),
re_path(r'^get_students_features(?P/csv)?$', api.get_students_features, name='get_students_features'),
path('get_issued_certificates/', api.get_issued_certificates, name='get_issued_certificates'),
- path('get_students_who_may_enroll', api.get_students_who_may_enroll, name='get_students_who_may_enroll'),
+ path('get_students_who_may_enroll', api.GetStudentsWhoMayEnroll.as_view(), name='get_students_who_may_enroll'),
path('get_anon_ids', api.get_anon_ids, name='get_anon_ids'),
path('get_student_enrollment_status', api.get_student_enrollment_status, name="get_student_enrollment_status"),
path('get_student_progress_url', api.StudentProgressUrl.as_view(), name='get_student_progress_url'),
From 5323c55abcebb90f0b2f03f1fc53f020df96595a Mon Sep 17 00:00:00 2001
From: Muhammad Anas <88967643+Anas12091101@users.noreply.github.com>
Date: Thu, 29 Aug 2024 18:47:54 +0500
Subject: [PATCH 087/242] fix: renamed DatabaseNotReadyError (#35390)
---
lms/djangoapps/grades/exceptions.py | 6 +++---
lms/djangoapps/grades/tasks.py | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/lms/djangoapps/grades/exceptions.py b/lms/djangoapps/grades/exceptions.py
index d615f1f64d..db2793efaa 100644
--- a/lms/djangoapps/grades/exceptions.py
+++ b/lms/djangoapps/grades/exceptions.py
@@ -3,9 +3,9 @@ Custom exceptions raised by grades.
"""
-class DatabaseNotReadyError(IOError):
+class ScoreNotFoundError(IOError):
"""
- Subclass of IOError to indicate the database has not yet committed
- the data we're trying to find.
+ Subclass of IOError to indicate the staff has not yet graded the problem or
+ the database has not yet committed the data we're trying to find.
"""
pass # lint-amnesty, pylint: disable=unnecessary-pass
diff --git a/lms/djangoapps/grades/tasks.py b/lms/djangoapps/grades/tasks.py
index 3b504e61eb..9ec237274b 100644
--- a/lms/djangoapps/grades/tasks.py
+++ b/lms/djangoapps/grades/tasks.py
@@ -33,7 +33,7 @@ from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disa
from .config.waffle import DISABLE_REGRADE_ON_POLICY_CHANGE
from .constants import ScoreDatabaseTableEnum
from .course_grade_factory import CourseGradeFactory
-from .exceptions import DatabaseNotReadyError
+from .exceptions import ScoreNotFoundError
from .grade_utils import are_grades_frozen
from .signals.signals import SUBSECTION_SCORE_CHANGED
from .subsection_grade_factory import SubsectionGradeFactory
@@ -45,7 +45,7 @@ COURSE_GRADE_TIMEOUT_SECONDS = 1200
KNOWN_RETRY_ERRORS = ( # Errors we expect occasionally, should be resolved on retry
DatabaseError,
ValidationError,
- DatabaseNotReadyError,
+ ScoreNotFoundError,
UsageKeyNotInBlockStructure,
)
RECALCULATE_GRADE_DELAY_SECONDS = 2 # to prevent excessive _has_db_updated failures. See TNL-6424.
@@ -239,7 +239,7 @@ def _recalculate_subsection_grade(self, **kwargs):
has_database_updated = _has_db_updated_with_new_score(self, scored_block_usage_key, **kwargs)
if not has_database_updated:
- raise DatabaseNotReadyError
+ raise ScoreNotFoundError
_update_subsection_grades(
course_key,
From 989a51316d7c7e46349c749cd1f4f88ff705e22d Mon Sep 17 00:00:00 2001
From: sameeramin <35958006+sameeramin@users.noreply.github.com>
Date: Thu, 29 Aug 2024 15:19:15 +0000
Subject: [PATCH 088/242] feat: Upgrade Python dependency edx-enterprise
populate encrypted client id and secret
Commit generated by workflow `openedx/edx-platform/.github/workflows/upgrade-one-python-dependency.yml@refs/heads/master`
---
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 8c7e19a18a..4a2f87e652 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -26,7 +26,7 @@ celery>=5.2.2,<6.0.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==4.23.13
+edx-enterprise==4.23.14
# Stay on LTS version, remove once this is added to common constraint
Django<5.0
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 640ccbe982..1c612fab67 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -467,7 +467,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.13
+edx-enterprise==4.23.14
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index 9ae45b7ff7..e6fd96e3be 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -741,7 +741,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.13
+edx-enterprise==4.23.14
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index d315955694..c3dcafee93 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -547,7 +547,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.13
+edx-enterprise==4.23.14
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index fcb7db05a5..925fe517c8 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -571,7 +571,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.13
+edx-enterprise==4.23.14
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
From 01c718dfb2621b4c5575c377a51538a6fa52e94e Mon Sep 17 00:00:00 2001
From: Robert Raposa
Date: Thu, 29 Aug 2024 11:23:05 -0400
Subject: [PATCH 089/242] Revert "feat: DEPR USE-JWT-COOKIE header (#35393)"
(#35397)
This reverts commit 1c2b804ef7eb9d1ae98a01cb3bf33ced7c952149.
---
cms/envs/common.py | 1 +
lms/envs/common.py | 4 +++-
openedx/core/djangoapps/user_authn/tests/test_cookies.py | 5 +++++
3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/cms/envs/common.py b/cms/envs/common.py
index dc94334c2e..45a8e97f3e 100644
--- a/cms/envs/common.py
+++ b/cms/envs/common.py
@@ -2544,6 +2544,7 @@ if FEATURES.get('ENABLE_CORS_HEADERS'):
# because that decision might happen in a later config file. (The headers to
# allow is an application logic, and not site policy.)
CORS_ALLOW_HEADERS = corsheaders_default_headers + (
+ 'use-jwt-cookie',
'content-range',
'content-disposition',
)
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 428c02951b..04a1753838 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -3686,7 +3686,9 @@ if FEATURES.get('ENABLE_CORS_HEADERS'):
# Set CORS_ALLOW_HEADERS regardless of whether we've enabled ENABLE_CORS_HEADERS
# because that decision might happen in a later config file. (The headers to
# allow is an application logic, and not site policy.)
-CORS_ALLOW_HEADERS = corsheaders_default_headers
+CORS_ALLOW_HEADERS = corsheaders_default_headers + (
+ 'use-jwt-cookie',
+)
# Default cache expiration for the cross-domain proxy HTML page.
# This is a static page that can be iframed into an external page
diff --git a/openedx/core/djangoapps/user_authn/tests/test_cookies.py b/openedx/core/djangoapps/user_authn/tests/test_cookies.py
index 8a7841b3b9..a90f20f194 100644
--- a/openedx/core/djangoapps/user_authn/tests/test_cookies.py
+++ b/openedx/core/djangoapps/user_authn/tests/test_cookies.py
@@ -74,6 +74,9 @@ class CookieTests(TestCase):
for key, val in response.cookies.items()
}
+ def _set_use_jwt_cookie_header(self, request):
+ request.META['HTTP_USE_JWT_COOKIE'] = 'true'
+
def _assert_recreate_jwt_from_cookies(self, response, can_recreate):
"""
If can_recreate is True, verifies that a JWT can be properly recreated
@@ -130,6 +133,7 @@ class CookieTests(TestCase):
@patch.dict("django.conf.settings.FEATURES", {"DISABLE_SET_JWT_COOKIES_FOR_TESTS": False})
def test_set_logged_in_jwt_cookies(self):
setup_login_oauth_client()
+ self._set_use_jwt_cookie_header(self.request)
response = cookies_api.set_logged_in_cookies(self.request, HttpResponse(), self.user)
self._assert_cookies_present(response, cookies_api.ALL_LOGGED_IN_COOKIE_NAMES)
self._assert_consistent_expires(response, num_of_unique_expires=2)
@@ -149,6 +153,7 @@ class CookieTests(TestCase):
@patch.dict("django.conf.settings.FEATURES", {"DISABLE_SET_JWT_COOKIES_FOR_TESTS": False})
def test_refresh_jwt_cookies(self):
setup_login_oauth_client()
+ self._set_use_jwt_cookie_header(self.request)
response = cookies_api.get_response_with_refreshed_jwt_cookies(self.request, self.user)
data = json.loads(response.content.decode('utf8').replace("'", '"'))
assert data['success'] is True
From ede9102e331c69968164eb5616b47ae91d296e7f Mon Sep 17 00:00:00 2001
From: Awais Qureshi
Date: Fri, 30 Aug 2024 16:19:48 +0500
Subject: [PATCH 090/242] feat: upgrading get_anon_ids api to drf compatible
(12) (#35345)
* feat: upgrading simple api to drf compatible.
---
lms/djangoapps/instructor/views/api.py | 33 ++++++++++++++-------
lms/djangoapps/instructor/views/api_urls.py | 2 +-
2 files changed, 24 insertions(+), 11 deletions(-)
diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py
index 0255e26e2a..d9a301b07e 100644
--- a/lms/djangoapps/instructor/views/api.py
+++ b/lms/djangoapps/instructor/views/api.py
@@ -1674,18 +1674,31 @@ def get_proctored_exam_results(request, course_id):
return JsonResponse({"status": success_status})
-@transaction.non_atomic_requests
-@ensure_csrf_cookie
-@cache_control(no_cache=True, no_store=True, must_revalidate=True)
-@require_course_permission(permissions.CAN_RESEARCH)
-def get_anon_ids(request, course_id):
+@method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True), name='dispatch')
+@method_decorator(transaction.non_atomic_requests, name='dispatch')
+class GetAnonIds(APIView):
"""
- Respond with 2-column CSV output of user-id, anonymized-user-id
+ Respond with 2-column CSV output of user-id, anonymized-user-id.
+ This API processes the incoming request to generate a CSV file containing
+ two columns: `user-id` and `anonymized-user-id`. The CSV is returned as a
+ response to the client.
"""
- report_type = _('Anonymized User IDs')
- success_status = SUCCESS_MESSAGE_TEMPLATE.format(report_type=report_type)
- task_api.generate_anonymous_ids(request, course_id)
- return JsonResponse({"status": success_status})
+ permission_classes = (IsAuthenticated, permissions.InstructorPermission)
+ permission_name = permissions.CAN_RESEARCH
+
+ @method_decorator(ensure_csrf_cookie)
+ @method_decorator(transaction.non_atomic_requests)
+ def post(self, request, course_id):
+ """
+ Handle POST request to generate a CSV output.
+
+ Returns:
+ Response: A CSV file with two columns: `user-id` and `anonymized-user-id`.
+ """
+ report_type = _('Anonymized User IDs')
+ success_status = SUCCESS_MESSAGE_TEMPLATE.format(report_type=report_type)
+ task_api.generate_anonymous_ids(request, course_id)
+ return JsonResponse({"status": success_status})
@require_POST
diff --git a/lms/djangoapps/instructor/views/api_urls.py b/lms/djangoapps/instructor/views/api_urls.py
index f25ea56c2e..14fe15c83c 100644
--- a/lms/djangoapps/instructor/views/api_urls.py
+++ b/lms/djangoapps/instructor/views/api_urls.py
@@ -31,7 +31,7 @@ urlpatterns = [
re_path(r'^get_students_features(?P/csv)?$', api.get_students_features, name='get_students_features'),
path('get_issued_certificates/', api.get_issued_certificates, name='get_issued_certificates'),
path('get_students_who_may_enroll', api.GetStudentsWhoMayEnroll.as_view(), name='get_students_who_may_enroll'),
- path('get_anon_ids', api.get_anon_ids, name='get_anon_ids'),
+ path('get_anon_ids', api.GetAnonIds.as_view(), name='get_anon_ids'),
path('get_student_enrollment_status', api.get_student_enrollment_status, name="get_student_enrollment_status"),
path('get_student_progress_url', api.StudentProgressUrl.as_view(), name='get_student_progress_url'),
path('reset_student_attempts', api.reset_student_attempts, name='reset_student_attempts'),
From ecd31b32a6281b6ad9fd1b4dc5b48a166b8cefdc Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Fri, 30 Aug 2024 11:20:58 +0000
Subject: [PATCH 091/242] fix(deps): update dependency webpack to v5.94.0
[security]
---
package-lock.json | 50 ++++++++++++++++++-----------------------------
1 file changed, 19 insertions(+), 31 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index c2a83bf7a6..87c75af690 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4223,24 +4223,6 @@
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
"integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow=="
},
- "node_modules/@types/eslint": {
- "version": "8.56.6",
- "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.6.tgz",
- "integrity": "sha512-ymwc+qb1XkjT/gfoQwxIeHZ6ixH23A+tCT2ADSA/DPVKzAjwYkTXBMCQ/f6fe4wEa85Lhp26VPeUxI7wMhAi7A==",
- "dependencies": {
- "@types/estree": "*",
- "@types/json-schema": "*"
- }
- },
- "node_modules/@types/eslint-scope": {
- "version": "3.7.7",
- "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
- "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
- "dependencies": {
- "@types/eslint": "*",
- "@types/estree": "*"
- }
- },
"node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
@@ -4628,10 +4610,11 @@
"node": ">=0.4.0"
}
},
- "node_modules/acorn-import-assertions": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
- "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==",
+ "node_modules/acorn-import-attributes": {
+ "version": "1.9.5",
+ "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
+ "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
+ "license": "MIT",
"peerDependencies": {
"acorn": "^8"
}
@@ -25127,20 +25110,20 @@
}
},
"node_modules/webpack": {
- "version": "5.91.0",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz",
- "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==",
+ "version": "5.94.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz",
+ "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
+ "license": "MIT",
"dependencies": {
- "@types/eslint-scope": "^3.7.3",
"@types/estree": "^1.0.5",
"@webassemblyjs/ast": "^1.12.1",
"@webassemblyjs/wasm-edit": "^1.12.1",
"@webassemblyjs/wasm-parser": "^1.12.1",
"acorn": "^8.7.1",
- "acorn-import-assertions": "^1.9.0",
+ "acorn-import-attributes": "^1.9.5",
"browserslist": "^4.21.10",
"chrome-trace-event": "^1.0.2",
- "enhanced-resolve": "^5.16.0",
+ "enhanced-resolve": "^5.17.1",
"es-module-lexer": "^1.2.1",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
@@ -25295,9 +25278,10 @@
}
},
"node_modules/webpack/node_modules/enhanced-resolve": {
- "version": "5.16.0",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz",
- "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==",
+ "version": "5.17.1",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
+ "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
+ "license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@@ -25310,6 +25294,7 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "license": "BSD-2-Clause",
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^4.1.1"
@@ -25322,6 +25307,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
}
@@ -25330,6 +25316,7 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
"integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
+ "license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.8",
"ajv": "^6.12.5",
@@ -25347,6 +25334,7 @@
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
From 638bc5dd8526ba139f94e6c869d80f87f88bd0df Mon Sep 17 00:00:00 2001
From: Juliana Kang
Date: Fri, 30 Aug 2024 09:20:01 -0400
Subject: [PATCH 092/242] chore: Update LMS openapi spec yml file (#35400)
---
docs/lms-openapi.yaml | 141 +++++++++++++++++++-----------------------
1 file changed, 65 insertions(+), 76 deletions(-)
diff --git a/docs/lms-openapi.yaml b/docs/lms-openapi.yaml
index 38c52e737e..9fa03436e1 100644
--- a/docs/lms-openapi.yaml
+++ b/docs/lms-openapi.yaml
@@ -3461,29 +3461,6 @@ paths:
in: path
required: true
type: string
- /demographics/v1/demographics/status/:
- get:
- operationId: demographics_v1_demographics_status_list
- summary: GET /api/user/v1/accounts/demographics/status
- description: This is a Web API to determine the status of demographics related
- features
- parameters: []
- responses:
- '200':
- description: ''
- tags:
- - demographics
- patch:
- operationId: demographics_v1_demographics_status_partial_update
- summary: PATCH /api/user/v1/accounts/demographics/status
- description: This is a Web API to update fields that are dependent on user interaction.
- parameters: []
- responses:
- '200':
- description: ''
- tags:
- - demographics
- parameters: []
/discounts/course/{course_key_string}:
get:
operationId: discounts_course_read
@@ -6649,6 +6626,11 @@ paths:
course, chapter, sequential, vertical, html, problem, video, and
discussion.
display_name: (str) The display name of the block.
+ course_progress: (dict) Contains information about how many assignments are in the course
+ and how many assignments the student has completed.
+ Included here:
+ * total_assignments_count: (int) Total course's assignments count.
+ * assignments_completed: (int) Assignments witch the student has completed.
**Returns**
@@ -6696,6 +6678,26 @@ paths:
in: path
required: true
type: string
+ /mobile/{api_version}/course_info/{course_id}/enrollment_details:
+ get:
+ operationId: mobile_course_info_enrollment_details_list
+ summary: Handle the GET request
+ description: Returns user enrollment and course details.
+ parameters: []
+ responses:
+ '200':
+ description: ''
+ tags:
+ - mobile
+ parameters:
+ - name: api_version
+ in: path
+ required: true
+ type: string
+ - name: course_id
+ in: path
+ required: true
+ type: string
/mobile/{api_version}/course_info/{course_id}/handouts:
get:
operationId: mobile_course_info_handouts_list
@@ -6861,6 +6863,10 @@ paths:
An additional attribute "expiration" has been added to the response, which lists the date
when access to the course will expire or null if it doesn't expire.
+ In v4 we added to the response primary object. Primary object contains the latest user's enrollment
+ or course where user has the latest progress. Primary object has been cut from user's
+ enrolments array and inserted into separated section with key `primary`.
+
**Example Request**
GET /api/mobile/v1/users/{username}/course_enrollments/
@@ -6910,14 +6916,14 @@ paths:
* mode: The type of certificate registration for this course (honor or
certified).
* url: URL to the downloadable version of the certificate, if exists.
+ * course_progress: Contains information about how many assignments are in the course
+ and how many assignments the student has completed.
+ * total_assignments_count: Total course's assignments count.
+ * assignments_completed: Assignments witch the student has completed.
parameters: []
responses:
'200':
description: ''
- schema:
- type: array
- items:
- $ref: '#/definitions/CourseEnrollment'
tags:
- mobile
parameters:
@@ -7031,22 +7037,6 @@ paths:
tags:
- notifications
parameters: []
- /notifications/channel/configurations/{course_key_string}:
- patch:
- operationId: notifications_channel_configurations_partial_update
- description: Update an existing user notification preference for an entire channel
- with the data in the request body.
- parameters: []
- responses:
- '200':
- description: ''
- tags:
- - notifications
- parameters:
- - name: course_key_string
- in: path
- required: true
- type: string
/notifications/configurations/{course_key_string}:
get:
operationId: notifications_configurations_read
@@ -7222,6 +7212,38 @@ paths:
in: path
required: true
type: string
+ /notifications/preferences/update/{username}/{patch}/:
+ get:
+ operationId: notifications_preferences_update_read
+ description: |-
+ View to update user preferences from encrypted username and patch.
+ username and patch must be string
+ parameters: []
+ responses:
+ '200':
+ description: ''
+ tags:
+ - notifications
+ post:
+ operationId: notifications_preferences_update_create
+ description: |-
+ View to update user preferences from encrypted username and patch.
+ username and patch must be string
+ parameters: []
+ responses:
+ '201':
+ description: ''
+ tags:
+ - notifications
+ parameters:
+ - name: username
+ in: path
+ required: true
+ type: string
+ - name: patch
+ in: path
+ required: true
+ type: string
/notifications/read/:
patch:
operationId: notifications_read_partial_update
@@ -11731,39 +11753,6 @@ definitions:
title: Course enrollments
type: string
readOnly: true
- CourseEnrollment:
- type: object
- properties:
- audit_access_expires:
- title: Audit access expires
- type: string
- readOnly: true
- created:
- title: Created
- type: string
- format: date-time
- readOnly: true
- x-nullable: true
- mode:
- title: Mode
- type: string
- maxLength: 100
- minLength: 1
- is_active:
- title: Is active
- type: boolean
- course:
- title: Course
- type: string
- readOnly: true
- certificate:
- title: Certificate
- type: string
- readOnly: true
- course_modes:
- title: Course modes
- type: string
- readOnly: true
Notification:
required:
- app_name
From 311da82ff9909dcbf71ffbf0b697e045ede0f894 Mon Sep 17 00:00:00 2001
From: Robert Raposa
Date: Fri, 30 Aug 2024 10:40:21 -0400
Subject: [PATCH 093/242] feat: DEPR USE-JWT-COOKIE header - Part 1 (#35401)
This repo is no longer using USE-JWT-COOKIE header,
since it has the required edx-drf-extensions>10.2.0,
where it was fully removed.
This removes all uses of the header, except updating
CORS_ALLOW_HEADERS, which can't be done before all
MFEs and other callers stop sending the header.
See "[DEPR]: USE-JWT-COOKIE header" for more details:
- https://github.com/openedx/edx-drf-extensions/issues/371
---
openedx/core/djangoapps/user_authn/tests/test_cookies.py | 5 -----
1 file changed, 5 deletions(-)
diff --git a/openedx/core/djangoapps/user_authn/tests/test_cookies.py b/openedx/core/djangoapps/user_authn/tests/test_cookies.py
index a90f20f194..8a7841b3b9 100644
--- a/openedx/core/djangoapps/user_authn/tests/test_cookies.py
+++ b/openedx/core/djangoapps/user_authn/tests/test_cookies.py
@@ -74,9 +74,6 @@ class CookieTests(TestCase):
for key, val in response.cookies.items()
}
- def _set_use_jwt_cookie_header(self, request):
- request.META['HTTP_USE_JWT_COOKIE'] = 'true'
-
def _assert_recreate_jwt_from_cookies(self, response, can_recreate):
"""
If can_recreate is True, verifies that a JWT can be properly recreated
@@ -133,7 +130,6 @@ class CookieTests(TestCase):
@patch.dict("django.conf.settings.FEATURES", {"DISABLE_SET_JWT_COOKIES_FOR_TESTS": False})
def test_set_logged_in_jwt_cookies(self):
setup_login_oauth_client()
- self._set_use_jwt_cookie_header(self.request)
response = cookies_api.set_logged_in_cookies(self.request, HttpResponse(), self.user)
self._assert_cookies_present(response, cookies_api.ALL_LOGGED_IN_COOKIE_NAMES)
self._assert_consistent_expires(response, num_of_unique_expires=2)
@@ -153,7 +149,6 @@ class CookieTests(TestCase):
@patch.dict("django.conf.settings.FEATURES", {"DISABLE_SET_JWT_COOKIES_FOR_TESTS": False})
def test_refresh_jwt_cookies(self):
setup_login_oauth_client()
- self._set_use_jwt_cookie_header(self.request)
response = cookies_api.get_response_with_refreshed_jwt_cookies(self.request, self.user)
data = json.loads(response.content.decode('utf8').replace("'", '"'))
assert data['success'] is True
From 92cc3bf8083116ddc3ff290e5b0fda0caca1f8e4 Mon Sep 17 00:00:00 2001
From: Zachary Hancock
Date: Fri, 30 Aug 2024 10:59:18 -0400
Subject: [PATCH 094/242] style: constrain exam iframe to avoid scroll problems
(#35396)
---
.../instructor/instructor_dashboard_2/special_exams.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lms/templates/instructor/instructor_dashboard_2/special_exams.html b/lms/templates/instructor/instructor_dashboard_2/special_exams.html
index 194c0cdcb0..2658af0bc7 100644
--- a/lms/templates/instructor/instructor_dashboard_2/special_exams.html
+++ b/lms/templates/instructor/instructor_dashboard_2/special_exams.html
@@ -7,7 +7,7 @@ import pytz
% if section_data.get('mfe_view_url'):
-
+
% else:
% if section_data.get('escalation_email'):
From bfee53b9b1f7497399add0f1209c15f5bf55b810 Mon Sep 17 00:00:00 2001
From: Braden MacDonald
Date: Fri, 30 Aug 2024 11:53:35 -0700
Subject: [PATCH 095/242] feat: Update CORS allowed headers for compat. w/
axios-cache-interceptor (#35402)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
See https://axios-cache-interceptor.js.org/config/request-specifics#cache-cachetakeover
cache.cacheTakeover (default true): you need to make sure Cache-Control,
Pragma and Expires headers are included into your server’s
Access-Control-Allow-Headers CORS configuration.
---
lms/envs/common.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 04a1753838..2d31cba982 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -3687,6 +3687,9 @@ if FEATURES.get('ENABLE_CORS_HEADERS'):
# because that decision might happen in a later config file. (The headers to
# allow is an application logic, and not site policy.)
CORS_ALLOW_HEADERS = corsheaders_default_headers + (
+ 'cache-control',
+ 'expires',
+ 'pragma',
'use-jwt-cookie',
)
From ec34753043964df9427224e6aaad96c5f8c951a3 Mon Sep 17 00:00:00 2001
From: Navin Karkera
Date: Sat, 31 Aug 2024 00:46:02 +0530
Subject: [PATCH 096/242] fix: update library v2 search index synchronously
(#35367)
Discarded components need to be removed from index in sync to make sure
that users see the latest updated data.
---
openedx/core/djangoapps/content/search/handlers.py | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/openedx/core/djangoapps/content/search/handlers.py b/openedx/core/djangoapps/content/search/handlers.py
index ba0e8c1a16..94ac02ea16 100644
--- a/openedx/core/djangoapps/content/search/handlers.py
+++ b/openedx/core/djangoapps/content/search/handlers.py
@@ -136,7 +136,13 @@ def content_library_updated_handler(**kwargs) -> None:
log.error("Received null or incorrect data for event")
return
- update_content_library_index_docs.delay(str(content_library_data.library_key))
+ # Update content library index synchronously to make sure that search index is updated before
+ # the frontend invalidates/refetches index.
+ # Currently, this is only required to make sure that removed/discarded components are removed
+ # from the search index and displayed to user properly. If it becomes a performance bottleneck
+ # for other update operations other than discard, we can update CONTENT_LIBRARY_UPDATED event
+ # to include a parameter which can help us decide if the task needs to run sync or async.
+ update_content_library_index_docs.apply(args=[str(content_library_data.library_key)])
@receiver(CONTENT_OBJECT_TAGS_CHANGED)
From c96949eeaaf00485f245de8e9cdd52e692248596 Mon Sep 17 00:00:00 2001
From: MueezKhan246 <93375917+MueezKhan246@users.noreply.github.com>
Date: Fri, 30 Aug 2024 12:16:52 +0000
Subject: [PATCH 097/242] feat: Upgrade Python dependency edx-enterprise
altered decrypted_secret to be encrypted and made credentials nullable
Commit generated by workflow `openedx/edx-platform/.github/workflows/upgrade-one-python-dependency.yml@refs/heads/master`
---
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 4a2f87e652..289f6a53be 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -26,7 +26,7 @@ celery>=5.2.2,<6.0.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==4.23.14
+edx-enterprise==4.23.15
# Stay on LTS version, remove once this is added to common constraint
Django<5.0
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 1c612fab67..86bc4dbf84 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -467,7 +467,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.14
+edx-enterprise==4.23.15
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index e6fd96e3be..d18028e3ef 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -741,7 +741,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.14
+edx-enterprise==4.23.15
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index c3dcafee93..571343e904 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -547,7 +547,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.14
+edx-enterprise==4.23.15
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 925fe517c8..553cb86f56 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -571,7 +571,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.14
+edx-enterprise==4.23.15
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
From e668790e11a2895a449d1218813f3ded4490c5f8 Mon Sep 17 00:00:00 2001
From: Muhammad Farhan Khan
Date: Mon, 2 Sep 2024 14:17:13 +0500
Subject: [PATCH 098/242] chore: Convert annotatable block sass variables to
css variables
---
xmodule/assets/annotatable/_display.scss | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/xmodule/assets/annotatable/_display.scss b/xmodule/assets/annotatable/_display.scss
index 124011e2bd..9aaa8649c6 100644
--- a/xmodule/assets/annotatable/_display.scss
+++ b/xmodule/assets/annotatable/_display.scss
@@ -9,7 +9,7 @@
@import 'bootstrap/scss/variables';
@import 'lms/theme/variables-v1';
-$annotatable--border-color: $gray-l3;
+$annotatable--border-color: var(--gray-l3);
$annotatable--body-font-size: em(14);
.annotatable-wrapper {
@@ -116,18 +116,18 @@ $annotatable--body-font-size: em(14);
border: 1px solid #333;
border-radius: 1em;
background-color: rgba(0, 0, 0, 0.85);
- color: $white;
+ color: var(--white);
-webkit-font-smoothing: antialiased;
.ui-tooltip-titlebar {
font-size: em(16);
color: inherit;
background-color: transparent;
- padding: ($baseline/4) ($baseline/2);
+ padding: calc((var(--baseline)/4)) calc((var(--baseline)/2));
border: none;
.ui-tooltip-title {
- padding: ($baseline/4) 0;
+ padding: calc((var(--baseline)/4)) 0;
border-bottom: 2px solid #333;
font-weight: bold;
}
@@ -139,7 +139,7 @@ $annotatable--body-font-size: em(14);
.ui-state-hover {
color: inherit;
- border: 1px solid $gray-l3;
+ border: 1px solid var(--gray-l3);
}
}
@@ -148,7 +148,7 @@ $annotatable--body-font-size: em(14);
font-size: em(14);
text-align: left;
font-weight: 400;
- padding: 0 ($baseline/2) ($baseline/2) ($baseline/2);
+ padding: 0 calc((var(--baseline)/2)) calc((var(--baseline)/2)) calc((var(--baseline)/2));
background-color: transparent;
border-color: transparent;
}
@@ -163,11 +163,11 @@ $annotatable--body-font-size: em(14);
max-width: 375px;
.ui-tooltip-content {
- padding: 0 ($baseline/2);
+ padding: 0 calc((var(--baseline)/2));
.annotatable-comment {
display: block;
- margin: 0 0 ($baseline/2) 0;
+ margin: 0 0 calc((var(--baseline)/2)) 0;
max-height: 225px;
overflow: auto;
line-height: normal;
@@ -176,7 +176,7 @@ $annotatable--body-font-size: em(14);
.annotatable-reply {
display: block;
border-top: 2px solid #333;
- padding: ($baseline/4) 0;
+ padding: calc((var(--baseline)/4)) 0;
margin: 0;
text-align: center;
}
@@ -190,7 +190,7 @@ $annotatable--body-font-size: em(14);
left: 50%;
height: 0;
width: 0;
- margin-left: -($baseline/4);
+ margin-left: calc(-1 * (var(--baseline) / 4));
border: 10px solid transparent;
border-top-color: rgba(0, 0, 0, 0.85);
}
From 14d2c4e967ca46db4a2230695e40b647b8faaf89 Mon Sep 17 00:00:00 2001
From: Muhammad Sameer Amin <35958006+sameeramin@users.noreply.github.com>
Date: Mon, 2 Sep 2024 16:55:09 +0500
Subject: [PATCH 099/242] feat: skip `migrations_are_in_sync` test
---
common/djangoapps/util/tests/test_db.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/common/djangoapps/util/tests/test_db.py b/common/djangoapps/util/tests/test_db.py
index 4a16c2a20a..88358ce205 100644
--- a/common/djangoapps/util/tests/test_db.py
+++ b/common/djangoapps/util/tests/test_db.py
@@ -1,5 +1,6 @@
"""Tests for util.db module."""
+import unittest
from io import StringIO
import ddt
@@ -120,6 +121,9 @@ class MigrationTests(TestCase):
Tests for migrations.
"""
+ @unittest.skip(
+ "Temporary skip for ENT-8971 while the client id and secret columns in Canvas replaced."
+ )
@override_settings(MIGRATION_MODULES={})
def test_migrations_are_in_sync(self):
"""
From ff2afe19df704296dffdc65dc0491643d0b77292 Mon Sep 17 00:00:00 2001
From: sameeramin <35958006+sameeramin@users.noreply.github.com>
Date: Tue, 3 Sep 2024 11:00:48 +0000
Subject: [PATCH 100/242] feat: Upgrade Python dependency edx-enterprise
reverts changes from #566
Commit generated by workflow `openedx/edx-platform/.github/workflows/upgrade-one-python-dependency.yml@refs/heads/master`
---
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 289f6a53be..6eff301ca1 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -26,7 +26,7 @@ celery>=5.2.2,<6.0.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==4.23.15
+edx-enterprise==4.23.19
# Stay on LTS version, remove once this is added to common constraint
Django<5.0
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 86bc4dbf84..f0a9075a18 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -467,7 +467,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.15
+edx-enterprise==4.23.19
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index d18028e3ef..db7760477b 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -741,7 +741,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.15
+edx-enterprise==4.23.19
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 571343e904..5ff7a74dbc 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -547,7 +547,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.15
+edx-enterprise==4.23.19
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 553cb86f56..be7f8305ea 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -571,7 +571,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.15
+edx-enterprise==4.23.19
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
From 4a12de7851ddc39c371e7ea4fb98316b8fc7d136 Mon Sep 17 00:00:00 2001
From: Braden MacDonald
Date: Tue, 3 Sep 2024 10:40:32 -0700
Subject: [PATCH 101/242] refactor: remove unused JS requirement in CMS
(#34545)
---
cms/envs/common.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/cms/envs/common.py b/cms/envs/common.py
index 45a8e97f3e..8daa08aeb1 100644
--- a/cms/envs/common.py
+++ b/cms/envs/common.py
@@ -1449,9 +1449,8 @@ base_vendor_js = [
'edx-ui-toolkit/js/utils/string-utils.js',
'edx-ui-toolkit/js/utils/html-utils.js',
- # Load Bootstrap and supporting libraries
- 'common/js/vendor/popper.js',
- 'common/js/vendor/bootstrap.js',
+ # Here we were loading Bootstrap and supporting libraries, but it no longer seems to be needed for any Studio UI.
+ # 'common/js/vendor/bootstrap.bundle.js',
# Finally load RequireJS
'common/js/vendor/require.js'
From dd3a7aad8dd59fe75f0c96ffd9b7cea6248d9a21 Mon Sep 17 00:00:00 2001
From: Muhammad Adeel Tajamul
<77053848+muhammadadeeltajamul@users.noreply.github.com>
Date: Wed, 4 Sep 2024 00:56:56 -0700
Subject: [PATCH 102/242] fix: update bulk email created event data (#35412)
---
lms/djangoapps/bulk_email/tasks.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lms/djangoapps/bulk_email/tasks.py b/lms/djangoapps/bulk_email/tasks.py
index 60d45c1564..0152d14ff0 100644
--- a/lms/djangoapps/bulk_email/tasks.py
+++ b/lms/djangoapps/bulk_email/tasks.py
@@ -472,7 +472,7 @@ def _send_course_email(entry_id, email_id, to_list, global_email_context, subtas
'edx.bulk_email.created',
{
'course_id': str(course_email.course_id),
- 'to_list': to_list,
+ 'to_list': [user_obj.get('email', '') for user_obj in to_list],
'total_recipients': total_recipients,
}
)
From 1ad9fd296757ec768105af6e13d922d66edcb731 Mon Sep 17 00:00:00 2001
From: sameeramin <35958006+sameeramin@users.noreply.github.com>
Date: Wed, 4 Sep 2024 13:22:19 +0000
Subject: [PATCH 103/242] feat: Upgrade Python dependency edx-enterprise
added migrations to remove client_id and client_secret from canvas
Commit generated by workflow `openedx/edx-platform/.github/workflows/upgrade-one-python-dependency.yml@refs/heads/master`
---
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 6eff301ca1..1aa5e88a6f 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -26,7 +26,7 @@ celery>=5.2.2,<6.0.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==4.23.19
+edx-enterprise==4.23.20
# Stay on LTS version, remove once this is added to common constraint
Django<5.0
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index f0a9075a18..c4da179ad7 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -467,7 +467,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.19
+edx-enterprise==4.23.20
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index db7760477b..7196b0d1d7 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -741,7 +741,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.19
+edx-enterprise==4.23.20
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 5ff7a74dbc..746bf686e1 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -547,7 +547,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.19
+edx-enterprise==4.23.20
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index be7f8305ea..8c11a4857d 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -571,7 +571,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.19
+edx-enterprise==4.23.20
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
From 96a6176a82ef0b07bef12b45101c859ce8779403 Mon Sep 17 00:00:00 2001
From: katrinan029 <71999631+katrinan029@users.noreply.github.com>
Date: Wed, 4 Sep 2024 17:27:57 +0000
Subject: [PATCH 104/242] feat: Upgrade Python dependency edx-enterprise
chore: version bump
Commit generated by workflow `openedx/edx-platform/.github/workflows/upgrade-one-python-dependency.yml@refs/heads/master`
---
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 1aa5e88a6f..c96cb6b578 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -26,7 +26,7 @@ celery>=5.2.2,<6.0.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==4.23.20
+edx-enterprise==4.24.0
# Stay on LTS version, remove once this is added to common constraint
Django<5.0
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index c4da179ad7..25e6e8556e 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -467,7 +467,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.20
+edx-enterprise==4.24.0
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index 7196b0d1d7..531eb6b00b 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -741,7 +741,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.20
+edx-enterprise==4.24.0
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 746bf686e1..8037d38b1a 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -547,7 +547,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.20
+edx-enterprise==4.24.0
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 8c11a4857d..646af2b7a0 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -571,7 +571,7 @@ edx-drf-extensions==10.3.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.23.20
+edx-enterprise==4.24.0
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
From 51d538cbe744cfea968cbc17d280045baa2c0e3d Mon Sep 17 00:00:00 2001
From: Juliana Kang
Date: Wed, 4 Sep 2024 14:01:45 -0400
Subject: [PATCH 105/242] fix: Remove B2C Subscriptions (#35303)
REV-3697
---
.../entitlements/rest_api/v1/permissions.py | 10 --
.../rest_api/v1/tests/test_views.py | 158 ------------------
.../entitlements/rest_api/v1/throttles.py | 21 ---
.../entitlements/rest_api/v1/urls.py | 7 +-
.../entitlements/rest_api/v1/views.py | 80 +--------
common/djangoapps/entitlements/tasks.py | 40 -----
docs/lms-openapi.yaml | 13 --
.../learner_dashboard/config/waffle.py | 17 --
lms/djangoapps/learner_dashboard/programs.py | 32 +---
lms/djangoapps/learner_dashboard/utils.py | 17 --
lms/envs/common.py | 12 --
lms/envs/devstack.py | 10 --
lms/envs/test.py | 9 -
.../models/program_subscription_model.js | 86 ----------
.../learner_dashboard/program_list_factory.js | 40 -----
.../spec/collection_list_view_spec.js | 10 --
.../spec/course_card_view_spec.js | 2 -
.../spec/program_alert_list_view_spec.js | 58 -------
.../spec/program_card_view_spec.js | 24 ---
.../spec/program_details_header_spec.js | 14 --
.../spec/program_details_sidebar_view_spec.js | 109 +-----------
.../spec/program_details_view_spec.js | 101 -----------
.../spec/program_list_header_view_spec.js | 31 ----
.../spec/sidebar_view_spec.js | 14 --
.../views/course_card_view.js | 7 -
.../views/program_alert_list_view.js | 89 ----------
.../views/program_card_view.js | 5 -
.../views/program_details_sidebar_view.js | 4 -
.../views/program_details_view.js | 116 -------------
.../views/program_header_view.js | 11 --
.../views/program_list_header_view.js | 69 --------
.../learner_dashboard/views/sidebar_view.js | 9 -
.../views/subscription_upsell_view.js | 30 ----
.../views/upgrade_message_view.js | 9 +-
lms/static/sass/views/_program-details.scss | 69 --------
lms/static/sass/views/_program-list.scss | 7 -
.../learner_dashboard/program_card.underscore | 5 -
.../program_details_fragment.html | 3 -
.../program_details_sidebar.underscore | 44 -----
.../program_details_tab_view.underscore | 5 +-
.../program_details_view.underscore | 4 +-
.../program_header_view.underscore | 5 -
.../program_list_header_view.underscore | 1 -
.../learner_dashboard/programs_fragment.html | 3 -
.../subscription_upsell_view.underscore | 20 ---
.../upgrade_message_subscription.underscore | 21 ---
.../credentials/tests/test_utils.py | 35 ----
openedx/core/djangoapps/credentials/utils.py | 57 -------
.../djangoapps/programs/tests/test_utils.py | 100 -----------
openedx/core/djangoapps/programs/utils.py | 75 +--------
50 files changed, 9 insertions(+), 1709 deletions(-)
delete mode 100644 common/djangoapps/entitlements/rest_api/v1/throttles.py
delete mode 100644 lms/static/js/learner_dashboard/models/program_subscription_model.js
delete mode 100644 lms/static/js/learner_dashboard/spec/program_alert_list_view_spec.js
delete mode 100644 lms/static/js/learner_dashboard/views/program_alert_list_view.js
delete mode 100644 lms/static/js/learner_dashboard/views/subscription_upsell_view.js
delete mode 100644 lms/templates/learner_dashboard/subscription_upsell_view.underscore
delete mode 100644 lms/templates/learner_dashboard/upgrade_message_subscription.underscore
diff --git a/common/djangoapps/entitlements/rest_api/v1/permissions.py b/common/djangoapps/entitlements/rest_api/v1/permissions.py
index 6a705d9fee..db14f05049 100644
--- a/common/djangoapps/entitlements/rest_api/v1/permissions.py
+++ b/common/djangoapps/entitlements/rest_api/v1/permissions.py
@@ -4,7 +4,6 @@ requiring Superuser access for all other Request types on an API endpoint.
"""
-from django.conf import settings
from rest_framework.permissions import SAFE_METHODS, BasePermission
from lms.djangoapps.courseware.access import has_access
@@ -22,12 +21,3 @@ class IsAdminOrSupportOrAuthenticatedReadOnly(BasePermission):
return request.user.is_authenticated
else:
return request.user.is_staff or has_access(request.user, "support", "global")
-
-
-class IsSubscriptionWorkerUser(BasePermission):
- """
- Method that will require the request to be coming from the subscriptions service worker user.
- """
-
- def has_permission(self, request, view):
- return request.user.username == settings.SUBSCRIPTIONS_SERVICE_WORKER_USERNAME
diff --git a/common/djangoapps/entitlements/rest_api/v1/tests/test_views.py b/common/djangoapps/entitlements/rest_api/v1/tests/test_views.py
index 34abc39c00..86d4ae6a87 100644
--- a/common/djangoapps/entitlements/rest_api/v1/tests/test_views.py
+++ b/common/djangoapps/entitlements/rest_api/v1/tests/test_views.py
@@ -6,7 +6,6 @@ import logging
import uuid
from datetime import datetime, timedelta
from unittest.mock import patch
-from uuid import uuid4
from django.conf import settings
from django.urls import reverse
@@ -1236,160 +1235,3 @@ class EntitlementEnrollmentViewSetTest(ModuleStoreTestCase):
assert CourseEnrollment.is_enrolled(self.user, self.course.id)
assert course_entitlement.enrollment_course_run is not None
assert course_entitlement.expired_at is None
-
-
-@skip_unless_lms
-class RevokeSubscriptionsVerifiedAccessViewTest(ModuleStoreTestCase):
- """
- Tests for the RevokeVerifiedAccessView
- """
- REVOKE_VERIFIED_ACCESS_PATH = 'entitlements_api:v1:revoke_subscriptions_verified_access'
-
- def setUp(self):
- super().setUp()
- self.user = UserFactory(username="subscriptions_worker", is_staff=True)
- self.client.login(username=self.user.username, password=TEST_PASSWORD)
- self.course = CourseFactory()
- self.course_mode1 = CourseModeFactory(
- course_id=self.course.id, # pylint: disable=no-member
- mode_slug=CourseMode.VERIFIED,
- expiration_datetime=now() + timedelta(days=1)
- )
- self.course_mode2 = CourseModeFactory(
- course_id=self.course.id, # pylint: disable=no-member
- mode_slug=CourseMode.AUDIT,
- expiration_datetime=now() + timedelta(days=1)
- )
-
- @patch('common.djangoapps.entitlements.rest_api.v1.views.get_courses_completion_status')
- def test_revoke_access_success(self, mock_get_courses_completion_status):
- mock_get_courses_completion_status.return_value = ([], False)
- enrollment = CourseEnrollmentFactory.create(
- user=self.user,
- course_id=self.course.id, # pylint: disable=no-member
- is_active=True,
- mode=CourseMode.VERIFIED
- )
- course_entitlement = CourseEntitlementFactory.create(user=self.user, enrollment_course_run=enrollment)
- url = reverse(self.REVOKE_VERIFIED_ACCESS_PATH)
-
- assert course_entitlement.enrollment_course_run is not None
-
- response = self.client.post(
- url,
- data={
- "entitlement_uuids": [str(course_entitlement.uuid)],
- "lms_user_id": self.user.id
- },
- content_type='application/json',
- )
- assert response.status_code == 204
-
- course_entitlement.refresh_from_db()
- enrollment.refresh_from_db()
- assert course_entitlement.expired_at is not None
- assert course_entitlement.enrollment_course_run is None
- assert enrollment.mode == CourseMode.AUDIT
-
- @patch('common.djangoapps.entitlements.rest_api.v1.views.get_courses_completion_status')
- def test_already_completed_course(self, mock_get_courses_completion_status):
- enrollment = CourseEnrollmentFactory.create(
- user=self.user,
- course_id=self.course.id, # pylint: disable=no-member
- is_active=True,
- mode=CourseMode.VERIFIED
- )
- mock_get_courses_completion_status.return_value = ([str(enrollment.course_id)], False)
- course_entitlement = CourseEntitlementFactory.create(user=self.user, enrollment_course_run=enrollment)
- url = reverse(self.REVOKE_VERIFIED_ACCESS_PATH)
-
- assert course_entitlement.enrollment_course_run is not None
-
- response = self.client.post(
- url,
- data={
- "entitlement_uuids": [str(course_entitlement.uuid)],
- "lms_user_id": self.user.id
- },
- content_type='application/json',
- )
- assert response.status_code == 204
-
- course_entitlement.refresh_from_db()
- assert course_entitlement.expired_at is None
- assert course_entitlement.enrollment_course_run.mode == CourseMode.VERIFIED
-
- @patch('common.djangoapps.entitlements.rest_api.v1.views.log.info')
- def test_revoke_access_invalid_uuid(self, mock_log):
- url = reverse(self.REVOKE_VERIFIED_ACCESS_PATH)
- entitlement_uuids = [str(uuid4())]
- response = self.client.post(
- url,
- data={
- "entitlement_uuids": entitlement_uuids,
- "lms_user_id": self.user.id
- },
- content_type='application/json',
- )
-
- mock_log.assert_called_once_with("B2C_SUBSCRIPTIONS: Entitlements not found for the provided"
- " entitlements data: %s and user: %s",
- entitlement_uuids,
- self.user.id)
- assert response.status_code == 204
-
- def test_revoke_access_unauthorized_user(self):
- user = UserFactory(is_staff=True, username='not_subscriptions_worker')
- self.client.login(username=user.username, password=TEST_PASSWORD)
-
- enrollment = CourseEnrollmentFactory.create(
- user=self.user,
- course_id=self.course.id, # pylint: disable=no-member
- is_active=True,
- mode=CourseMode.VERIFIED
- )
- course_entitlement = CourseEntitlementFactory.create(user=self.user, enrollment_course_run=enrollment)
- url = reverse(self.REVOKE_VERIFIED_ACCESS_PATH)
-
- assert course_entitlement.enrollment_course_run is not None
-
- response = self.client.post(
- url,
- data={
- "entitlement_uuids": [],
- "lms_user_id": self.user.id
- },
- content_type='application/json',
- )
- assert response.status_code == 403
-
- course_entitlement.refresh_from_db()
- assert course_entitlement.expired_at is None
- assert course_entitlement.enrollment_course_run.mode == CourseMode.VERIFIED
-
- @patch('common.djangoapps.entitlements.tasks.retry_revoke_subscriptions_verified_access.apply_async')
- @patch('common.djangoapps.entitlements.rest_api.v1.views.get_courses_completion_status')
- def test_course_completion_exception_triggers_task(self, mock_get_courses_completion_status, mock_task):
- mock_get_courses_completion_status.return_value = ([], True)
- enrollment = CourseEnrollmentFactory.create(
- user=self.user,
- course_id=self.course.id, # pylint: disable=no-member
- is_active=True,
- mode=CourseMode.VERIFIED
- )
- course_entitlement = CourseEntitlementFactory.create(user=self.user, enrollment_course_run=enrollment)
-
- url = reverse(self.REVOKE_VERIFIED_ACCESS_PATH)
-
- response = self.client.post(
- url,
- data={
- "entitlement_uuids": [str(course_entitlement.uuid)],
- "lms_user_id": self.user.id
- },
- content_type='application/json',
- )
- assert response.status_code == 204
- mock_task.assert_called_once_with(args=([str(course_entitlement.uuid)],
- [str(enrollment.course_id)],
- self.user.username))
diff --git a/common/djangoapps/entitlements/rest_api/v1/throttles.py b/common/djangoapps/entitlements/rest_api/v1/throttles.py
deleted file mode 100644
index 3a010c76af..0000000000
--- a/common/djangoapps/entitlements/rest_api/v1/throttles.py
+++ /dev/null
@@ -1,21 +0,0 @@
-"""
-Throttle classes for the entitlements API.
-"""
-
-from django.conf import settings
-from rest_framework.throttling import UserRateThrottle
-
-
-class ServiceUserThrottle(UserRateThrottle):
- """A throttle allowing service users to override rate limiting"""
-
- def allow_request(self, request, view):
- """Returns True if the request is coming from one of the service users
- and defaults to UserRateThrottle's configured setting otherwise.
- """
- service_users = [
- settings.SUBSCRIPTIONS_SERVICE_WORKER_USERNAME
- ]
- if request.user.username in service_users:
- return True
- return super().allow_request(request, view)
diff --git a/common/djangoapps/entitlements/rest_api/v1/urls.py b/common/djangoapps/entitlements/rest_api/v1/urls.py
index e1d98a2485..e04341b5ef 100644
--- a/common/djangoapps/entitlements/rest_api/v1/urls.py
+++ b/common/djangoapps/entitlements/rest_api/v1/urls.py
@@ -6,7 +6,7 @@ from django.urls import include
from django.urls import path, re_path
from rest_framework.routers import DefaultRouter
-from .views import EntitlementEnrollmentViewSet, EntitlementViewSet, SubscriptionsRevokeVerifiedAccessView
+from .views import EntitlementEnrollmentViewSet, EntitlementViewSet
router = DefaultRouter()
router.register(r'entitlements', EntitlementViewSet, basename='entitlements')
@@ -24,9 +24,4 @@ urlpatterns = [
ENROLLMENTS_VIEW,
name='enrollments'
),
- path(
- 'subscriptions/entitlements/revoke',
- SubscriptionsRevokeVerifiedAccessView.as_view(),
- name='revoke_subscriptions_verified_access'
- )
]
diff --git a/common/djangoapps/entitlements/rest_api/v1/views.py b/common/djangoapps/entitlements/rest_api/v1/views.py
index 3306604d5d..4f3dd54b52 100644
--- a/common/djangoapps/entitlements/rest_api/v1/views.py
+++ b/common/djangoapps/entitlements/rest_api/v1/views.py
@@ -15,7 +15,6 @@ from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from rest_framework import permissions, status, viewsets
from rest_framework.response import Response
-from rest_framework.views import APIView
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.entitlements.models import ( # lint-amnesty, pylint: disable=line-too-long
@@ -24,22 +23,13 @@ from common.djangoapps.entitlements.models import ( # lint-amnesty, pylint: dis
CourseEntitlementSupportDetail
)
from common.djangoapps.entitlements.rest_api.v1.filters import CourseEntitlementFilter
-from common.djangoapps.entitlements.rest_api.v1.permissions import (
- IsAdminOrSupportOrAuthenticatedReadOnly,
- IsSubscriptionWorkerUser
-)
+from common.djangoapps.entitlements.rest_api.v1.permissions import IsAdminOrSupportOrAuthenticatedReadOnly
from common.djangoapps.entitlements.rest_api.v1.serializers import CourseEntitlementSerializer
-from common.djangoapps.entitlements.rest_api.v1.throttles import ServiceUserThrottle
-from common.djangoapps.entitlements.tasks import retry_revoke_subscriptions_verified_access
-from common.djangoapps.entitlements.utils import (
- is_course_run_entitlement_fulfillable,
- revoke_entitlements_and_downgrade_courses_to_audit
-)
+from common.djangoapps.entitlements.utils import is_course_run_entitlement_fulfillable
from common.djangoapps.student.models import AlreadyEnrolledError, CourseEnrollment, CourseEnrollmentException
from openedx.core.djangoapps.catalog.utils import get_course_runs_for_course, get_owners_for_course
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.cors_csrf.authentication import SessionAuthenticationCrossDomainCsrf
-from openedx.core.djangoapps.credentials.utils import get_courses_completion_status
from openedx.core.djangoapps.user_api.preferences.api import update_email_opt_in
User = get_user_model()
@@ -132,7 +122,6 @@ class EntitlementViewSet(viewsets.ModelViewSet):
filter_backends = (DjangoFilterBackend,)
filterset_class = CourseEntitlementFilter
pagination_class = EntitlementsPagination
- throttle_classes = (ServiceUserThrottle,)
def get_queryset(self):
user = self.request.user
@@ -530,68 +519,3 @@ class EntitlementEnrollmentViewSet(viewsets.GenericViewSet):
})
return Response(status=status.HTTP_204_NO_CONTENT)
-
-
-class SubscriptionsRevokeVerifiedAccessView(APIView):
- """
- Endpoint for expiring entitlements for a user and downgrading the enrollments
- to Audit mode. This endpoint accepts a list of entitlement UUIDs and will expire
- the entitlements along with downgrading the related enrollments to Audit mode.
- Only those enrollments are downgraded to Audit for which user has not been awarded
- a completion certificate yet.
- """
- authentication_classes = (JwtAuthentication, SessionAuthenticationCrossDomainCsrf,)
- permission_classes = (permissions.IsAuthenticated, IsSubscriptionWorkerUser,)
- throttle_classes = (ServiceUserThrottle,)
-
- def _process_revoke_and_downgrade_to_audit(self, course_entitlements, user_id, revocable_entitlement_uuids):
- """
- Gets course completion status for the provided course entitlements and triggers the
- revoke and downgrade to audit process for the course entitlements which are not completed.
- Triggers the retry task asynchronously if there is an exception while getting the
- course completion status.
- """
- entitled_course_ids = []
- user = User.objects.get(id=user_id)
- username = user.username
- for course_entitlement in course_entitlements:
- if course_entitlement.enrollment_course_run is not None:
- entitled_course_ids.append(str(course_entitlement.enrollment_course_run.course_id))
-
- log.info('B2C_SUBSCRIPTIONS: Getting course completion status for user [%s] and entitled_course_ids %s',
- username,
- entitled_course_ids)
- awarded_cert_course_ids, is_exception = get_courses_completion_status(username, entitled_course_ids)
-
- if is_exception:
- # Trigger the retry task asynchronously
- log.exception('B2C_SUBSCRIPTIONS: Exception occurred while getting course completion status for user %s '
- 'and entitled_course_ids %s',
- username,
- entitled_course_ids)
- retry_revoke_subscriptions_verified_access.apply_async(args=(revocable_entitlement_uuids,
- entitled_course_ids,
- username))
- return
- revoke_entitlements_and_downgrade_courses_to_audit(course_entitlements, username, awarded_cert_course_ids,
- revocable_entitlement_uuids)
-
- def post(self, request):
- """
- Invokes the entitlements expiration process for the provided uuids and downgrades the
- enrollments to Audit mode.
- """
- revocable_entitlement_uuids = request.data.get('entitlement_uuids', [])
- user_id = request.data.get('lms_user_id', None)
- course_entitlements = (CourseEntitlement.objects.filter(uuid__in=revocable_entitlement_uuids).
- select_related('user').
- select_related('enrollment_course_run'))
-
- if course_entitlements.exists():
- self._process_revoke_and_downgrade_to_audit(course_entitlements, user_id, revocable_entitlement_uuids)
- return Response(status=status.HTTP_204_NO_CONTENT)
- else:
- log.info('B2C_SUBSCRIPTIONS: Entitlements not found for the provided entitlements data: %s and user: %s',
- revocable_entitlement_uuids,
- user_id)
- return Response(status=status.HTTP_204_NO_CONTENT)
diff --git a/common/djangoapps/entitlements/tasks.py b/common/djangoapps/entitlements/tasks.py
index 981879e217..9bd200bc90 100644
--- a/common/djangoapps/entitlements/tasks.py
+++ b/common/djangoapps/entitlements/tasks.py
@@ -4,15 +4,12 @@ This file contains celery tasks for entitlements-related functionality.
import logging
from celery import shared_task
-from celery.exceptions import MaxRetriesExceededError
from celery.utils.log import get_task_logger
from django.conf import settings # lint-amnesty, pylint: disable=unused-import
from django.contrib.auth import get_user_model
from edx_django_utils.monitoring import set_code_owner_attribute
from common.djangoapps.entitlements.models import CourseEntitlement, CourseEntitlementSupportDetail
-from common.djangoapps.entitlements.utils import revoke_entitlements_and_downgrade_courses_to_audit
-from openedx.core.djangoapps.credentials.utils import get_courses_completion_status
LOGGER = get_task_logger(__name__)
log = logging.getLogger(__name__)
@@ -154,40 +151,3 @@ def expire_and_create_entitlements(self, entitlement_ids, support_username):
'%d entries, task id :%s',
len(entitlement_ids),
self.request.id)
-
-
-@shared_task(bind=True)
-@set_code_owner_attribute
-def retry_revoke_subscriptions_verified_access(self, revocable_entitlement_uuids, entitled_course_ids, username):
- """
- Task to process course access revoke and move to audit.
- This is called only if call to get_courses_completion_status fails due to any exception.
- """
- LOGGER.info("B2C_SUBSCRIPTIONS: Running retry_revoke_subscriptions_verified_access for user [%s],"
- " entitlement_uuids %s and entitled_course_ids %s",
- username,
- revocable_entitlement_uuids,
- entitled_course_ids)
- course_entitlements = CourseEntitlement.objects.filter(uuid__in=revocable_entitlement_uuids)
- course_entitlements = course_entitlements.select_related('user').select_related('enrollment_course_run')
- if course_entitlements.exists():
- awarded_cert_course_ids, is_exception = get_courses_completion_status(username, entitled_course_ids)
- if is_exception:
- try:
- countdown = 2 ** self.request.retries
- self.retry(countdown=countdown, max_retries=3)
- except MaxRetriesExceededError:
- LOGGER.exception(
- 'B2C_SUBSCRIPTIONS: Failed to process retry_revoke_subscriptions_verified_access '
- 'for user [%s] and entitlement_uuids %s',
- username,
- revocable_entitlement_uuids
- )
- return
- revoke_entitlements_and_downgrade_courses_to_audit(course_entitlements, username, awarded_cert_course_ids,
- revocable_entitlement_uuids)
- else:
- LOGGER.info('B2C_SUBSCRIPTIONS: Entitlements not found for the provided entitlements uuids %s '
- 'for user [%s] duing the retry_revoke_subscriptions_verified_access task',
- revocable_entitlement_uuids,
- username)
diff --git a/docs/lms-openapi.yaml b/docs/lms-openapi.yaml
index 9fa03436e1..5e9afcc6d3 100644
--- a/docs/lms-openapi.yaml
+++ b/docs/lms-openapi.yaml
@@ -5277,19 +5277,6 @@ paths:
required: true
type: string
format: uuid
- /entitlements/v1/subscriptions/entitlements/revoke:
- post:
- operationId: entitlements_v1_subscriptions_entitlements_revoke_create
- description: |-
- Invokes the entitlements expiration process for the provided uuids and downgrades the
- enrollments to Audit mode.
- parameters: []
- responses:
- '201':
- description: ''
- tags:
- - entitlements
- parameters: []
/experiments/v0/custom/REV-934/:
get:
operationId: experiments_v0_custom_REV-934_list
diff --git a/lms/djangoapps/learner_dashboard/config/waffle.py b/lms/djangoapps/learner_dashboard/config/waffle.py
index 2195a26972..cc63e8d5d1 100644
--- a/lms/djangoapps/learner_dashboard/config/waffle.py
+++ b/lms/djangoapps/learner_dashboard/config/waffle.py
@@ -37,20 +37,3 @@ ENABLE_MASTERS_PROGRAM_TAB_VIEW = WaffleFlag(
'learner_dashboard.enable_masters_program_tab_view',
__name__,
)
-
-# .. toggle_name: learner_dashboard.enable_b2c_subscriptions
-# .. toggle_implementation: WaffleFlag
-# .. toggle_default: False
-# .. toggle_description: Waffle flag to enable new B2C Subscriptions Program data.
-# This flag is used to decide whether we need to enable program subscription related properties in program listing
-# and detail pages.
-# .. toggle_use_cases: temporary
-# .. toggle_creation_date: 2023-04-13
-# .. toggle_target_removal_date: 2023-07-01
-# .. toggle_warning: When the flag is ON, the new B2C Subscriptions Program data will be enabled in program listing
-# and detail pages.
-# .. toggle_tickets: PON-79
-ENABLE_B2C_SUBSCRIPTIONS = WaffleFlag(
- 'learner_dashboard.enable_b2c_subscriptions',
- __name__,
-)
diff --git a/lms/djangoapps/learner_dashboard/programs.py b/lms/djangoapps/learner_dashboard/programs.py
index d567a4b9a3..dc334c0ce3 100644
--- a/lms/djangoapps/learner_dashboard/programs.py
+++ b/lms/djangoapps/learner_dashboard/programs.py
@@ -6,7 +6,6 @@ import json
from abc import ABC, abstractmethod
from urllib.parse import quote
-from django.conf import settings
from django.contrib.sites.shortcuts import get_current_site
from django.http import Http404
from django.template.loader import render_to_string
@@ -18,7 +17,7 @@ from web_fragments.fragment import Fragment
from common.djangoapps.student.models import anonymous_id_for_user
from common.djangoapps.student.roles import GlobalStaff
-from lms.djangoapps.learner_dashboard.utils import b2c_subscriptions_enabled, program_tab_view_is_enabled
+from lms.djangoapps.learner_dashboard.utils import program_tab_view_is_enabled
from openedx.core.djangoapps.catalog.utils import get_programs
from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
from openedx.core.djangoapps.programs.models import (
@@ -32,9 +31,7 @@ from openedx.core.djangoapps.programs.utils import (
get_industry_and_credit_pathways,
get_program_and_course_data,
get_program_marketing_url,
- get_program_subscriptions_marketing_url,
get_program_urls,
- get_programs_subscription_data
)
from openedx.core.djangoapps.user_api.preferences.api import get_user_preferences
from openedx.core.djangolib.markup import HTML
@@ -60,30 +57,12 @@ class ProgramsFragmentView(EdxFragmentView):
raise Http404
meter = ProgramProgressMeter(request.site, user, mobile_only=mobile_only)
- is_user_b2c_subscriptions_enabled = b2c_subscriptions_enabled(mobile_only)
- programs_subscription_data = (
- get_programs_subscription_data(user)
- if is_user_b2c_subscriptions_enabled
- else []
- )
- subscription_upsell_data = (
- {
- 'marketing_url': get_program_subscriptions_marketing_url(),
- 'minimum_price': settings.SUBSCRIPTIONS_MINIMUM_PRICE,
- 'trial_length': settings.SUBSCRIPTIONS_TRIAL_LENGTH,
- }
- if is_user_b2c_subscriptions_enabled
- else {}
- )
context = {
'marketing_url': get_program_marketing_url(programs_config, mobile_only),
'programs': meter.engaged_programs,
'progress': meter.progress(),
- 'programs_subscription_data': programs_subscription_data,
- 'subscription_upsell_data': subscription_upsell_data,
'user_preferences': get_user_preferences(user),
- 'is_user_b2c_subscriptions_enabled': is_user_b2c_subscriptions_enabled,
'mobile_only': bool(mobile_only)
}
html = render_to_string('learner_dashboard/programs_fragment.html', context)
@@ -137,12 +116,6 @@ class ProgramDetailsFragmentView(EdxFragmentView):
program_discussion_lti = ProgramDiscussionLTI(program_uuid, request)
program_live_lti = ProgramLiveLTI(program_uuid, request)
- is_user_b2c_subscriptions_enabled = b2c_subscriptions_enabled(mobile_only)
- program_subscription_data = (
- get_programs_subscription_data(user, program_uuid)
- if is_user_b2c_subscriptions_enabled
- else []
- )
def program_tab_view_enabled() -> bool:
return program_tab_view_is_enabled() and (
@@ -156,14 +129,11 @@ class ProgramDetailsFragmentView(EdxFragmentView):
'urls': urls,
'user_preferences': get_user_preferences(user),
'program_data': program_data,
- 'program_subscription_data': program_subscription_data,
'course_data': course_data,
'certificate_data': certificate_data,
'industry_pathways': industry_pathways,
'credit_pathways': credit_pathways,
'program_tab_view_enabled': program_tab_view_enabled(),
- 'is_user_b2c_subscriptions_enabled': is_user_b2c_subscriptions_enabled,
- 'subscriptions_trial_length': settings.SUBSCRIPTIONS_TRIAL_LENGTH,
'discussion_fragment': {
'configured': program_discussion_lti.is_configured,
'iframe': program_discussion_lti.render_iframe()
diff --git a/lms/djangoapps/learner_dashboard/utils.py b/lms/djangoapps/learner_dashboard/utils.py
index a604ba7378..5e9c172fcb 100644
--- a/lms/djangoapps/learner_dashboard/utils.py
+++ b/lms/djangoapps/learner_dashboard/utils.py
@@ -7,7 +7,6 @@ from opaque_keys.edx.keys import CourseKey
from common.djangoapps.student.roles import GlobalStaff
from lms.djangoapps.learner_dashboard.config.waffle import (
- ENABLE_B2C_SUBSCRIPTIONS,
ENABLE_MASTERS_PROGRAM_TAB_VIEW,
ENABLE_PROGRAM_TAB_VIEW
)
@@ -50,19 +49,3 @@ def is_enrolled_or_staff(request, program_uuid):
except ObjectDoesNotExist:
return False
return True
-
-
-def b2c_subscriptions_is_enabled() -> bool:
- """
- Check if B2C program subscriptions flag is enabled.
- """
- return ENABLE_B2C_SUBSCRIPTIONS.is_enabled()
-
-
-def b2c_subscriptions_enabled(is_mobile=False) -> bool:
- """
- Check whether B2C Subscriptions pages should be shown to user.
- """
- if not is_mobile and b2c_subscriptions_is_enabled():
- return True
- return False
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 2d31cba982..a6c8e810d2 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -4691,7 +4691,6 @@ ENTERPRISE_ALL_SERVICE_USERNAMES = [
'enterprise_channel_worker',
'enterprise_access_worker',
'enterprise_subsidy_worker',
- 'subscriptions_worker'
]
# Setting for Open API key and prompts used by edx-enterprise.
@@ -5385,17 +5384,6 @@ ENTERPRISE_MANUAL_REPORTING_CUSTOMER_UUIDS = []
AVAILABLE_DISCUSSION_TOURS = []
-######################## Subscriptions API SETTINGS ########################
-SUBSCRIPTIONS_ROOT_URL = ""
-SUBSCRIPTIONS_API_PATH = f"{SUBSCRIPTIONS_ROOT_URL}/api/v1/stripe-subscription/"
-
-SUBSCRIPTIONS_LEARNER_HELP_CENTER_URL = None
-SUBSCRIPTIONS_BUY_SUBSCRIPTION_URL = f"{SUBSCRIPTIONS_ROOT_URL}/api/v1/stripe-subscribe/"
-SUBSCRIPTIONS_MANAGE_SUBSCRIPTION_URL = None
-SUBSCRIPTIONS_MINIMUM_PRICE = '$39'
-SUBSCRIPTIONS_TRIAL_LENGTH = 7
-SUBSCRIPTIONS_SERVICE_WORKER_USERNAME = 'subscriptions_worker'
-
############## NOTIFICATIONS ##############
NOTIFICATIONS_EXPIRY = 60
EXPIRED_NOTIFICATIONS_DELETE_BATCH_SIZE = 10000
diff --git a/lms/envs/devstack.py b/lms/envs/devstack.py
index 6110179628..890164b0bd 100644
--- a/lms/envs/devstack.py
+++ b/lms/envs/devstack.py
@@ -522,16 +522,6 @@ course_access_role_removed_event_setting = EVENT_BUS_PRODUCER_CONFIG[
]
course_access_role_removed_event_setting['learning-course-access-role-lifecycle']['enabled'] = True
-######################## Subscriptions API SETTINGS ########################
-SUBSCRIPTIONS_ROOT_URL = "http://host.docker.internal:18750"
-SUBSCRIPTIONS_API_PATH = f"{SUBSCRIPTIONS_ROOT_URL}/api/v1/stripe-subscription/"
-
-SUBSCRIPTIONS_LEARNER_HELP_CENTER_URL = None
-SUBSCRIPTIONS_BUY_SUBSCRIPTION_URL = f"{SUBSCRIPTIONS_ROOT_URL}/api/v1/stripe-subscribe/"
-SUBSCRIPTIONS_MANAGE_SUBSCRIPTION_URL = None
-SUBSCRIPTIONS_MINIMUM_PRICE = '$39'
-SUBSCRIPTIONS_TRIAL_LENGTH = 7
-
# API access management
API_ACCESS_MANAGER_EMAIL = 'api-access@example.com'
API_ACCESS_FROM_EMAIL = 'api-requests@example.com'
diff --git a/lms/envs/test.py b/lms/envs/test.py
index 3c4bb95649..a9e8aaf9f2 100644
--- a/lms/envs/test.py
+++ b/lms/envs/test.py
@@ -650,15 +650,6 @@ SURVEY_REPORT_CHECK_THRESHOLD = 6
SURVEY_REPORT_ENABLE = True
ANONYMOUS_SURVEY_REPORT = False
-######################## Subscriptions API SETTINGS ########################
-SUBSCRIPTIONS_ROOT_URL = "http://localhost:18750"
-SUBSCRIPTIONS_API_PATH = f"{SUBSCRIPTIONS_ROOT_URL}/api/v1/stripe-subscription/"
-
-SUBSCRIPTIONS_LEARNER_HELP_CENTER_URL = None
-SUBSCRIPTIONS_BUY_SUBSCRIPTION_URL = f"{SUBSCRIPTIONS_ROOT_URL}/api/v1/stripe-subscribe/"
-SUBSCRIPTIONS_MANAGE_SUBSCRIPTION_URL = None
-SUBSCRIPTIONS_MINIMUM_PRICE = '$39'
-SUBSCRIPTIONS_TRIAL_LENGTH = 7
CSRF_TRUSTED_ORIGINS = ['.example.com']
CSRF_TRUSTED_ORIGINS_WITH_SCHEME = ['https://*.example.com']
diff --git a/lms/static/js/learner_dashboard/models/program_subscription_model.js b/lms/static/js/learner_dashboard/models/program_subscription_model.js
deleted file mode 100644
index 18f30031f7..0000000000
--- a/lms/static/js/learner_dashboard/models/program_subscription_model.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import Backbone from 'backbone';
-import moment from 'moment';
-
-import DateUtils from 'edx-ui-toolkit/js/utils/date-utils';
-import StringUtils from 'edx-ui-toolkit/js/utils/string-utils';
-
-
-/**
- * Model for Program Subscription Data.
- */
-class ProgramSubscriptionModel extends Backbone.Model {
- constructor({ context }, ...args) {
- const {
- subscriptionData: [data = {}],
- programData: { subscription_prices },
- urls = {},
- userPreferences = {},
- subscriptionsTrialLength: trialLength = 7,
- } = context;
-
- const priceInUSD = subscription_prices?.find(({ currency }) => currency === 'USD');
-
- const subscriptionState = data.subscription_state?.toLowerCase() ?? '';
- const subscriptionPrice = StringUtils.interpolate(
- gettext('${price}/month {currency}'),
- {
- price: parseFloat(priceInUSD?.price),
- currency: priceInUSD?.currency,
- }
- );
-
- const subscriptionUrl =
- subscriptionState === 'active'
- ? urls.manage_subscription_url
- : urls.buy_subscription_url;
-
- const hasActiveTrial = false;
-
- const remainingDays = 0;
-
- const [currentPeriodEnd] = ProgramSubscriptionModel.formatDate(
- data.current_period_end,
- userPreferences
- );
- const [trialEndDate, trialEndTime] = ['', ''];
-
- super(
- {
- hasActiveTrial,
- currentPeriodEnd,
- remainingDays,
- subscriptionPrice,
- subscriptionState,
- subscriptionUrl,
- trialEndDate,
- trialEndTime,
- trialLength,
- },
- ...args
- );
- }
-
- static formatDate(date, userPreferences) {
- if (!date) {
- return ['', ''];
- }
-
- const userTimezone = (
- userPreferences.time_zone || moment?.tz?.guess?.() || 'UTC'
- );
- const userLanguage = userPreferences['pref-lang'] || 'en';
- const context = {
- datetime: date,
- timezone: userTimezone,
- language: userLanguage,
- format: DateUtils.dateFormatEnum.shortDate,
- };
-
- const localDate = DateUtils.localize(context);
- const localTime = '';
-
- return [localDate, localTime];
- }
-}
-
-export default ProgramSubscriptionModel;
diff --git a/lms/static/js/learner_dashboard/program_list_factory.js b/lms/static/js/learner_dashboard/program_list_factory.js
index 5433306641..b9ff1c4019 100644
--- a/lms/static/js/learner_dashboard/program_list_factory.js
+++ b/lms/static/js/learner_dashboard/program_list_factory.js
@@ -11,58 +11,18 @@ import HeaderView from './views/program_list_header_view';
function ProgramListFactory(options) {
const progressCollection = new ProgressCollection();
- const subscriptionCollection = new Backbone.Collection();
if (options.userProgress) {
progressCollection.set(options.userProgress);
options.progressCollection = progressCollection; // eslint-disable-line no-param-reassign
}
- if (options.programsSubscriptionData.length) {
- subscriptionCollection.set(options.programsSubscriptionData);
- options.subscriptionCollection = subscriptionCollection; // eslint-disable-line no-param-reassign
- }
-
if (options.programsData.length) {
if (!options.mobileOnly) {
new HeaderView({
context: options,
}).render();
}
-
- const activeSubscriptions = options.programsSubscriptionData
- // eslint-disable-next-line camelcase
- .filter(({ subscription_state }) => subscription_state === 'active')
- .sort((a, b) => new Date(b.created) - new Date(a.created));
-
- // Sort programs so programs with active subscriptions are at the top
- if (activeSubscriptions.length) {
- // eslint-disable-next-line no-param-reassign
- options.programsData = options.programsData
- .map((programsData) => ({
- ...programsData,
- subscriptionIndex: activeSubscriptions.findIndex(
- // eslint-disable-next-line camelcase
- ({ resource_id }) => resource_id === programsData.uuid,
- ),
- }))
- .sort(({ subscriptionIndex: indexA }, { subscriptionIndex: indexB }) => {
- switch (true) {
- case indexA === -1 && indexB === -1:
- // Maintain the original order for non-subscription programs
- return 0;
- case indexA === -1:
- // Move non-subscription program to the end
- return 1;
- case indexB === -1:
- // Keep non-subscription program to the end
- return -1;
- default:
- // Sort by subscriptionIndex in ascending order
- return indexA - indexB;
- }
- });
- }
}
new CollectionListView({
diff --git a/lms/static/js/learner_dashboard/spec/collection_list_view_spec.js b/lms/static/js/learner_dashboard/spec/collection_list_view_spec.js
index c9c1c4d97b..1cd490447b 100644
--- a/lms/static/js/learner_dashboard/spec/collection_list_view_spec.js
+++ b/lms/static/js/learner_dashboard/spec/collection_list_view_spec.js
@@ -1,7 +1,5 @@
/* globals setFixtures */
-import Backbone from 'backbone';
-
import CollectionListView from '../views/collection_list_view';
import ProgramCardView from '../views/program_card_view';
import ProgramCollection from '../collections/program_collection';
@@ -11,7 +9,6 @@ describe('Collection List View', () => {
let view = null;
let programCollection;
let progressCollection;
- let subscriptionCollection;
const context = {
programsData: [
{
@@ -101,21 +98,14 @@ describe('Collection List View', () => {
not_started: 3,
},
],
- programsSubscriptionData: [{
- resource_id: 'a87e5eac-3c93-45a1-a8e1-4c79ca8401c8',
- subscription_state: 'active',
- }],
- isUserB2CSubscriptionsEnabled: false,
};
beforeEach(() => {
setFixtures('
');
programCollection = new ProgramCollection(context.programsData);
progressCollection = new ProgressCollection();
- subscriptionCollection = new Backbone.Collection(context.programsSubscriptionData);
progressCollection.set(context.userProgress);
context.progressCollection = progressCollection;
- context.subscriptionCollection = subscriptionCollection;
view = new CollectionListView({
el: '.program-cards-container',
diff --git a/lms/static/js/learner_dashboard/spec/course_card_view_spec.js b/lms/static/js/learner_dashboard/spec/course_card_view_spec.js
index 5a0f181628..91439c4a87 100644
--- a/lms/static/js/learner_dashboard/spec/course_card_view_spec.js
+++ b/lms/static/js/learner_dashboard/spec/course_card_view_spec.js
@@ -17,10 +17,8 @@ describe('Course Card View', () => {
programData,
collectionCourseStatus,
courseData: {},
- subscriptionData: [],
urls: {},
userPreferences: {},
- isSubscriptionEligible: false,
};
if (typeof collectionCourseStatus === 'undefined') {
diff --git a/lms/static/js/learner_dashboard/spec/program_alert_list_view_spec.js b/lms/static/js/learner_dashboard/spec/program_alert_list_view_spec.js
deleted file mode 100644
index 501cb90004..0000000000
--- a/lms/static/js/learner_dashboard/spec/program_alert_list_view_spec.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/* globals setFixtures */
-
-import ProgramAlertListView from '../views/program_alert_list_view';
-
-describe('Program Alert List View', () => {
- let view = null;
- const context = {
- enrollmentAlerts: [{ title: 'Test Program' }],
- trialEndingAlerts: [{
- title: 'Test Program',
- hasActiveTrial: true,
- currentPeriodEnd: 'May 8, 2023',
- remainingDays: 2,
- subscriptionPrice: '$100/month USD',
- subscriptionState: 'active',
- subscriptionUrl: null,
- trialEndDate: 'Apr 20, 2023',
- trialEndTime: '5:59 am',
- trialLength: 7,
- }],
- pageType: 'programDetails',
- };
-
- beforeEach(() => {
- setFixtures('
');
- view = new ProgramAlertListView({
- el: '.js-program-details-alerts',
- context,
- });
- view.render();
- });
-
- afterEach(() => {
- view.remove();
- });
-
- it('should exist', () => {
- expect(view).toBeDefined();
- });
-
- it('should render no enrollement alert', () => {
- expect(view.$('.alert:first .alert-heading').text().trim()).toEqual(
- 'Enroll in a Test Program\'s course',
- );
- expect(view.$('.alert:first .alert-message').text().trim()).toEqual(
- 'You have an active subscription to the Test Program program but are not enrolled in any courses. Enroll in a remaining course and enjoy verified access.',
- );
- });
-
- it('should render subscription trial is expiring alert', () => {
- expect(view.$('.alert:last .alert-heading').text().trim()).toEqual(
- 'Subscription trial expires in 2 days',
- );
- expect(view.$('.alert:last .alert-message').text().trim()).toEqual(
- 'Your Test Program trial will expire in 2 days at 5:59 am on Apr 20, 2023 and the card on file will be charged $100/month USD.',
- );
- });
-});
diff --git a/lms/static/js/learner_dashboard/spec/program_card_view_spec.js b/lms/static/js/learner_dashboard/spec/program_card_view_spec.js
index 290db60a4d..bf8a718f0a 100644
--- a/lms/static/js/learner_dashboard/spec/program_card_view_spec.js
+++ b/lms/static/js/learner_dashboard/spec/program_card_view_spec.js
@@ -42,7 +42,6 @@ describe('Program card View', () => {
name: 'Wageningen University & Research',
},
],
- subscriptionIndex: 1,
};
const userProgress = [
{
@@ -58,11 +57,6 @@ describe('Program card View', () => {
not_started: 3,
},
];
- // eslint-disable-next-line no-undef
- const subscriptionCollection = new Backbone.Collection([{
- resource_id: 'a87e5eac-3c93-45a1-a8e1-4c79ca8401c8',
- subscription_state: 'active',
- }]);
const progressCollection = new ProgressCollection();
const cardRenders = ($card) => {
expect($card).toBeDefined();
@@ -80,8 +74,6 @@ describe('Program card View', () => {
model: programModel,
context: {
progressCollection,
- subscriptionCollection,
- isUserB2CSubscriptionsEnabled: true,
},
});
});
@@ -133,10 +125,6 @@ describe('Program card View', () => {
view.remove();
view = new ProgramCardView({
model: programModel,
- context: {
- subscriptionCollection,
- isUserB2CSubscriptionsEnabled: true,
- },
});
cardRenders(view.$el);
expect(view.$('.progress').length).toEqual(0);
@@ -149,10 +137,6 @@ describe('Program card View', () => {
programModel = new ProgramModel(programNoBanner);
view = new ProgramCardView({
model: programModel,
- context: {
- subscriptionCollection,
- isUserB2CSubscriptionsEnabled: true,
- },
});
cardRenders(view.$el);
expect(view.$el.find('.banner-image').attr('srcset')).toEqual('');
@@ -167,16 +151,8 @@ describe('Program card View', () => {
programModel = new ProgramModel(programNoBanner);
view = new ProgramCardView({
model: programModel,
- context: {
- subscriptionCollection,
- isUserB2CSubscriptionsEnabled: true,
- },
});
cardRenders(view.$el);
expect(view.$el.find('.banner-image').attr('srcset')).toEqual('');
});
-
- it('should render the subscription badge if subscription is active', () => {
- expect(view.$('.subscription-badge .badge').html()?.trim()).toEqual('Subscribed');
- });
});
diff --git a/lms/static/js/learner_dashboard/spec/program_details_header_spec.js b/lms/static/js/learner_dashboard/spec/program_details_header_spec.js
index d28d8f0bd3..862fb3f228 100644
--- a/lms/static/js/learner_dashboard/spec/program_details_header_spec.js
+++ b/lms/static/js/learner_dashboard/spec/program_details_header_spec.js
@@ -45,16 +45,6 @@ describe('Program Details Header View', () => {
},
],
},
- subscriptionData: [
- {
- trial_end: '1970-01-01T03:25:45Z',
- current_period_end: '1970-06-03T07:12:04Z',
- price: '100.00',
- currency: 'USD',
- subscription_state: 'active',
- },
- ],
- isSubscriptionEligible: true,
};
beforeEach(() => {
@@ -81,8 +71,4 @@ describe('Program Details Header View', () => {
expect(view.$('.org-logo').attr('alt'))
.toEqual(`${context.programData.authoring_organizations[0].name}'s logo`);
});
-
- it('should render the subscription badge if subscription is active', () => {
- expect(view.$('.meta-info .badge').html().trim()).toEqual('Subscribed');
- });
});
diff --git a/lms/static/js/learner_dashboard/spec/program_details_sidebar_view_spec.js b/lms/static/js/learner_dashboard/spec/program_details_sidebar_view_spec.js
index 60c877da8a..e1db3ddd18 100644
--- a/lms/static/js/learner_dashboard/spec/program_details_sidebar_view_spec.js
+++ b/lms/static/js/learner_dashboard/spec/program_details_sidebar_view_spec.js
@@ -1,9 +1,7 @@
/* globals setFixtures */
import Backbone from 'backbone';
-import moment from 'moment';
-import SubscriptionModel from '../models/program_subscription_model';
import ProgramSidebarView from '../views/program_details_sidebar_view';
describe('Program Progress View', () => {
@@ -25,15 +23,13 @@ describe('Program Progress View', () => {
"url": "/certificates/bed3980e67ca40f0b31e309d9dfe9e7e", "type": "course", "title": "Introduction to the Treatment of Urban Sewage"
}
],
- urls: {"program_listing_url": "/dashboard/programs/", "commerce_api_url": "/api/commerce/v0/baskets/", "track_selection_url": "/course_modes/choose/", "program_record_url": "/foo/bar", "buy_subscription_url": "/subscriptions", "orders_and_subscriptions_url": "/orders", "subscriptions_learner_help_center_url": "/learner"},
+ urls: {"program_listing_url": "/dashboard/programs/", "commerce_api_url": "/api/commerce/v0/baskets/", "track_selection_url": "/course_modes/choose/"},
userPreferences: {"pref-lang": "en"}
};
/* eslint-enable */
let programModel;
let courseData;
- let subscriptionData;
let certificateCollection;
- let isSubscriptionEligible;
const testCircle = (progress) => {
const $circle = view.$('.progress-circle');
@@ -53,55 +49,15 @@ describe('Program Progress View', () => {
expect(parseInt($numbers.find('.total').html(), 10)).toEqual(total);
};
- const testSubscriptionState = (state, heading, body) => {
- isSubscriptionEligible = true;
- subscriptionData.subscription_state = state;
- // eslint-disable-next-line no-use-before-define
- view = initView();
- // eslint-disable-next-line no-param-reassign
- body += ' on the Orders and subscriptions page';
-
- expect(view.$('.js-subscription-info')[0]).toBeInDOM();
- expect(
- view.$('.js-subscription-info .divider-heading').text().trim(),
- ).toEqual(heading);
- expect(
- view.$('.js-subscription-info .subscription-section p:nth-child(1)'),
- ).toContainHtml(body);
- expect(
- view.$('.js-subscription-info .subscription-section p:nth-child(2)'),
- ).toContainText(
- /Need help\? Check out the.*Learner Help Center.*to troubleshoot issues or contact support/,
- );
- expect(
- view.$('.js-subscription-info .subscription-section p:nth-child(2) .subscription-link').attr('href'),
- ).toEqual('/learner');
- };
-
const initView = () => new ProgramSidebarView({
el: '.js-program-sidebar',
model: programModel,
courseModel: courseData,
- subscriptionModel: new SubscriptionModel({
- context: {
- programData: {
- subscription_eligible: isSubscriptionEligible,
- subscription_prices: [{
- price: '100.00',
- currency: 'USD',
- }],
- },
- subscriptionData: [subscriptionData],
- urls: data.urls,
- userPreferences: data.userPreferences,
- },
- }),
certificateCollection,
industryPathways: data.industryPathways,
creditPathways: data.creditPathways,
programTabViewEnabled: false,
urls: data.urls,
- isSubscriptionEligible,
});
beforeEach(() => {
@@ -109,14 +65,6 @@ describe('Program Progress View', () => {
programModel = new Backbone.Model(data.programData);
courseData = new Backbone.Model(data.courseData);
certificateCollection = new Backbone.Collection(data.certificateData);
- isSubscriptionEligible = false;
- subscriptionData = {
- trial_end: '1970-01-01T03:25:45Z',
- current_period_end: '1970-06-03T07:12:04Z',
- price: '100.00',
- currency: 'USD',
- subscription_state: 'pre',
- };
});
afterEach(() => {
@@ -203,69 +151,14 @@ describe('Program Progress View', () => {
el: '.js-program-sidebar',
model: programModel,
courseModel: courseData,
- subscriptionModel: new SubscriptionModel({
- context: {
- programData: {
- subscription_eligible: isSubscriptionEligible,
- subscription_prices: [{
- price: '100.00',
- currency: 'USD',
- }],
- },
- subscriptionData: [subscriptionData],
- urls: data.urls,
- userPreferences: data.userPreferences,
- },
- }),
certificateCollection,
industryPathways: [],
creditPathways: [],
programTabViewEnabled: false,
urls: data.urls,
- isSubscriptionEligible,
});
expect(emptyView.$('.program-credit-pathways .divider-heading')).toHaveLength(0);
expect(emptyView.$('.program-industry-pathways .divider-heading')).toHaveLength(0);
});
-
- it('should not render subscription info if program is not subscription eligible', () => {
- view = initView();
- expect(view.$('.js-subscription-info')[0]).not.toBeInDOM();
- });
-
- it('should render subscription info if program is subscription eligible', () => {
- testSubscriptionState(
- 'pre',
- 'Inactive subscription',
- 'If you had a subscription previously, your payment history is still available',
- );
- });
-
- it('should render active trial subscription info if subscription is active with trial', () => {
- subscriptionData.trial_end = moment().add(3, 'days').utc().format(
- 'YYYY-MM-DDTHH:mm:ss[Z]',
- );
- testSubscriptionState(
- 'active',
- 'Trial subscription',
- 'View your receipts or modify your subscription',
- );
- });
-
- it('should render active subscription info if subscription active', () => {
- testSubscriptionState(
- 'active',
- 'Active subscription',
- 'View your receipts or modify your subscription',
- );
- });
-
- it('should render inactive subscription info if subscription inactive', () => {
- testSubscriptionState(
- 'inactive',
- 'Inactive subscription',
- 'Restart your subscription for $100/month USD. Your payment history is still available',
- );
- });
});
diff --git a/lms/static/js/learner_dashboard/spec/program_details_view_spec.js b/lms/static/js/learner_dashboard/spec/program_details_view_spec.js
index feaf725261..a3be0f1081 100644
--- a/lms/static/js/learner_dashboard/spec/program_details_view_spec.js
+++ b/lms/static/js/learner_dashboard/spec/program_details_view_spec.js
@@ -7,11 +7,6 @@ describe('Program Details View', () => {
let view = null;
const options = {
programData: {
- subscription_eligible: false,
- subscription_prices: [{
- price: '100.00',
- currency: 'USD',
- }],
subtitle: '',
overview: '',
weeks_to_complete: null,
@@ -468,24 +463,11 @@ describe('Program Details View', () => {
},
],
},
- subscriptionData: [
- {
- trial_end: '1970-01-01T03:25:45Z',
- current_period_end: '1970-06-03T07:12:04Z',
- price: '100.00',
- currency: 'USD',
- subscription_state: 'pre',
- },
- ],
urls: {
program_listing_url: '/dashboard/programs/',
commerce_api_url: '/api/commerce/v0/baskets/',
track_selection_url: '/course_modes/choose/',
program_record_url: 'http://credentials.example.com/records/programs/UUID',
- buy_subscription_url: '/subscriptions',
- manage_subscription_url: '/orders',
- subscriptions_learner_help_center_url: '/learner',
- orders_and_subscriptions_url: '/orders',
},
userPreferences: {
'pref-lang': 'en',
@@ -513,59 +495,9 @@ describe('Program Details View', () => {
},
],
programTabViewEnabled: false,
- isUserB2CSubscriptionsEnabled: false,
};
const data = options.programData;
- const testSubscriptionState = (state, heading, body, trial = false) => {
- const subscriptionData = {
- ...options.subscriptionData[0],
- subscription_state: state,
- };
- if (trial) {
- subscriptionData.trial_end = moment().add(3, 'days').utc().format(
- 'YYYY-MM-DDTHH:mm:ss[Z]',
- );
- }
- // eslint-disable-next-line no-use-before-define
- view = initView({
- // eslint-disable-next-line no-undef
- programData: $.extend({}, options.programData, {
- subscription_eligible: true,
- }),
- isUserB2CSubscriptionsEnabled: true,
- subscriptionData: [subscriptionData],
- });
- view.render();
- expect(view.$('.upgrade-subscription')[0]).toBeInDOM();
- expect(view.$('.upgrade-subscription .upgrade-button'))
- .toContainText(heading);
- expect(view.$('.upgrade-subscription .subscription-info-brief'))
- .toContainText(body);
- };
-
- const testSubscriptionSunsetting = (state, heading, body) => {
- const subscriptionData = {
- ...options.subscriptionData[0],
- subscription_state: state,
- };
- // eslint-disable-next-line no-use-before-define
- view = initView({
- // eslint-disable-next-line no-undef
- programData: $.extend({}, options.programData, {
- subscription_eligible: false,
- }),
- isUserB2CSubscriptionsEnabled: true,
- subscriptionData: [subscriptionData],
- });
- view.render();
- expect(view.$('.upgrade-subscription')[0]).not.toBeInDOM();
- expect(view.$('.upgrade-subscription .upgrade-button')).not
- .toContainText(heading);
- expect(view.$('.upgrade-subscription .subscription-info-brief')).not
- .toContainText(body);
- };
-
const initView = (updates) => {
// eslint-disable-next-line no-undef
const viewOptions = $.extend({}, options, updates);
@@ -730,37 +662,4 @@ describe('Program Details View', () => {
properties,
);
});
-
- it('should not render the get subscription link if program is not active', () => {
- testSubscriptionSunsetting(
- 'pre',
- 'Start 7-day free trial',
- '$100/month USD subscription after trial ends. Cancel anytime.',
- );
- });
-
- it('should not render appropriate subscription text when subscription is active with trial', () => {
- testSubscriptionSunsetting(
- 'active',
- 'Manage my subscription',
- 'Trial ends',
- true,
- );
- });
-
- it('should not render appropriate subscription text when subscription is active', () => {
- testSubscriptionSunsetting(
- 'active',
- 'Manage my subscription',
- 'Your next billing date is',
- );
- });
-
- it('should not render appropriate subscription text when subscription is inactive', () => {
- testSubscriptionSunsetting(
- 'inactive',
- 'Restart my subscription',
- '$100/month USD subscription. Cancel anytime.',
- );
- });
});
diff --git a/lms/static/js/learner_dashboard/spec/program_list_header_view_spec.js b/lms/static/js/learner_dashboard/spec/program_list_header_view_spec.js
index 4a663fc1f8..5e1c09bfe4 100644
--- a/lms/static/js/learner_dashboard/spec/program_list_header_view_spec.js
+++ b/lms/static/js/learner_dashboard/spec/program_list_header_view_spec.js
@@ -13,27 +13,14 @@ describe('Program List Header View', () => {
{
uuid: '5b234e3c-3a2e-472e-90db-6f51501dc86c',
title: 'edX Demonstration Program',
- subscription_eligible: null,
- subscription_prices: [],
detail_url: '/dashboard/programs/5b234e3c-3a2e-472e-90db-6f51501dc86c/',
},
{
uuid: 'b90d70d5-f981-4508-bdeb-5b792d930c03',
title: 'Test Program',
- subscription_eligible: true,
- subscription_prices: [{ price: '500.00', currency: 'USD' }],
detail_url: '/dashboard/programs/b90d70d5-f981-4508-bdeb-5b792d930c03/',
},
],
- programsSubscriptionData: [
- {
- id: 'eeb25640-9741-4c11-963c-8a27337f217c',
- resource_id: 'b90d70d5-f981-4508-bdeb-5b792d930c03',
- trial_end: '2022-04-20T05:59:42Z',
- current_period_end: '2023-05-08T05:59:42Z',
- subscription_state: 'active',
- },
- ],
userProgress: [
{
uuid: '5b234e3c-3a2e-472e-90db-6f51501dc86c',
@@ -50,13 +37,9 @@ describe('Program List Header View', () => {
all_unenrolled: true,
},
],
- isUserB2CSubscriptionsEnabled: true,
};
beforeEach(() => {
- context.subscriptionCollection = new Backbone.Collection(
- context.programsSubscriptionData,
- );
context.progressCollection = new ProgressCollection(
context.userProgress,
);
@@ -78,18 +61,4 @@ describe('Program List Header View', () => {
it('should render the program heading', () => {
expect(view.$('h2:first').text().trim()).toEqual('My programs');
});
-
- it('should render a program alert', () => {
- expect(
- view.$('.js-program-list-alerts .alert .alert-heading').html().trim(),
- ).toEqual('Enroll in a Test Program\'s course');
- expect(
- view.$('.js-program-list-alerts .alert .alert-message'),
- ).toContainHtml(
- 'According to our records, you are not enrolled in any courses included in your Test Program program subscription. Enroll in a course from the Program Details page.',
- );
- expect(
- view.$('.js-program-list-alerts .alert .view-button').attr('href'),
- ).toEqual('/dashboard/programs/b90d70d5-f981-4508-bdeb-5b792d930c03/');
- });
});
diff --git a/lms/static/js/learner_dashboard/spec/sidebar_view_spec.js b/lms/static/js/learner_dashboard/spec/sidebar_view_spec.js
index 04c936908e..e96369abb6 100644
--- a/lms/static/js/learner_dashboard/spec/sidebar_view_spec.js
+++ b/lms/static/js/learner_dashboard/spec/sidebar_view_spec.js
@@ -6,12 +6,6 @@ describe('Sidebar View', () => {
let view = null;
const context = {
marketingUrl: 'https://www.example.org/programs',
- subscriptionUpsellData: {
- marketing_url: 'https://www.example.org/program-subscriptions',
- minimum_price: '$39',
- trial_length: 7,
- },
- isUserB2CSubscriptionsEnabled: true,
};
beforeEach(() => {
@@ -32,10 +26,6 @@ describe('Sidebar View', () => {
expect(view).toBeDefined();
});
- it('should not render the subscription upsell section', () => {
- expect(view.$('.js-subscription-upsell')[0]).not.toBeInDOM();
- });
-
it('should load the exploration panel given a marketing URL', () => {
expect(view.$('.program-advertise .advertise-message').html().trim())
.toEqual(
@@ -49,10 +39,6 @@ describe('Sidebar View', () => {
view.remove();
view = new SidebarView({
el: '.sidebar',
- context: {
- isUserB2CSubscriptionsEnabled: true,
- subscriptionUpsellData: context.subscriptionUpsellData,
- },
});
view.render();
const $ad = view.$el.find('.program-advertise');
diff --git a/lms/static/js/learner_dashboard/views/course_card_view.js b/lms/static/js/learner_dashboard/views/course_card_view.js
index 72028d6d95..dce9c7a384 100644
--- a/lms/static/js/learner_dashboard/views/course_card_view.js
+++ b/lms/static/js/learner_dashboard/views/course_card_view.js
@@ -9,8 +9,6 @@ import ExpiredNotificationView from './expired_notification_view';
import CourseEnrollView from './course_enroll_view';
import EntitlementView from './course_entitlement_view';
-import SubscriptionModel from '../models/program_subscription_model';
-
import pageTpl from '../../../templates/learner_dashboard/course_card.underscore';
class CourseCardView extends Backbone.View {
@@ -27,9 +25,6 @@ class CourseCardView extends Backbone.View {
this.enrollModel = new EnrollModel();
if (options.context) {
this.urlModel = new Backbone.Model(options.context.urls);
- this.subscriptionModel = new SubscriptionModel({
- context: options.context,
- });
this.enrollModel.urlRoot = this.urlModel.get('commerce_api_url');
}
this.context = options.context || {};
@@ -93,8 +88,6 @@ class CourseCardView extends Backbone.View {
this.upgradeMessage = new UpgradeMessageView({
$el: $upgradeMessage,
model: this.model,
- subscriptionModel: this.subscriptionModel,
- isSubscriptionEligible: this.context.isSubscriptionEligible,
});
$certStatus.remove();
diff --git a/lms/static/js/learner_dashboard/views/program_alert_list_view.js b/lms/static/js/learner_dashboard/views/program_alert_list_view.js
deleted file mode 100644
index 6c42d85444..0000000000
--- a/lms/static/js/learner_dashboard/views/program_alert_list_view.js
+++ /dev/null
@@ -1,89 +0,0 @@
-import Backbone from 'backbone';
-
-import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
-import StringUtils from 'edx-ui-toolkit/js/utils/string-utils';
-
-import warningIcon from '../../../images/warning-icon.svg';
-import programAlertTpl from '../../../templates/learner_dashboard/program_alert_list_view.underscore';
-
-class ProgramAlertListView extends Backbone.View {
- constructor(options) {
- const defaults = {
- el: '.js-program-details-alerts',
- };
- // eslint-disable-next-line prefer-object-spread
- super(Object.assign({}, defaults, options));
- }
-
- initialize({ context }) {
- this.tpl = HtmlUtils.template(programAlertTpl);
- this.enrollmentAlerts = context.enrollmentAlerts || [];
- this.trialEndingAlerts = context.trialEndingAlerts || [];
- this.pageType = context.pageType;
- this.render();
- }
-
- render() {
- const data = {
- alertList: this.getAlertList(),
- warningIcon,
- };
- HtmlUtils.setHtml(this.$el, this.tpl(data));
- }
-
- getAlertList() {
- const alertList = this.enrollmentAlerts.map(
- ({ title: programName, url }) => ({
- url,
- // eslint-disable-next-line no-undef
- urlText: gettext('View program'),
- title: StringUtils.interpolate(
- // eslint-disable-next-line no-undef
- gettext('Enroll in a {programName}\'s course'),
- { programName },
- ),
- message: this.pageType === 'programDetails'
- ? StringUtils.interpolate(
- // eslint-disable-next-line no-undef
- gettext('You have an active subscription to the {programName} program but are not enrolled in any courses. Enroll in a remaining course and enjoy verified access.'),
- { programName },
- )
- : HtmlUtils.interpolateHtml(
- // eslint-disable-next-line no-undef
- gettext('According to our records, you are not enrolled in any courses included in your {programName} program subscription. Enroll in a course from the {i_start}Program Details{i_end} page.'),
- {
- programName,
- i_start: HtmlUtils.HTML(''),
- i_end: HtmlUtils.HTML(' '),
- },
- ),
- }),
- );
- return alertList.concat(this.trialEndingAlerts.map(
- ({ title: programName, remainingDays, ...data }) => ({
- title: StringUtils.interpolate(
- remainingDays < 1
- // eslint-disable-next-line no-undef
- ? gettext('Subscription trial expires in less than 24 hours')
- // eslint-disable-next-line no-undef
- : ngettext('Subscription trial expires in {remainingDays} day', 'Subscription trial expires in {remainingDays} days', remainingDays),
- { remainingDays },
- ),
- message: StringUtils.interpolate(
- remainingDays < 1
- // eslint-disable-next-line no-undef
- ? gettext('Your {programName} trial will expire at {trialEndTime} on {trialEndDate} and the card on file will be charged {subscriptionPrice}.')
- // eslint-disable-next-line no-undef
- : ngettext('Your {programName} trial will expire in {remainingDays} day at {trialEndTime} on {trialEndDate} and the card on file will be charged {subscriptionPrice}.', 'Your {programName} trial will expire in {remainingDays} days at {trialEndTime} on {trialEndDate} and the card on file will be charged {subscriptionPrice}.', remainingDays),
- {
- programName,
- remainingDays,
- ...data,
- },
- ),
- }),
- ));
- }
-}
-
-export default ProgramAlertListView;
diff --git a/lms/static/js/learner_dashboard/views/program_card_view.js b/lms/static/js/learner_dashboard/views/program_card_view.js
index 1a5a053135..f4715e2538 100644
--- a/lms/static/js/learner_dashboard/views/program_card_view.js
+++ b/lms/static/js/learner_dashboard/views/program_card_view.js
@@ -30,10 +30,6 @@ class ProgramCardView extends Backbone.View {
uuid: this.model.get('uuid'),
});
}
- this.isSubscribed = (
- context.isUserB2CSubscriptionsEnabled &&
- this.model.get('subscriptionIndex') > -1
- ) ?? false;
this.render();
}
@@ -45,7 +41,6 @@ class ProgramCardView extends Backbone.View {
this.getProgramProgress(),
{
orgList: orgList.join(' '),
- isSubscribed: this.isSubscribed,
},
);
diff --git a/lms/static/js/learner_dashboard/views/program_details_sidebar_view.js b/lms/static/js/learner_dashboard/views/program_details_sidebar_view.js
index fea4ebd809..fa8ccb629b 100644
--- a/lms/static/js/learner_dashboard/views/program_details_sidebar_view.js
+++ b/lms/static/js/learner_dashboard/views/program_details_sidebar_view.js
@@ -30,9 +30,7 @@ class ProgramDetailsSidebarView extends Backbone.View {
this.industryPathways = options.industryPathways;
this.creditPathways = options.creditPathways;
this.programModel = options.model;
- this.subscriptionModel = options.subscriptionModel;
this.programTabViewEnabled = options.programTabViewEnabled;
- this.isSubscriptionEligible = options.isSubscriptionEligible;
this.urls = options.urls;
this.render();
}
@@ -42,14 +40,12 @@ class ProgramDetailsSidebarView extends Backbone.View {
const data = $.extend(
{},
this.model.toJSON(),
- this.subscriptionModel.toJSON(),
{
programCertificate: this.programCertificate
? this.programCertificate.toJSON() : {},
industryPathways: this.industryPathways,
creditPathways: this.creditPathways,
programTabViewEnabled: this.programTabViewEnabled,
- isSubscriptionEligible: this.isSubscriptionEligible,
arrowUprightIcon,
...this.urls,
},
diff --git a/lms/static/js/learner_dashboard/views/program_details_view.js b/lms/static/js/learner_dashboard/views/program_details_view.js
index 220840c182..006d30c59b 100644
--- a/lms/static/js/learner_dashboard/views/program_details_view.js
+++ b/lms/static/js/learner_dashboard/views/program_details_view.js
@@ -10,10 +10,6 @@ import CourseCardView from './course_card_view';
// eslint-disable-next-line import/no-named-as-default, import/no-named-as-default-member
import HeaderView from './program_header_view';
import SidebarView from './program_details_sidebar_view';
-import AlertListView from './program_alert_list_view';
-
-// eslint-disable-next-line import/no-named-as-default, import/no-named-as-default-member
-import SubscriptionModel from '../models/program_subscription_model';
import launchIcon from '../../../images/launch-icon.svg';
import restartIcon from '../../../images/restart-icon.svg';
@@ -27,7 +23,6 @@ class ProgramDetailsView extends Backbone.View {
el: '.js-program-details-wrapper',
events: {
'click .complete-program': 'trackPurchase',
- 'click .js-subscription-cta': 'trackSubscriptionCTA',
},
};
// eslint-disable-next-line prefer-object-spread
@@ -46,9 +41,6 @@ class ProgramDetailsView extends Backbone.View {
this.certificateCollection = new Backbone.Collection(
this.options.certificateData,
);
- this.subscriptionModel = new SubscriptionModel({
- context: this.options,
- });
this.completedCourseCollection = new CourseCardCollection(
this.courseData.get('completed') || [],
this.options.userPreferences,
@@ -61,11 +53,6 @@ class ProgramDetailsView extends Backbone.View {
this.courseData.get('not_started') || [],
this.options.userPreferences,
);
- this.subscriptionEventParams = {
- label: this.options.programData.title,
- program_uuid: this.options.programData.uuid,
- };
- this.options.isSubscriptionEligible = this.getIsSubscriptionEligible();
this.render();
@@ -76,7 +63,6 @@ class ProgramDetailsView extends Backbone.View {
pageName: 'program_dashboard',
linkCategory: 'green_upgrade',
});
- this.trackSubscriptionEligibleProgramView();
}
static getUrl(base, programData) {
@@ -107,7 +93,6 @@ class ProgramDetailsView extends Backbone.View {
creditPathways: this.options.creditPathways,
discussionFragment: this.options.discussionFragment,
live_fragment: this.options.live_fragment,
- isSubscriptionEligible: this.options.isSubscriptionEligible,
launchIcon,
restartIcon,
};
@@ -115,7 +100,6 @@ class ProgramDetailsView extends Backbone.View {
data = $.extend(
data,
this.programModel.toJSON(),
- this.subscriptionModel.toJSON(),
);
HtmlUtils.setHtml(this.$el, this.tpl(data));
this.postRender();
@@ -126,20 +110,6 @@ class ProgramDetailsView extends Backbone.View {
model: new Backbone.Model(this.options),
});
- if (this.options.isSubscriptionEligible) {
- const { enrollmentAlerts, trialEndingAlerts } = this.getAlerts();
-
- if (enrollmentAlerts.length || trialEndingAlerts.length) {
- this.alertListView = new AlertListView({
- context: {
- enrollmentAlerts,
- trialEndingAlerts,
- pageType: 'programDetails',
- },
- });
- }
- }
-
if (this.remainingCourseCollection.length > 0) {
new CollectionListView({
el: '.js-course-list-remaining',
@@ -178,12 +148,10 @@ class ProgramDetailsView extends Backbone.View {
el: '.js-program-sidebar',
model: this.programModel,
courseModel: this.courseData,
- subscriptionModel: this.subscriptionModel,
certificateCollection: this.certificateCollection,
industryPathways: this.options.industryPathways,
creditPathways: this.options.creditPathways,
programTabViewEnabled: this.options.programTabViewEnabled,
- isSubscriptionEligible: this.options.isSubscriptionEligible,
urls: this.options.urls,
});
let hasIframe = false;
@@ -197,59 +165,6 @@ class ProgramDetailsView extends Backbone.View {
}).bind(this);
}
- getIsSubscriptionEligible() {
- const courseCollections = [
- this.completedCourseCollection,
- this.inProgressCourseCollection,
- ];
- const isSomeCoursePurchasable = courseCollections.some((collection) => (
- collection.some((course) => (
- course.get('upgrade_url')
- && !(course.get('expired') === true)
- ))
- ));
- const programPurchasedWithoutSubscription = (
- this.subscriptionModel.get('subscriptionState') !== 'active'
- && this.subscriptionModel.get('subscriptionState') !== 'inactive'
- && !isSomeCoursePurchasable
- && this.remainingCourseCollection.length === 0
- );
-
- const isSubscriptionActiveSunsetting = (
- this.subscriptionModel.get('subscriptionState') === 'active'
- )
-
- return (
- this.options.isUserB2CSubscriptionsEnabled
- && isSubscriptionActiveSunsetting
- && !programPurchasedWithoutSubscription
- );
- }
-
- getAlerts() {
- const alerts = {
- enrollmentAlerts: [],
- trialEndingAlerts: [],
- };
- if (this.subscriptionModel.get('subscriptionState') === 'active') {
- if (this.courseData.get('all_unenrolled')) {
- alerts.enrollmentAlerts.push({
- title: this.programModel.get('title'),
- });
- }
- if (
- this.subscriptionModel.get('remainingDays') <= 7
- && this.subscriptionModel.get('hasActiveTrial')
- ) {
- alerts.trialEndingAlerts.push({
- title: this.programModel.get('title'),
- ...this.subscriptionModel.toJSON(),
- });
- }
- }
- return alerts;
- }
-
trackPurchase() {
const data = this.options.programData;
window.analytics.track('edx.bi.user.dashboard.program.purchase', {
@@ -258,37 +173,6 @@ class ProgramDetailsView extends Backbone.View {
uuid: data.uuid,
});
}
-
- trackSubscriptionCTA() {
- const state = this.subscriptionModel.get('subscriptionState');
-
- if (state === 'active') {
- window.analytics.track(
- 'edx.bi.user.subscription.program-detail-page.manage.clicked',
- this.subscriptionEventParams,
- );
- } else {
- const isNewSubscription = state !== 'inactive';
- window.analytics.track(
- 'edx.bi.user.subscription.program-detail-page.subscribe.clicked',
- {
- category: `${this.options.programData.variant} bundle`,
- is_new_subscription: isNewSubscription,
- is_trial_eligible: isNewSubscription,
- ...this.subscriptionEventParams,
- },
- );
- }
- }
-
- trackSubscriptionEligibleProgramView() {
- if (this.options.isSubscriptionEligible) {
- window.analytics.track(
- 'edx.bi.user.subscription.program-detail-page.viewed',
- this.subscriptionEventParams,
- );
- }
- }
}
export default ProgramDetailsView;
diff --git a/lms/static/js/learner_dashboard/views/program_header_view.js b/lms/static/js/learner_dashboard/views/program_header_view.js
index 2fd8e9fe51..acb3c876ca 100644
--- a/lms/static/js/learner_dashboard/views/program_header_view.js
+++ b/lms/static/js/learner_dashboard/views/program_header_view.js
@@ -42,22 +42,11 @@ class ProgramHeaderView extends Backbone.View {
return logo;
}
- getIsSubscribed() {
- const isSubscriptionEligible = this.model.get('isSubscriptionEligible');
- const subscriptionData = this.model.get('subscriptionData')?.[0];
-
- return (
- isSubscriptionEligible &&
- subscriptionData?.subscription_state === 'active'
- );
- }
-
render() {
// eslint-disable-next-line no-undef
const data = $.extend(this.model.toJSON(), {
breakpoints: this.breakpoints,
logo: this.getLogo(),
- isSubscribed: this.getIsSubscribed(),
});
if (this.model.get('programData')) {
diff --git a/lms/static/js/learner_dashboard/views/program_list_header_view.js b/lms/static/js/learner_dashboard/views/program_list_header_view.js
index 6520caf086..98e628cefa 100644
--- a/lms/static/js/learner_dashboard/views/program_list_header_view.js
+++ b/lms/static/js/learner_dashboard/views/program_list_header_view.js
@@ -2,10 +2,6 @@ import Backbone from 'backbone';
import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
-import AlertListView from './program_alert_list_view';
-
-import SubscriptionModel from '../models/program_subscription_model';
-
import programListHeaderTpl from '../../../templates/learner_dashboard/program_list_header_view.underscore';
class ProgramListHeaderView extends Backbone.View {
@@ -19,76 +15,11 @@ class ProgramListHeaderView extends Backbone.View {
initialize({ context }) {
this.context = context;
this.tpl = HtmlUtils.template(programListHeaderTpl);
- this.programAndSubscriptionData = context.programsData
- .map((programData) => ({
- programData,
- subscriptionData: context.subscriptionCollection
- ?.findWhere({
- resource_id: programData.uuid,
- subscription_state: 'active',
- })
- ?.toJSON(),
- }))
- .filter(({ subscriptionData }) => !!subscriptionData);
this.render();
}
render() {
HtmlUtils.setHtml(this.$el, this.tpl(this.context));
- this.postRender();
- }
-
- postRender() {
- if (this.context.isUserB2CSubscriptionsEnabled) {
- const enrollmentAlerts = this.getEnrollmentAlerts();
- const trialEndingAlerts = this.getTrialEndingAlerts();
-
- if (enrollmentAlerts.length || trialEndingAlerts.length) {
- this.alertListView = new AlertListView({
- el: '.js-program-list-alerts',
- context: {
- enrollmentAlerts,
- trialEndingAlerts,
- pageType: 'programList',
- },
- });
- }
- }
- }
-
- getEnrollmentAlerts() {
- return this.programAndSubscriptionData
- .map(({ programData, subscriptionData }) =>
- this.context.progressCollection?.findWhere({
- uuid: programData.uuid,
- all_unenrolled: true,
- }) ? {
- title: programData.title,
- url: programData.detail_url,
- } : null
- )
- .filter(Boolean);
- }
-
- getTrialEndingAlerts() {
- return this.programAndSubscriptionData
- .map(({ programData, subscriptionData }) => {
- const subscriptionModel = new SubscriptionModel({
- context: {
- programData,
- subscriptionData: [subscriptionData],
- userPreferences: this.context?.userPreferences,
- },
- });
- return (
- subscriptionModel.get('remainingDays') <= 7 &&
- subscriptionModel.get('hasActiveTrial') && {
- title: programData.title,
- ...subscriptionModel.toJSON(),
- }
- );
- })
- .filter(Boolean);
}
}
diff --git a/lms/static/js/learner_dashboard/views/sidebar_view.js b/lms/static/js/learner_dashboard/views/sidebar_view.js
index 3359eac1b4..520efbe29f 100644
--- a/lms/static/js/learner_dashboard/views/sidebar_view.js
+++ b/lms/static/js/learner_dashboard/views/sidebar_view.js
@@ -10,9 +10,6 @@ class SidebarView extends Backbone.View {
constructor(options) {
const defaults = {
el: '.sidebar',
- events: {
- 'click .js-subscription-upsell-cta ': 'trackSubscriptionUpsellCTA',
- },
};
// eslint-disable-next-line prefer-object-spread
super(Object.assign({}, defaults, options));
@@ -33,12 +30,6 @@ class SidebarView extends Backbone.View {
context: this.context,
});
}
-
- trackSubscriptionUpsellCTA() {
- window.analytics.track(
- 'edx.bi.user.subscription.program-dashboard.upsell.clicked',
- );
- }
}
export default SidebarView;
diff --git a/lms/static/js/learner_dashboard/views/subscription_upsell_view.js b/lms/static/js/learner_dashboard/views/subscription_upsell_view.js
deleted file mode 100644
index 3c085aaf7e..0000000000
--- a/lms/static/js/learner_dashboard/views/subscription_upsell_view.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import Backbone from 'backbone';
-
-import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
-
-import subscriptionUpsellTpl from '../../../templates/learner_dashboard/subscription_upsell_view.underscore';
-
-class SubscriptionUpsellView extends Backbone.View {
- constructor(options) {
- const defaults = {
- el: '.js-subscription-upsell',
- };
- // eslint-disable-next-line prefer-object-spread
- super(Object.assign({}, defaults, options));
- }
-
- initialize(options) {
- this.tpl = HtmlUtils.template(subscriptionUpsellTpl);
- this.subscriptionUpsellModel = new Backbone.Model(
- options.subscriptionUpsellData,
- );
- this.render();
- }
-
- render() {
- const data = this.subscriptionUpsellModel.toJSON();
- HtmlUtils.setHtml(this.$el, this.tpl(data));
- }
-}
-
-export default SubscriptionUpsellView;
diff --git a/lms/static/js/learner_dashboard/views/upgrade_message_view.js b/lms/static/js/learner_dashboard/views/upgrade_message_view.js
index 07d1b9522e..c8ad363286 100644
--- a/lms/static/js/learner_dashboard/views/upgrade_message_view.js
+++ b/lms/static/js/learner_dashboard/views/upgrade_message_view.js
@@ -3,18 +3,12 @@ import Backbone from 'backbone';
import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
import upgradeMessageTpl from '../../../templates/learner_dashboard/upgrade_message.underscore';
-import upgradeMessageSubscriptionTpl from '../../../templates/learner_dashboard/upgrade_message_subscription.underscore';
import trackECommerceEvents from '../../commerce/track_ecommerce_events';
class UpgradeMessageView extends Backbone.View {
initialize(options) {
- if (options.isSubscriptionEligible) {
- this.messageTpl = HtmlUtils.template(upgradeMessageSubscriptionTpl);
- } else {
- this.messageTpl = HtmlUtils.template(upgradeMessageTpl);
- }
+ this.messageTpl = HtmlUtils.template(upgradeMessageTpl);
this.$el = options.$el;
- this.subscriptionModel = options.subscriptionModel;
this.render();
const courseUpsellButtons = this.$el.find('.program_dashboard_course_upsell_button');
@@ -30,7 +24,6 @@ class UpgradeMessageView extends Backbone.View {
const data = $.extend(
{},
this.model.toJSON(),
- this.subscriptionModel.toJSON(),
);
HtmlUtils.setHtml(this.$el, this.messageTpl(data));
}
diff --git a/lms/static/sass/views/_program-details.scss b/lms/static/sass/views/_program-details.scss
index 9056f04a13..f5a6eb62b5 100644
--- a/lms/static/sass/views/_program-details.scss
+++ b/lms/static/sass/views/_program-details.scss
@@ -90,21 +90,6 @@ $btn-color-primary: $primary-dark;
}
}
-.program-details-alerts {
- .page-banner {
- margin: 0;
- padding: 0 0 48px;
- gap: 24px;
- }
-}
-
-.program-details-tab-alerts {
- .page-banner {
- margin: 0;
- gap: 24px;
- }
-}
-
// CSS for April 2017 version of Program Details Page
.program-details {
.window-wrap {
@@ -449,42 +434,6 @@ $btn-color-primary: $primary-dark;
}
}
- .upgrade-subscription {
- margin: 16px 0 10px;
- row-gap: 16px;
- column-gap: 24px;
- }
-
- .subscription-icon-launch {
- width: 22.5px;
- height: 22.5px;
- margin-inline-start: 8px;
- }
-
- .subscription-icon-restart {
- width: 22.5px;
- height: 22.5px;
- margin-inline-end: 8px;
- }
-
- .subscription-icon-arrow-upright {
- display: inline-flex;
- align-items: center;
- width: 15px;
- height: 15px;
- margin-inline-start: 8px;
- }
-
- .subscription-info-brief {
- font-size: 0.9375em;
- color: $gray-500;
- }
-
- .subscription-info-upsell {
- margin-top: 0.25rem;
- font-size: 0.8125em;
- }
-
.program-course-card {
width: 100%;
padding: 15px 15px 15px 0px;
@@ -681,24 +630,6 @@ $btn-color-primary: $primary-dark;
.program-sidebar {
padding: 40px 40px 40px 0px;
- .program-record,.subscription-info {
- text-align: left;
- padding-bottom: 2em;
- }
-
- .subscription-section {
- display: flex;
- flex-direction: column;
- gap: 16px;
- color: #414141;
-
- .subscription-link {
- color: inherit;
- text-decoration: none;
- border-bottom: 1px solid currentColor;
- }
- }
-
.sidebar-section {
font-size: 0.9375em;
width: auto;
diff --git a/lms/static/sass/views/_program-list.scss b/lms/static/sass/views/_program-list.scss
index 23f9a78b7c..d05e2eb285 100644
--- a/lms/static/sass/views/_program-list.scss
+++ b/lms/static/sass/views/_program-list.scss
@@ -39,13 +39,6 @@
.program-cards-container {
@include grid-container();
padding-top: 32px;
-
- .subscription-badge {
- position: absolute;
- top: 8px;
- left: 8px;
- z-index: 10;
- }
}
.sidebar {
diff --git a/lms/templates/learner_dashboard/program_card.underscore b/lms/templates/learner_dashboard/program_card.underscore
index c9364d6ca2..de98c952dd 100644
--- a/lms/templates/learner_dashboard/program_card.underscore
+++ b/lms/templates/learner_dashboard/program_card.underscore
@@ -61,8 +61,3 @@
-<% if (isSubscribed) { %>
-
- <%- gettext('Subscribed') %>
-
-<% } %>
diff --git a/lms/templates/learner_dashboard/program_details_fragment.html b/lms/templates/learner_dashboard/program_details_fragment.html
index 7aff07a6a3..70571ca80f 100644
--- a/lms/templates/learner_dashboard/program_details_fragment.html
+++ b/lms/templates/learner_dashboard/program_details_fragment.html
@@ -14,7 +14,6 @@ from openedx.core.djangolib.js_utils import (
<%static:webpack entry="ProgramDetailsFactory">
ProgramDetailsFactory({
programData: ${program_data | n, dump_js_escaped_json},
- subscriptionData: ${program_subscription_data | n, dump_js_escaped_json},
courseData: ${course_data | n, dump_js_escaped_json},
certificateData: ${certificate_data | n, dump_js_escaped_json},
urls: ${urls | n, dump_js_escaped_json},
@@ -22,8 +21,6 @@ ProgramDetailsFactory({
industryPathways: ${industry_pathways | n, dump_js_escaped_json},
creditPathways: ${credit_pathways | n, dump_js_escaped_json},
programTabViewEnabled: ${program_tab_view_enabled | n, dump_js_escaped_json},
- isUserB2CSubscriptionsEnabled: ${is_user_b2c_subscriptions_enabled | n, dump_js_escaped_json},
- subscriptionsTrialLength: ${subscriptions_trial_length | n, dump_js_escaped_json},
discussionFragment: ${discussion_fragment, | n, dump_js_escaped_json},
live_fragment: ${live_fragment, | n, dump_js_escaped_json}
});
diff --git a/lms/templates/learner_dashboard/program_details_sidebar.underscore b/lms/templates/learner_dashboard/program_details_sidebar.underscore
index cab7aad04b..0e05ae9b9a 100644
--- a/lms/templates/learner_dashboard/program_details_sidebar.underscore
+++ b/lms/templates/learner_dashboard/program_details_sidebar.underscore
@@ -8,50 +8,6 @@
<% } %>
-<% if (isSubscriptionEligible) { %>
-
-
- <%- hasActiveTrial
- ? gettext('Trial subscription')
- : subscriptionState === 'active'
- ? gettext('Active subscription')
- : gettext('Inactive subscription')
- %>
-
-
-
-<% } %>