learning-credentials 0.2.0rc2__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/CHANGELOG.rst +10 -2
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/MANIFEST.in +1 -1
- {learning_credentials-0.2.0rc2/learning_credentials.egg-info → learning_credentials-0.2.1}/PKG-INFO +14 -6
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/README.rst +2 -2
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/__init__.py +1 -1
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/generators.py +2 -1
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/models.py +3 -3
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/processors.py +6 -0
- learning_credentials-0.2.1/learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/body.txt +13 -0
- learning_credentials-0.2.1/learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/from_name.txt +1 -0
- learning_credentials-0.2.1/learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/subject.txt +4 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1/learning_credentials.egg-info}/PKG-INFO +14 -6
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials.egg-info/SOURCES.txt +3 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials.egg-info/requires.txt +1 -1
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/requirements/base.in +1 -2
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/tests/test_generators.py +2 -1
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/tests/test_processors.py +48 -50
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/LICENSE.txt +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/admin.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/apps.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/compat.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/exceptions.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/migrations/0001_initial.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/migrations/0002_migrate_to_learning_credentials.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/migrations/0003_rename_certificates_to_credentials.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/migrations/0004_replace_course_keys_with_learning_context_keys.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/migrations/0005_rename_processors_and_generators.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/migrations/__init__.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/settings/__init__.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/settings/common.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/settings/production.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/tasks.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/templates/learning_credentials/base.html +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/body.html +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/head.html +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/urls.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/views.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials.egg-info/dependency_links.txt +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials.egg-info/entry_points.txt +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials.egg-info/not-zip-safe +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials.egg-info/top_level.txt +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/openedx_certificates/__init__.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/openedx_certificates/apps.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/openedx_certificates/migrations/0001_initial.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/openedx_certificates/migrations/__init__.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/openedx_certificates/models.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/pyproject.toml +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/requirements/constraints.txt +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/setup.cfg +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/setup.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/tests/test_models.py +0 -0
- {learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/tests/test_tasks.py +0 -0
|
@@ -16,13 +16,21 @@ Unreleased
|
|
|
16
16
|
|
|
17
17
|
*
|
|
18
18
|
|
|
19
|
-
0.2.
|
|
19
|
+
0.2.1 – 2025-05-05
|
|
20
|
+
******************
|
|
21
|
+
|
|
22
|
+
Fixed
|
|
23
|
+
=====
|
|
24
|
+
|
|
25
|
+
* Check enrollment status before issuing Learning Path credentials.
|
|
26
|
+
|
|
27
|
+
0.2.0 – 2025-04-03
|
|
20
28
|
******************
|
|
21
29
|
|
|
22
30
|
Added
|
|
23
31
|
=====
|
|
24
32
|
|
|
25
|
-
*
|
|
33
|
+
* Learning Paths support.
|
|
26
34
|
|
|
27
35
|
|
|
28
36
|
0.1.0 – 2025-01-29
|
|
@@ -3,4 +3,4 @@ include LICENSE.txt
|
|
|
3
3
|
include README.rst
|
|
4
4
|
include requirements/base.in
|
|
5
5
|
include requirements/constraints.txt
|
|
6
|
-
recursive-include learning_credentials *.html *.png *.gif *.js *.css *.jpg *.jpeg *.svg
|
|
6
|
+
recursive-include learning_credentials *.html *.png *.gif *.js *.css *.jpg *.jpeg *.svg *.txt
|
{learning_credentials-0.2.0rc2/learning_credentials.egg-info → learning_credentials-0.2.1}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: learning-credentials
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: A pluggable service for preparing Open edX credentials.
|
|
5
5
|
Home-page: https://github.com/open-craft/learning-credentials
|
|
6
6
|
Author: OpenCraft
|
|
@@ -28,7 +28,7 @@ Requires-Dist: django_reverse_admin
|
|
|
28
28
|
Requires-Dist: djangorestframework
|
|
29
29
|
Requires-Dist: edx-opaque-keys
|
|
30
30
|
Requires-Dist: edx_ace
|
|
31
|
-
Requires-Dist: learning-paths-plugin
|
|
31
|
+
Requires-Dist: learning-paths-plugin>=0.3.0
|
|
32
32
|
Requires-Dist: openedx-completion-aggregator
|
|
33
33
|
Requires-Dist: pypdf
|
|
34
34
|
Requires-Dist: reportlab
|
|
@@ -139,7 +139,7 @@ Please do not report security issues in public. Please email security@openedx.or
|
|
|
139
139
|
:target: https://pypi.python.org/pypi/learning-credentials/
|
|
140
140
|
:alt: PyPI
|
|
141
141
|
|
|
142
|
-
.. |ci-badge| image:: https://github.com/open-craft/learning-credentials/workflows/
|
|
142
|
+
.. |ci-badge| image:: https://github.com/open-craft/learning-credentials/actions/workflows/ci.yml/badge.svg?branch=main
|
|
143
143
|
:target: https://github.com/open-craft/learning-credentials/actions
|
|
144
144
|
:alt: CI
|
|
145
145
|
|
|
@@ -159,7 +159,7 @@ Please do not report security issues in public. Please email security@openedx.or
|
|
|
159
159
|
:target: https://github.com/open-craft/learning-credentials/blob/main/LICENSE.txt
|
|
160
160
|
:alt: License
|
|
161
161
|
|
|
162
|
-
.. |status-badge| image:: https://img.shields.io/badge/Status-
|
|
162
|
+
.. |status-badge| image:: https://img.shields.io/badge/Status-Maintained-brightgreen
|
|
163
163
|
:alt: Status
|
|
164
164
|
|
|
165
165
|
.. https://githubnext.com/projects/repo-visualization/
|
|
@@ -186,13 +186,21 @@ Unreleased
|
|
|
186
186
|
|
|
187
187
|
*
|
|
188
188
|
|
|
189
|
-
0.2.
|
|
189
|
+
0.2.1 – 2025-05-05
|
|
190
|
+
******************
|
|
191
|
+
|
|
192
|
+
Fixed
|
|
193
|
+
=====
|
|
194
|
+
|
|
195
|
+
* Check enrollment status before issuing Learning Path credentials.
|
|
196
|
+
|
|
197
|
+
0.2.0 – 2025-04-03
|
|
190
198
|
******************
|
|
191
199
|
|
|
192
200
|
Added
|
|
193
201
|
=====
|
|
194
202
|
|
|
195
|
-
*
|
|
203
|
+
* Learning Paths support.
|
|
196
204
|
|
|
197
205
|
|
|
198
206
|
0.1.0 – 2025-01-29
|
|
@@ -92,7 +92,7 @@ Please do not report security issues in public. Please email security@openedx.or
|
|
|
92
92
|
:target: https://pypi.python.org/pypi/learning-credentials/
|
|
93
93
|
:alt: PyPI
|
|
94
94
|
|
|
95
|
-
.. |ci-badge| image:: https://github.com/open-craft/learning-credentials/workflows/
|
|
95
|
+
.. |ci-badge| image:: https://github.com/open-craft/learning-credentials/actions/workflows/ci.yml/badge.svg?branch=main
|
|
96
96
|
:target: https://github.com/open-craft/learning-credentials/actions
|
|
97
97
|
:alt: CI
|
|
98
98
|
|
|
@@ -112,7 +112,7 @@ Please do not report security issues in public. Please email security@openedx.or
|
|
|
112
112
|
:target: https://github.com/open-craft/learning-credentials/blob/main/LICENSE.txt
|
|
113
113
|
:alt: License
|
|
114
114
|
|
|
115
|
-
.. |status-badge| image:: https://img.shields.io/badge/Status-
|
|
115
|
+
.. |status-badge| image:: https://img.shields.io/badge/Status-Maintained-brightgreen
|
|
116
116
|
:alt: Status
|
|
117
117
|
|
|
118
118
|
.. https://githubnext.com/projects/repo-visualization/
|
{learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/generators.py
RENAMED
|
@@ -98,7 +98,7 @@ def _write_text_on_template(template: any, font: str, username: str, context_nam
|
|
|
98
98
|
pdf_canvas.drawString(name_x, name_y, username)
|
|
99
99
|
|
|
100
100
|
# Write the learning context name.
|
|
101
|
-
pdf_canvas.setFont(font, 28)
|
|
101
|
+
pdf_canvas.setFont(font, options.get('context_name_size', 28))
|
|
102
102
|
context_name_color = options.get('context_name_color', '#000')
|
|
103
103
|
pdf_canvas.setFillColorRGB(*hex_to_rgb(context_name_color))
|
|
104
104
|
|
|
@@ -187,6 +187,7 @@ def generate_pdf_credential(
|
|
|
187
187
|
- context_name: Specify the custom course or Learning Path name.
|
|
188
188
|
- context_name_y: The Y coordinate of the context name on the credential (vertical position on the template).
|
|
189
189
|
- context_name_color: The color of the context name on the credential (hexadecimal color code).
|
|
190
|
+
- context_name_size: The font size of the context name on the credential. The default value is 28.
|
|
190
191
|
- issue_date_y: The Y coordinate of the issue date on the credential (vertical position on the template).
|
|
191
192
|
- issue_date_color: The color of the issue date on the credential (hexadecimal color code).
|
|
192
193
|
"""
|
|
@@ -291,7 +291,7 @@ class Credential(TimeStampedModel):
|
|
|
291
291
|
|
|
292
292
|
def send_email(self):
|
|
293
293
|
"""Send a credential link to the student."""
|
|
294
|
-
|
|
294
|
+
learning_context_name = get_learning_context_name(self.learning_context_key)
|
|
295
295
|
user = get_user_model().objects.get(id=self.user_id)
|
|
296
296
|
msg = Message(
|
|
297
297
|
name="certificate_generated",
|
|
@@ -299,8 +299,8 @@ class Credential(TimeStampedModel):
|
|
|
299
299
|
recipient=Recipient(lms_user_id=user.id, email_address=user.email),
|
|
300
300
|
language='en',
|
|
301
301
|
context={
|
|
302
|
-
'
|
|
303
|
-
'course_name':
|
|
302
|
+
'certificate_link': self.download_url,
|
|
303
|
+
'course_name': learning_context_name,
|
|
304
304
|
'platform_name': settings.PLATFORM_NAME,
|
|
305
305
|
},
|
|
306
306
|
)
|
{learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/processors.py
RENAMED
|
@@ -68,6 +68,11 @@ def _process_learning_context(
|
|
|
68
68
|
else:
|
|
69
69
|
results &= course_results
|
|
70
70
|
|
|
71
|
+
# Filter out users who are not enrolled in the Learning Path.
|
|
72
|
+
results &= set(
|
|
73
|
+
learning_path.enrolled_users.filter(learningpathenrollment__is_active=True).values_list('id', flat=True),
|
|
74
|
+
)
|
|
75
|
+
|
|
71
76
|
return list(results) if results else []
|
|
72
77
|
|
|
73
78
|
|
|
@@ -240,6 +245,7 @@ def _retrieve_course_completions(course_id: CourseKey, options: dict[str, Any])
|
|
|
240
245
|
required_completion = options.get('required_completion', 0.9)
|
|
241
246
|
|
|
242
247
|
url = f'/completion-aggregator/v1/course/{course_id}/'
|
|
248
|
+
# The API supports up to 10k results per page, but we limit it to 1k to avoid performance issues.
|
|
243
249
|
query_params = {'page_size': 1000, 'page': 1}
|
|
244
250
|
|
|
245
251
|
# TODO: Extract the logic of this view into an API. The current approach is very hacky.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{% load i18n %}{% autoescape off %}
|
|
2
|
+
|
|
3
|
+
{% blocktrans %}Thank you for your participation in {{ course_name }} at {{ platform_name }}!{% endblocktrans %}
|
|
4
|
+
|
|
5
|
+
{% blocktrans %}We are happy to inform you that you have earned a certificate. You should feel very proud of the work you have done in this course. We congratulate you on your efforts and your learning.{% endblocktrans %}
|
|
6
|
+
|
|
7
|
+
{% trans "To view and download your certificate, please click on the following link:" %}
|
|
8
|
+
|
|
9
|
+
{{ certificate_link }}
|
|
10
|
+
|
|
11
|
+
{% blocktrans %}Thank you for choosing {{ platform_name }} for your learning journey. We look forward to seeing you in more courses in the future.{% endblocktrans %}
|
|
12
|
+
|
|
13
|
+
{% endautoescape %}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{ platform_name }}
|
{learning_credentials-0.2.0rc2 → learning_credentials-0.2.1/learning_credentials.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: learning-credentials
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: A pluggable service for preparing Open edX credentials.
|
|
5
5
|
Home-page: https://github.com/open-craft/learning-credentials
|
|
6
6
|
Author: OpenCraft
|
|
@@ -28,7 +28,7 @@ Requires-Dist: django_reverse_admin
|
|
|
28
28
|
Requires-Dist: djangorestframework
|
|
29
29
|
Requires-Dist: edx-opaque-keys
|
|
30
30
|
Requires-Dist: edx_ace
|
|
31
|
-
Requires-Dist: learning-paths-plugin
|
|
31
|
+
Requires-Dist: learning-paths-plugin>=0.3.0
|
|
32
32
|
Requires-Dist: openedx-completion-aggregator
|
|
33
33
|
Requires-Dist: pypdf
|
|
34
34
|
Requires-Dist: reportlab
|
|
@@ -139,7 +139,7 @@ Please do not report security issues in public. Please email security@openedx.or
|
|
|
139
139
|
:target: https://pypi.python.org/pypi/learning-credentials/
|
|
140
140
|
:alt: PyPI
|
|
141
141
|
|
|
142
|
-
.. |ci-badge| image:: https://github.com/open-craft/learning-credentials/workflows/
|
|
142
|
+
.. |ci-badge| image:: https://github.com/open-craft/learning-credentials/actions/workflows/ci.yml/badge.svg?branch=main
|
|
143
143
|
:target: https://github.com/open-craft/learning-credentials/actions
|
|
144
144
|
:alt: CI
|
|
145
145
|
|
|
@@ -159,7 +159,7 @@ Please do not report security issues in public. Please email security@openedx.or
|
|
|
159
159
|
:target: https://github.com/open-craft/learning-credentials/blob/main/LICENSE.txt
|
|
160
160
|
:alt: License
|
|
161
161
|
|
|
162
|
-
.. |status-badge| image:: https://img.shields.io/badge/Status-
|
|
162
|
+
.. |status-badge| image:: https://img.shields.io/badge/Status-Maintained-brightgreen
|
|
163
163
|
:alt: Status
|
|
164
164
|
|
|
165
165
|
.. https://githubnext.com/projects/repo-visualization/
|
|
@@ -186,13 +186,21 @@ Unreleased
|
|
|
186
186
|
|
|
187
187
|
*
|
|
188
188
|
|
|
189
|
-
0.2.
|
|
189
|
+
0.2.1 – 2025-05-05
|
|
190
|
+
******************
|
|
191
|
+
|
|
192
|
+
Fixed
|
|
193
|
+
=====
|
|
194
|
+
|
|
195
|
+
* Check enrollment status before issuing Learning Path credentials.
|
|
196
|
+
|
|
197
|
+
0.2.0 – 2025-04-03
|
|
190
198
|
******************
|
|
191
199
|
|
|
192
200
|
Added
|
|
193
201
|
=====
|
|
194
202
|
|
|
195
|
-
*
|
|
203
|
+
* Learning Paths support.
|
|
196
204
|
|
|
197
205
|
|
|
198
206
|
0.1.0 – 2025-01-29
|
|
@@ -33,7 +33,10 @@ learning_credentials/settings/common.py
|
|
|
33
33
|
learning_credentials/settings/production.py
|
|
34
34
|
learning_credentials/templates/learning_credentials/base.html
|
|
35
35
|
learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/body.html
|
|
36
|
+
learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/body.txt
|
|
37
|
+
learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/from_name.txt
|
|
36
38
|
learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/head.html
|
|
39
|
+
learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/subject.txt
|
|
37
40
|
openedx_certificates/__init__.py
|
|
38
41
|
openedx_certificates/apps.py
|
|
39
42
|
openedx_certificates/models.py
|
|
@@ -14,5 +14,4 @@ pypdf # PDF manipulation library
|
|
|
14
14
|
reportlab # PDF generation library
|
|
15
15
|
openedx-completion-aggregator # Completion aggregation service
|
|
16
16
|
edx_ace # Messaging library
|
|
17
|
-
|
|
18
|
-
learning-paths-plugin==0.3.0rc1
|
|
17
|
+
learning-paths-plugin>=0.3.0
|
|
@@ -75,6 +75,7 @@ def test_register_font_with_custom_font(mock_register_font: Mock, mock_font_clas
|
|
|
75
75
|
'name_color': '123',
|
|
76
76
|
'context_name_color': '#9B192A',
|
|
77
77
|
'issue_date_color': '#f59a8e',
|
|
78
|
+
'context_name_size': 20,
|
|
78
79
|
},
|
|
79
80
|
{
|
|
80
81
|
'name_color': (17 / 255, 34 / 255, 51 / 255),
|
|
@@ -135,7 +136,7 @@ def test_write_text_on_template(mock_canvas_class: Mock, context_name: str, opti
|
|
|
135
136
|
assert canvas_object.drawString.call_args_list[0] == call(expected_name_x, expected_name_y, username)
|
|
136
137
|
assert mock_canvas_class.return_value.stringWidth.mock_calls[0][1] == (username,)
|
|
137
138
|
|
|
138
|
-
assert canvas_object.setFont.call_args_list[1] == call(font, 28)
|
|
139
|
+
assert canvas_object.setFont.call_args_list[1] == call(font, options.get('context_name_size', 28))
|
|
139
140
|
assert canvas_object.setFillColorRGB.call_args_list[1] == call(*expected_context_name_color)
|
|
140
141
|
|
|
141
142
|
assert canvas_object.setFont.call_args_list[2] == call(font, 12)
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
5
6
|
from unittest.mock import Mock, call, patch
|
|
6
7
|
|
|
7
8
|
import pytest
|
|
8
9
|
from django.http import QueryDict
|
|
9
|
-
from learning_paths.models import LearningPath
|
|
10
|
+
from learning_paths.models import LearningPath
|
|
10
11
|
from opaque_keys.edx.keys import CourseKey
|
|
11
12
|
|
|
12
13
|
# noinspection PyProtectedMember
|
|
@@ -19,6 +20,10 @@ from learning_credentials.processors import (
|
|
|
19
20
|
retrieve_completions_and_grades,
|
|
20
21
|
retrieve_subsection_grades,
|
|
21
22
|
)
|
|
23
|
+
from test_utils.factories import UserFactory
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from django.contrib.auth.models import User
|
|
22
27
|
|
|
23
28
|
|
|
24
29
|
@patch(
|
|
@@ -322,66 +327,59 @@ def test_retrieve_course_completions_and_grades(
|
|
|
322
327
|
|
|
323
328
|
|
|
324
329
|
@pytest.fixture
|
|
325
|
-
def
|
|
326
|
-
"""Create a
|
|
327
|
-
|
|
328
|
-
key='path-v1:test+number+run+group',
|
|
329
|
-
slug="test",
|
|
330
|
-
)
|
|
330
|
+
def users() -> list[User]:
|
|
331
|
+
"""Create a list of users."""
|
|
332
|
+
return UserFactory.create_batch(6)
|
|
331
333
|
|
|
332
|
-
course_keys = [f"course-v1:TestX+Test101+2023_{i}" for i in range(3)]
|
|
333
334
|
|
|
334
|
-
|
|
335
|
-
|
|
335
|
+
@pytest.fixture
|
|
336
|
+
def learning_path_with_courses(users: list[User]) -> LearningPath:
|
|
337
|
+
"""Create a LearningPath with multiple course steps."""
|
|
338
|
+
learning_path = LearningPath.objects.create(key='path-v1:test+number+run+group')
|
|
336
339
|
|
|
337
|
-
|
|
340
|
+
for i in range(3):
|
|
341
|
+
learning_path.steps.create(course_key=f"course-v1:TestX+Test101+2023_{i}", order=i)
|
|
338
342
|
|
|
343
|
+
# Enroll all users except the last one.
|
|
344
|
+
for i in range(len(users) - 1):
|
|
345
|
+
learning_path.enrolled_users.add(users[i])
|
|
339
346
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
def test_retrieve_subsection_grades_with_learning_path(
|
|
343
|
-
mock_retrieve_course_subsection_grades: Mock,
|
|
344
|
-
learning_path_with_courses: LearningPath,
|
|
345
|
-
):
|
|
346
|
-
"""Test retrieving subsection grades with a real learning path."""
|
|
347
|
-
options = Mock()
|
|
348
|
-
mock_retrieve_course_subsection_grades.side_effect = [
|
|
349
|
-
[101, 102, 103], # Users passing course0
|
|
350
|
-
[102, 103, 104], # Users passing course1
|
|
351
|
-
[101, 103, 105], # Users passing course2
|
|
352
|
-
]
|
|
347
|
+
# Mark the second last user's enrollment as inactive.
|
|
348
|
+
learning_path.learningpathenrollment_set.filter(user=users[-2]).update(is_active=False)
|
|
353
349
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
assert result == [103]
|
|
357
|
-
|
|
358
|
-
assert mock_retrieve_course_subsection_grades.call_count == 3
|
|
359
|
-
course_keys = [step.course_key for step in learning_path_with_courses.steps.all()]
|
|
360
|
-
for i, course_key in enumerate(course_keys):
|
|
361
|
-
call_args = mock_retrieve_course_subsection_grades.call_args_list[i]
|
|
362
|
-
assert call_args[0] == (course_key, options)
|
|
350
|
+
return learning_path
|
|
363
351
|
|
|
364
352
|
|
|
353
|
+
@pytest.mark.parametrize(
|
|
354
|
+
('patch_target', 'function_to_test'),
|
|
355
|
+
[
|
|
356
|
+
("learning_credentials.processors._retrieve_course_subsection_grades", retrieve_subsection_grades),
|
|
357
|
+
("learning_credentials.processors._retrieve_course_completions", retrieve_completions),
|
|
358
|
+
],
|
|
359
|
+
ids=['subsection_grades', 'completions'],
|
|
360
|
+
)
|
|
365
361
|
@pytest.mark.django_db
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
362
|
+
def test_retrieve_data_for_learning_path(
|
|
363
|
+
patch_target: str,
|
|
364
|
+
function_to_test: callable,
|
|
369
365
|
learning_path_with_courses: LearningPath,
|
|
366
|
+
users: list[User],
|
|
370
367
|
):
|
|
371
|
-
"""Test retrieving
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
368
|
+
"""Test retrieving data for a learning path."""
|
|
369
|
+
with patch(patch_target) as mock_retrieve:
|
|
370
|
+
options = Mock()
|
|
371
|
+
mock_retrieve.side_effect = (
|
|
372
|
+
(users[i].id for i in (0, 1, 2, 4, 5)), # Users passing/completing course0
|
|
373
|
+
(users[i].id for i in (0, 1, 2, 3, 4, 5)), # Users passing/completing course1
|
|
374
|
+
(users[i].id for i in (0, 2, 3, 4, 5)), # Users passing/completing course2
|
|
375
|
+
)
|
|
378
376
|
|
|
379
|
-
|
|
377
|
+
result = function_to_test(learning_path_with_courses.key, options)
|
|
380
378
|
|
|
381
|
-
|
|
379
|
+
assert sorted(result) == [users[0].id, users[2].id]
|
|
382
380
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
381
|
+
assert mock_retrieve.call_count == 3
|
|
382
|
+
course_keys = [step.course_key for step in learning_path_with_courses.steps.all()]
|
|
383
|
+
for i, course_key in enumerate(course_keys):
|
|
384
|
+
call_args = mock_retrieve.call_args_list[i]
|
|
385
|
+
assert call_args[0] == (course_key, options)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/learning_credentials/settings/common.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{learning_credentials-0.2.0rc2 → learning_credentials-0.2.1}/openedx_certificates/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|