learning-paths-plugin 0.3.4rc4__tar.gz → 0.3.4rc6__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_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/CHANGELOG.rst +5 -0
  2. {learning_paths_plugin-0.3.4rc4/learning_paths_plugin.egg-info → learning_paths_plugin-0.3.4rc6}/PKG-INFO +6 -1
  3. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/__init__.py +1 -1
  4. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/api/v1/serializers.py +1 -1
  5. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/api/v1/views.py +27 -2
  6. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/compat.py +4 -6
  7. learning_paths_plugin-0.3.4rc6/learning_paths/migrations/0014_learningpathenrollmentallowed_is_active.py +20 -0
  8. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/models.py +6 -3
  9. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/receivers.py +4 -1
  10. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6/learning_paths_plugin.egg-info}/PKG-INFO +6 -1
  11. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths_plugin.egg-info/SOURCES.txt +1 -0
  12. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/LICENSE.txt +0 -0
  13. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/MANIFEST.in +0 -0
  14. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/README.rst +0 -0
  15. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/admin.py +0 -0
  16. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/api/__init__.py +0 -0
  17. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/api/urls.py +0 -0
  18. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/api/v1/__init__.py +0 -0
  19. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/api/v1/filters.py +0 -0
  20. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/api/v1/permissions.py +0 -0
  21. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/api/v1/urls.py +0 -0
  22. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/api/v1/utils.py +0 -0
  23. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/apps.py +0 -0
  24. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/conftest.py +0 -0
  25. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/keys.py +0 -0
  26. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/migrations/0001_initial.py +0 -0
  27. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/migrations/0002_learningpath_uuid.py +0 -0
  28. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/migrations/0003_learningpath_subtitle.py +0 -0
  29. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/migrations/0004_auto_20240207_1633.py +0 -0
  30. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/migrations/0005_learningpathstep_weight_learningpathgradingcriteria.py +0 -0
  31. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/migrations/0006_enrollment_models.py +0 -0
  32. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/migrations/0007_replace_uuid_with_learningpathkey.py +0 -0
  33. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/migrations/0008_remove_learningpathstep_relative_due_date_in_days.py +0 -0
  34. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/migrations/0009_remove_learningpath_slug.py +0 -0
  35. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/migrations/0010_learningpath_invite_only.py +0 -0
  36. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/migrations/0011_replace_learningpath_image_url_with_image.py +0 -0
  37. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/migrations/0012_alter_learningpath_subtitle.py +0 -0
  38. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/migrations/0013_enrollment_audit.py +0 -0
  39. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/migrations/__init__.py +0 -0
  40. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/settings.py +0 -0
  41. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/templates/learning_paths/base.html +0 -0
  42. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths/urls.py +0 -0
  43. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths_plugin.egg-info/dependency_links.txt +0 -0
  44. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths_plugin.egg-info/entry_points.txt +0 -0
  45. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths_plugin.egg-info/not-zip-safe +0 -0
  46. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths_plugin.egg-info/requires.txt +0 -0
  47. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/learning_paths_plugin.egg-info/top_level.txt +0 -0
  48. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/pyproject.toml +0 -0
  49. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/requirements/base.in +0 -0
  50. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/requirements/constraints.txt +0 -0
  51. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/setup.cfg +0 -0
  52. {learning_paths_plugin-0.3.4rc4 → learning_paths_plugin-0.3.4rc6}/setup.py +0 -0
@@ -25,6 +25,11 @@ Added
25
25
  * Bulk unenrollment API.
26
26
  * Enrollment audit model that tracks the enrollment state transitions.
27
27
 
28
+ Changed
29
+ =======
30
+
31
+ * The Learning Paths API includes start and end dates for its steps.
32
+
28
33
  0.3.3 - 2025-05-23
29
34
  ******************
30
35
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: learning-paths-plugin
3
- Version: 0.3.4rc4
3
+ Version: 0.3.4rc6
4
4
  Summary: Learning Paths plugin
5
5
  Home-page: https://github.com/open-craft/learning-paths-plugin
6
6
  Author: OpenCraft
@@ -129,6 +129,11 @@ Added
129
129
  * Bulk unenrollment API.
130
130
  * Enrollment audit model that tracks the enrollment state transitions.
131
131
 
132
+ Changed
133
+ =======
134
+
135
+ * The Learning Paths API includes start and end dates for its steps.
136
+
132
137
  0.3.3 - 2025-05-23
