learning-credentials 0.2.0rc3__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.
Files changed (52) hide show
  1. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/CHANGELOG.rst +10 -2
  2. {learning_credentials-0.2.0rc3/learning_credentials.egg-info → learning_credentials-0.2.1}/PKG-INFO +14 -6
  3. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/README.rst +2 -2
  4. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/__init__.py +1 -1
  5. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/generators.py +2 -1
  6. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/models.py +2 -2
  7. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/processors.py +6 -0
  8. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1/learning_credentials.egg-info}/PKG-INFO +14 -6
  9. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials.egg-info/requires.txt +1 -1
  10. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/requirements/base.in +1 -2
  11. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/tests/test_generators.py +2 -1
  12. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/tests/test_processors.py +48 -50
  13. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/LICENSE.txt +0 -0
  14. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/MANIFEST.in +0 -0
  15. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/admin.py +0 -0
  16. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/apps.py +0 -0
  17. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/compat.py +0 -0
  18. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/exceptions.py +0 -0
  19. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/migrations/0001_initial.py +0 -0
  20. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/migrations/0002_migrate_to_learning_credentials.py +0 -0
  21. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/migrations/0003_rename_certificates_to_credentials.py +0 -0
  22. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/migrations/0004_replace_course_keys_with_learning_context_keys.py +0 -0
  23. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/migrations/0005_rename_processors_and_generators.py +0 -0
  24. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/migrations/__init__.py +0 -0
  25. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/settings/__init__.py +0 -0
  26. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/settings/common.py +0 -0
  27. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/settings/production.py +0 -0
  28. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/tasks.py +0 -0
  29. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/templates/learning_credentials/base.html +0 -0
  30. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/body.html +0 -0
  31. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/body.txt +0 -0
  32. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/from_name.txt +0 -0
  33. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/head.html +0 -0
  34. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/subject.txt +0 -0
  35. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/urls.py +0 -0
  36. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials/views.py +0 -0
  37. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials.egg-info/SOURCES.txt +0 -0
  38. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials.egg-info/dependency_links.txt +0 -0
  39. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials.egg-info/entry_points.txt +0 -0
  40. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials.egg-info/not-zip-safe +0 -0
  41. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/learning_credentials.egg-info/top_level.txt +0 -0
  42. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/openedx_certificates/__init__.py +0 -0
  43. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/openedx_certificates/apps.py +0 -0
  44. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/openedx_certificates/migrations/0001_initial.py +0 -0
  45. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/openedx_certificates/migrations/__init__.py +0 -0
  46. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/openedx_certificates/models.py +0 -0
  47. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/pyproject.toml +0 -0
  48. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/requirements/constraints.txt +0 -0
  49. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/setup.cfg +0 -0
  50. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/setup.py +0 -0
  51. {learning_credentials-0.2.0rc3 → learning_credentials-0.2.1}/tests/test_models.py +0 -0
  52. {learning_credentials-0.2.0rc3 → 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.0 – 2025-03-31
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
- * Initial implementation of the certificates app.
33
+ * Learning Paths support.
26
34
 
27
35
 
28
36
  0.1.0 – 2025-01-29
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: learning-credentials
3
- Version: 0.2.0rc3
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==0.3.0rc1
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/Python%20CI/badge.svg?branch=main
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-Experimental-yellow
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.0 – 2025-03-31
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
- * Initial implementation of the certificates app.
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/Python%20CI/badge.svg?branch=main
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-Experimental-yellow
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/
@@ -1,3 +1,3 @@
1
1
  """A pluggable service for preparing Open edX credentials."""
2
2
 
3
- __version__ = '0.2.0rc3'
3
+ __version__ = '0.2.1'
@@ -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
- course_name = get_learning_context_name(self.learning_context_key)
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",
@@ -300,7 +300,7 @@ class Credential(TimeStampedModel):
300
300
  language='en',
301
301
  context={
302
302
  'certificate_link': self.download_url,
303
- 'course_name': course_name,
303
+ 'course_name': learning_context_name,
304
304
  'platform_name': settings.PLATFORM_NAME,
305
305
  },
306
306
  )
@@ -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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: learning-credentials
3
- Version: 0.2.0rc3
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==0.3.0rc1
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/Python%20CI/badge.svg?branch=main
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-Experimental-yellow
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.0 – 2025-03-31
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
- * Initial implementation of the certificates app.
203
+ * Learning Paths support.
196
204
 
197
205
 
198
206
  0.1.0 – 2025-01-29
@@ -7,7 +7,7 @@ django_reverse_admin
7
7
  djangorestframework
8
8
  edx-opaque-keys
9
9
  edx_ace
10
- learning-paths-plugin==0.3.0rc1
10
+ learning-paths-plugin>=0.3.0
11
11
  openedx-completion-aggregator
12
12
  pypdf
13
13
  reportlab
@@ -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
- # TODO: Use the PyPI version of this package once it is available. Ensure to pin the minimum version.
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, LearningPathStep
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 learning_path_with_courses() -> LearningPath:
326
- """Create a LearningPath with multiple course steps."""
327
- learning_path = LearningPath.objects.create(
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
- for i, course_key_str in enumerate(course_keys):
335
- LearningPathStep.objects.create(learning_path=learning_path, course_key=course_key_str, order=i)
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
- return learning_path
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
- @pytest.mark.django_db
341
- @patch("learning_credentials.processors._retrieve_course_subsection_grades")
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
- result = retrieve_subsection_grades(learning_path_with_courses.key, options)
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
- @patch("learning_credentials.processors._retrieve_course_completions")
367
- def test_retrieve_completions_with_learning_path_db(
368
- mock_retrieve_course_completions: Mock,
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 completions with a real learning path."""
372
- options = Mock()
373
- mock_retrieve_course_completions.side_effect = [
374
- [101, 102, 103, 106], # Users completing course0
375
- [102, 103, 104, 106], # Users completing course1
376
- [103, 105, 106], # Users completing course2
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
- result = retrieve_completions(learning_path_with_courses.key, options)
377
+ result = function_to_test(learning_path_with_courses.key, options)
380
378
 
381
- assert sorted(result) == [103, 106]
379
+ assert sorted(result) == [users[0].id, users[2].id]
382
380
 
383
- assert mock_retrieve_course_completions.call_count == 3
384
- course_keys = [step.course_key for step in learning_path_with_courses.steps.all()]
385
- for i, course_key in enumerate(course_keys):
386
- call_args = mock_retrieve_course_completions.call_args_list[i]
387
- assert call_args[0] == (course_key, options)
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)