learning-paths-plugin 0.3.4rc6__tar.gz → 0.3.4rc7__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 (53) hide show
  1. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/CHANGELOG.rst +1 -0
  2. {learning_paths_plugin-0.3.4rc6/learning_paths_plugin.egg-info → learning_paths_plugin-0.3.4rc7}/PKG-INFO +2 -1
  3. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/__init__.py +1 -1
  4. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/admin.py +1 -1
  5. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/api/v1/serializers.py +7 -7
  6. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/api/v1/views.py +1 -3
  7. learning_paths_plugin-0.3.4rc7/learning_paths/migrations/0015_remove_learningpathenrollment_enrolled_at.py +17 -0
  8. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/models.py +11 -15
  9. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7/learning_paths_plugin.egg-info}/PKG-INFO +2 -1
  10. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths_plugin.egg-info/SOURCES.txt +1 -0
  11. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/LICENSE.txt +0 -0
  12. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/MANIFEST.in +0 -0
  13. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/README.rst +0 -0
  14. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/api/__init__.py +0 -0
  15. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/api/urls.py +0 -0
  16. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/api/v1/__init__.py +0 -0
  17. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/api/v1/filters.py +0 -0
  18. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/api/v1/permissions.py +0 -0
  19. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/api/v1/urls.py +0 -0
  20. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/api/v1/utils.py +0 -0
  21. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/apps.py +0 -0
  22. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/compat.py +0 -0
  23. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/conftest.py +0 -0
  24. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/keys.py +0 -0
  25. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/migrations/0001_initial.py +0 -0
  26. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/migrations/0002_learningpath_uuid.py +0 -0
  27. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/migrations/0003_learningpath_subtitle.py +0 -0
  28. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/migrations/0004_auto_20240207_1633.py +0 -0
  29. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/migrations/0005_learningpathstep_weight_learningpathgradingcriteria.py +0 -0
  30. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/migrations/0006_enrollment_models.py +0 -0
  31. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/migrations/0007_replace_uuid_with_learningpathkey.py +0 -0
  32. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/migrations/0008_remove_learningpathstep_relative_due_date_in_days.py +0 -0
  33. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/migrations/0009_remove_learningpath_slug.py +0 -0
  34. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/migrations/0010_learningpath_invite_only.py +0 -0
  35. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/migrations/0011_replace_learningpath_image_url_with_image.py +0 -0
  36. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/migrations/0012_alter_learningpath_subtitle.py +0 -0
  37. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/migrations/0013_enrollment_audit.py +0 -0
  38. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/migrations/0014_learningpathenrollmentallowed_is_active.py +0 -0
  39. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/migrations/__init__.py +0 -0
  40. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/receivers.py +0 -0
  41. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/settings.py +0 -0
  42. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/templates/learning_paths/base.html +0 -0
  43. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths/urls.py +0 -0
  44. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths_plugin.egg-info/dependency_links.txt +0 -0
  45. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths_plugin.egg-info/entry_points.txt +0 -0
  46. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths_plugin.egg-info/not-zip-safe +0 -0
  47. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths_plugin.egg-info/requires.txt +0 -0
  48. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/learning_paths_plugin.egg-info/top_level.txt +0 -0
  49. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/pyproject.toml +0 -0
  50. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/requirements/base.in +0 -0
  51. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/requirements/constraints.txt +0 -0
  52. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/setup.cfg +0 -0
  53. {learning_paths_plugin-0.3.4rc6 → learning_paths_plugin-0.3.4rc7}/setup.py +0 -0
@@ -29,6 +29,7 @@ Changed
29
29
  =======
30
30
 
31
31
  * The Learning Paths API includes start and end dates for its steps.
32
+ * Return enrollment date in the API instead of a boolean.
32
33
 
33
34
  0.3.3 - 2025-05-23
34
35
  ******************
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: learning-paths-plugin
3
- Version: 0.3.4rc6
3
+ Version: 0.3.4rc7
4
4
  Summary: Learning Paths plugin
5
5
  Home-page: https://github.com/open-craft/learning-paths-plugin