133
138
  ******************
134
139
 
@@ -2,4 +2,4 @@
2
2
  Learning Paths plugin.
3
3
  """
4
4
 
5
- __version__ = "0.3.4-rc4"
5
+ __version__ = "0.3.4-rc6"
@@ -97,7 +97,7 @@ class LearningPathGradeSerializer(serializers.Serializer):
97
97
  class LearningPathStepSerializer(serializers.ModelSerializer):
98
98
  class Meta:
99
99
  model = LearningPathStep
100
- fields = ["order", "course_key", "due_date", "weight"]
100
+ fields = ["order", "course_key", "course_dates", "weight"]
101
101
 
102
102
 
103
103
  class LearningPathListSerializer(serializers.ModelSerializer):
@@ -452,12 +452,13 @@ class BulkEnrollView(APIView):
452
452
  `role` (str, optional): User role, used for audit.
453
453
 
454
454
  * For existing users, it deactivates their LearningPathEnrollment records.
455
- * Does not affect LearningPathEnrollmentAllowed records (allowed to enroll records).
455
+ * For emails with active LearningPathEnrollmentAllowed records, it deactivates those records.
456
456
 
457
457
  """
458
- learning_paths, existing_users, _ = self._setup_bulk_operation(request)
458
+ learning_paths, existing_users, emails = self._setup_bulk_operation(request)
459
459
 
460
460
  enrollments_unenrolled = []
461
+ enrollment_allowed_deactivated = []
461
462
 
462
463
  for learning_path in learning_paths:
463
464
  for user in existing_users:
@@ -474,9 +475,33 @@ class BulkEnrollView(APIView):
474
475
  enrollment._audit = audit_data # pylint: disable=protected-access
475
476
  enrollment.save()
476
477
 
478
+ for email in emails:
479
+ try:
480
+ validate_email(email)
481
+ except ValidationError:
482
+ logger.warning("BulkEnrollView: Invalid email: %s", email)
483
+ continue
484
+
485
+ enrollment_allowed = LearningPathEnrollmentAllowed.objects.filter(
486
+ email=email,
487
+ learning_path=learning_path,
488
+ ).first()
489
+
490
+ if enrollment_allowed:
491
+ if enrollment_allowed.is_active:
492
+ state_transition = LearningPathEnrollmentAudit.ALLOWEDTOENROLL_TO_UNENROLLED
493
+ enrollment_allowed.is_active = False
494
+ enrollment_allowed_deactivated.append(enrollment_allowed)
495
+ else:
496
+ state_transition = LearningPathEnrollmentAudit.UNENROLLED_TO_UNENROLLED
497
+ audit_data = self._create_audit_data(request, state_transition)
498
+ enrollment_allowed._audit = audit_data # pylint: disable=protected-access
499
+ enrollment_allowed.save()
500
+
477
501
  return Response(
478
502
  {
479
503
  "enrollments_unenrolled": len(enrollments_unenrolled),
504
+ "enrollment_allowed_deactivated": len(enrollment_allowed_deactivated),
480
505
  },
481
506
  status=status.HTTP_204_NO_CONTENT,
482
507
  )
@@ -46,18 +46,16 @@ def get_course_keys_with_outlines() -> list[CourseKey]:
46
46
  return course_keys_with_outlines()
47
47
 
48
48
 
49
- def get_course_due_date(course_key: CourseKey) -> datetime | None:
50
- """
51
- Retrieve course end date.
52
- """
49
+ def get_course_dates(course_key: CourseKey) -> tuple[datetime | None, datetime | None]:
50
+ """Retrieve course start and end dates."""
53
51
  # pylint: disable=import-outside-toplevel, import-error
54
52
  from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
55
53
 
56
54
  try:
57
55
  overview = CourseOverview.objects.get(id=course_key)
58
- return overview.end
56
+ return overview.start, overview.end
59
57
  except CourseOverview.DoesNotExist:
60
- return None
58
+ return None, None
61
59
 
62
60
 
63
61
  def enroll_user_in_course(user: AbstractBaseUser, course_key: CourseKey) -> bool:
@@ -0,0 +1,20 @@
1
+ # Generated by Django 4.2.20 on 2025-05-30 19:57
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("learning_paths", "0013_enrollment_audit"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name="learningpathenrollmentallowed",
15
+ name="is_active",
16
+ field=models.BooleanField(
17
+ db_index=True, default=True, help_text="Indicates if the enrollment allowance is active"
18
+ ),
19
+ ),
20
+ ]
@@ -19,7 +19,7 @@ from model_utils.models import TimeStampedModel
19
19
  from opaque_keys.edx.django.models import CourseKeyField
20
20
  from slugify import slugify
21
21
 
22
- from .compat import get_course_due_date, get_user_course_grade
22
+ from .compat import get_course_dates, get_user_course_grade
23
23
  from .keys import LearningPathKeyField
24
24
 
25
25
  log = logging.getLogger(__name__)
@@ -203,9 +203,9 @@ class LearningPathStep(TimeStampedModel):
203
203
  )
204
204
 
205
205
  @property
206
- def due_date(self) -> datetime | None:
206
+ def course_dates(self) -> tuple[datetime | None, datetime | None]:
207
207
  """Retrieve the due date for this course."""
208
- return get_course_due_date(self.course_key)
208
+ return get_course_dates(self.course_key)
209
209
 
210
210
  def __str__(self):
211
211
  """User-friendly string representation of this model."""
@@ -372,6 +372,9 @@ class LearningPathEnrollmentAllowed(TimeStampedModel):
372
372
  email = models.EmailField(db_index=True)
373
373
  learning_path = models.ForeignKey(LearningPath, on_delete=models.CASCADE)
374
374
  user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
375
+ is_active = models.BooleanField(
376
+ default=True, db_index=True, help_text=_("Indicates if the enrollment allowance is active")
377
+ )
375
378
 
376
379
  def __str__(self):
377
380
  """User-friendly string representation of this model."""
@@ -39,7 +39,7 @@ def process_pending_enrollments(sender, instance, created, **kwargs):
39
39
  return
40
40
 
41
41
  logger.info("[LearningPaths] Processing pending enrollments for user %s", instance)
42
- pending_enrollments = LearningPathEnrollmentAllowed.objects.filter(email=instance.email)
42
+ pending_enrollments = LearningPathEnrollmentAllowed.objects.filter(email=instance.email, is_active=True)
43
43
  enrollments_created = 0
44
44
 
45
45
  for entry in pending_enrollments:
@@ -67,6 +67,7 @@ def process_pending_enrollments(sender, instance, created, **kwargs):
67
67
  entry.learning_path.key,
68
68
  )
69
69
  finally:
70
+ entry.is_active = False
70
71
  entry.user = instance
71
72
  entry.save()
72
73
 
@@ -129,6 +130,8 @@ def create_enrollment_allowed_audit(sender, instance, created, **kwargs):
129
130
  if not (audit_data := getattr(instance, "_audit", {})):
130
131
  return
131
132
 
133
+ audit_data.setdefault("state_transition", LearningPathEnrollmentAudit.UNENROLLED_TO_ALLOWEDTOENROLL)
134
+
132
135
  audit_data["state_transition"] = audit_data.get(
133
136
  "state_transition", LearningPathEnrollmentAudit.UNENROLLED_TO_ALLOWEDTOENROLL
134
137
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: learning-paths-plugin
3
- Version: 0.3.4rc4
3
+ Version: 0.3.4rc6
4
4
  Summary: Learning Paths plugin
5
5
  Home-page: https://github.com/open-craft/learning-paths-plugin
6
6
  Author: OpenCraft
@@ -129,6 +129,11 @@ Added
129
129
  * Bulk unenrollment API.
130
130
  * Enrollment audit model that tracks the enrollment state transitions.
131
131
 
132
+ Changed
133
+ =======
134
+
135
+ * The Learning Paths API includes start and end dates for its steps.
136
+
132
137
  0.3.3 - 2025-05-23
133
138
  ******************
134
139
 
@@ -37,6 +37,7 @@ learning_paths/migrations/0010_learningpath_invite_only.py
37
37
  learning_paths/migrations/0011_replace_learningpath_image_url_with_image.py
38
38
  learning_paths/migrations/0012_alter_learningpath_subtitle.py
39
39
  learning_paths/migrations/0013_enrollment_audit.py
40
+ learning_paths/migrations/0014_learningpathenrollmentallowed_is_active.py
40
41
  learning_paths/migrations/__init__.py
41
42
  learning_paths/templates/learning_paths/base.html
42
43
  learning_paths_plugin.egg-info/PKG-INFO