6
6
  Author: OpenCraft
@@ -133,6 +133,7 @@ Changed
133
133
  =======
134
134
 
135
135
  * The Learning Paths API includes start and end dates for its steps.
136
+ * Return enrollment date in the API instead of a boolean.
136
137
 
137
138
  0.3.3 - 2025-05-23
138
139
  ******************
@@ -2,4 +2,4 @@
2
2
  Learning Paths plugin.
3
3
  """
4
4
 
5
- __version__ = "0.3.4-rc6"
5
+ __version__ = "0.3.4-rc7"
@@ -249,8 +249,8 @@ class EnrolledUsersAdmin(admin.ModelAdmin):
249
249
  "id",
250
250
  "user",
251
251
  "learning_path",
252
- "enrolled_at",
253
252
  "is_active",
253
+ "created",
254
254
  ]
255
255
 
256
256
  list_filter = [
@@ -105,7 +105,7 @@ class LearningPathListSerializer(serializers.ModelSerializer):
105
105
 
106
106
  steps = LearningPathStepSerializer(many=True, read_only=True)
107
107
  required_completion = serializers.FloatField(source="grading_criteria.required_completion", read_only=True)
108
- is_enrolled = serializers.SerializerMethodField()
108
+ enrollment_date = serializers.SerializerMethodField()
109
109
  invite_only = serializers.BooleanField()
110
110
  image = serializers.ImageField(read_only=True)
111
111
 
@@ -118,17 +118,17 @@ class LearningPathListSerializer(serializers.ModelSerializer):
118
118
  "sequential",
119
119
  "steps",
120
120
  "required_completion",
121
- "is_enrolled",
121
+ "enrollment_date",
122
122
  "invite_only",
123
123
  ]
124
124
 
125
- def get_is_enrolled(self, obj):
125
+ def get_enrollment_date(self, obj):
126
126
  """
127
127
  Check if the current user is enrolled in this learning path.
128
128
  """
129
- if hasattr(obj, "is_enrolled"):
130
- return obj.is_enrolled
131
- return False
129
+ if hasattr(obj, "enrollment_date"):
130
+ return obj.enrollment_date
131
+ return None
132
132
 
133
133
 
134
134
  class SkillSerializer(serializers.ModelSerializer):
@@ -183,4 +183,4 @@ class LearningPathDetailSerializer(LearningPathListSerializer):
183
183
  class LearningPathEnrollmentSerializer(serializers.ModelSerializer):
184
184
  class Meta:
185
185
  model = LearningPathEnrollment
186
- fields = ("user", "learning_path", "is_active", "enrolled_at")
186
+ fields = ("user", "learning_path", "is_active", "created")
@@ -242,7 +242,6 @@ class LearningPathEnrollmentView(APIView):
242
242
  return Response({"detail": "Enrollment exists."}, status=status.HTTP_409_CONFLICT)
243
243
 
244
244
  enrollment.is_active = True
245
- enrollment.enrolled_at = datetime.now(timezone.utc)
246
245
  enrollment.save()
247
246
  return Response(LearningPathEnrollmentSerializer(enrollment).data)
248
247
 
@@ -389,7 +388,6 @@ class BulkEnrollView(APIView):
389
388
  if enrollment:
390
389
  if not enrollment.is_active:
391
390
  enrollment.is_active = True
392
- enrollment.enrolled_at = datetime.now(timezone.utc)
393
391
  enrolled_now = True
394
392
  else:
395
393
  audit_data["state_transition"] = LearningPathEnrollmentAudit.ENROLLED_TO_ENROLLED
@@ -519,7 +517,7 @@ class LearningPathCourseEnrollmentView(APIView):
519
517
  :raises: Http404 if the learning path is not found or the user does not have access.
520
518
  """
521
519
  return get_object_or_404(
522
- LearningPath.objects.get_paths_visible_to_user(self.request.user).filter(is_enrolled=True),
520
+ LearningPath.objects.get_paths_visible_to_user(self.request.user).filter(enrollment_date__isnull=False),
523
521
  key=learning_path_key_str,
524
522
  )
525
523
 
@@ -0,0 +1,17 @@
1
+ # Generated by Django 4.2.20 on 2025-06-02 14:39
2
+
3
+ from django.db import migrations
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("learning_paths", "0014_learningpathenrollmentallowed_is_active"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.RemoveField(
14
+ model_name="learningpathenrollment",
15
+ name="enrolled_at",
16
+ ),
17
+ ]
@@ -38,27 +38,29 @@ class LearningPathManager(models.Manager):
38
38
 
39
39
  def get_paths_visible_to_user(self, user: User) -> models.QuerySet:
40
40
  """
41
- Return only learning paths that should be visible to the given user with enrollment status.
41
+ Return only learning paths that should be visible to the given user with an enrollment date.
42
42
 
43
43
  For staff users: all learning paths.
44
44
  For non-staff: non-invite-only paths or invite-only paths they're enrolled in.
45
45
 
46
- Each learning path in the queryset is annotated with `is_enrolled` indicating
47
- whether the user has an active enrollment in that learning path.
46
+ Each learning path in the queryset is annotated with `enrollment_date` indicating
47
+ the date when the user enrolled in that learning path (None if not enrolled).
48
+ Results are ordered by enrollment date (the most recent first), with non-enrolled paths at the end.
48
49
  """
49
50
  queryset = self.get_queryset()
50
51
 
51
- # Annotate each path with whether the user is enrolled.
52
- enrollment_exists = LearningPathEnrollment.objects.filter(
52
+ # Annotate each path with the enrollment date.
53
+ enrollment_subquery = LearningPathEnrollment.objects.filter(
53
54
  learning_path=OuterRef("pk"), user=user, is_active=True
54
- )
55
- queryset = queryset.annotate(is_enrolled=Exists(enrollment_exists))
55
+ ).values("created")[:1]
56
+ queryset = queryset.annotate(enrollment_date=models.Subquery(enrollment_subquery))
56
57
 
57
58
  # Apply visibility filtering based on the user role.
58
59
  if not user.is_staff:
59
- queryset = queryset.filter(Q(invite_only=False) | Q(is_enrolled=True))
60
+ queryset = queryset.filter(Q(invite_only=False) | Q(enrollment_date__isnull=False))
60
61
 
61
- return queryset
62
+ # Order by enrollment date (the most recent first), with null values at the end.
63
+ return queryset.order_by(models.F('enrollment_date').desc(nulls_last=True))
62
64
 
63
65
 
64
66
  class LearningPath(TimeStampedModel):
@@ -289,12 +291,6 @@ class LearningPathEnrollment(TimeStampedModel):
289
291
  default=True,
290
292
  help_text=_("Indicates if the learner is enrolled or not in the Learning Path"),
291
293
  )
292
- enrolled_at = models.DateTimeField(
293
- auto_now_add=True,
294
- help_text=_(
295
- "Timestamp of enrollment or un-enrollment. To be explicitly set when performing a learner enrollment."
296
- ),
297
- )
298
294
  tracker = FieldTracker(fields=["is_active"])
299
295
 
300
296
  def __str__(self):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: learning-paths-plugin
3
- Version: 0.3.4rc6
3
+ Version: 0.3.4rc7
4
4
  Summary: Learning Paths plugin
5
5
  Home-page: https://github.com/open-craft/learning-paths-plugin
6
6
  Author: OpenCraft
@@ -133,6 +133,7 @@ Changed
133
133
  =======
134
134
 
135
135
  * The Learning Paths API includes start and end dates for its steps.
136
+ * Return enrollment date in the API instead of a boolean.
136
137
 
137
138
  0.3.3 - 2025-05-23
138
139
  ******************
@@ -38,6 +38,7 @@ 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
40
  learning_paths/migrations/0014_learningpathenrollmentallowed_is_active.py
41
+ learning_paths/migrations/0015_remove_learningpathenrollment_enrolled_at.py
41
42
  learning_paths/migrations/__init__.py
42
43
  learning_paths/templates/learning_paths/base.html
43
44
  learning_paths_plugin.egg-info/PKG-INFO