learning-credentials 0.3.0rc2__py3-none-any.whl → 0.3.0rc4__py3-none-any.whl
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/api/v1/serializers.py +1 -1
- learning_credentials/api/v1/urls.py +7 -1
- learning_credentials/api/v1/views.py +80 -6
- learning_credentials/apps.py +4 -4
- {learning_credentials-0.3.0rc2.dist-info → learning_credentials-0.3.0rc4.dist-info}/METADATA +1 -1
- {learning_credentials-0.3.0rc2.dist-info → learning_credentials-0.3.0rc4.dist-info}/RECORD +10 -10
- {learning_credentials-0.3.0rc2.dist-info → learning_credentials-0.3.0rc4.dist-info}/entry_points.txt +0 -3
- {learning_credentials-0.3.0rc2.dist-info → learning_credentials-0.3.0rc4.dist-info}/WHEEL +0 -0
- {learning_credentials-0.3.0rc2.dist-info → learning_credentials-0.3.0rc4.dist-info}/licenses/LICENSE.txt +0 -0
- {learning_credentials-0.3.0rc2.dist-info → learning_credentials-0.3.0rc4.dist-info}/top_level.txt +0 -0
|
@@ -45,7 +45,7 @@ class CredentialEligibilitySerializer(serializers.Serializer):
|
|
|
45
45
|
name = serializers.CharField()
|
|
46
46
|
is_eligible = serializers.BooleanField()
|
|
47
47
|
existing_credential = serializers.UUIDField(required=False, allow_null=True)
|
|
48
|
-
existing_credential_url = serializers.URLField(required=False, allow_null=True)
|
|
48
|
+
existing_credential_url = serializers.URLField(required=False, allow_blank=True, allow_null=True)
|
|
49
49
|
|
|
50
50
|
current_grades = serializers.DictField(required=False)
|
|
51
51
|
required_grades = serializers.DictField(required=False)
|
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
from django.urls import path
|
|
4
4
|
|
|
5
|
-
from .views import CredentialEligibilityView, CredentialListView
|
|
5
|
+
from .views import CredentialConfigurationCheckView, CredentialEligibilityView, CredentialListView
|
|
6
6
|
|
|
7
7
|
urlpatterns = [
|
|
8
|
+
# Credential configuration check endpoint
|
|
9
|
+
path(
|
|
10
|
+
'configured/<str:learning_context_key>/',
|
|
11
|
+
CredentialConfigurationCheckView.as_view(),
|
|
12
|
+
name='credential-configuration-check',
|
|
13
|
+
),
|
|
8
14
|
# Credential eligibility endpoints
|
|
9
15
|
path('eligibility/<str:learning_context_key>/', CredentialEligibilityView.as_view(), name='credential-eligibility'),
|
|
10
16
|
path(
|
|
@@ -28,6 +28,66 @@ if TYPE_CHECKING:
|
|
|
28
28
|
logger = logging.getLogger(__name__)
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
class CredentialConfigurationCheckView(APIView):
|
|
32
|
+
"""API view to check if any credentials are configured for a specific learning context."""
|
|
33
|
+
|
|
34
|
+
permission_classes = (IsAuthenticated, IsAdminOrSelf, CanAccessLearningContext)
|
|
35
|
+
|
|
36
|
+
@apidocs.schema(
|
|
37
|
+
parameters=[
|
|
38
|
+
apidocs.string_parameter(
|
|
39
|
+
"learning_context_key",
|
|
40
|
+
ParameterLocation.PATH,
|
|
41
|
+
description=(
|
|
42
|
+
"Learning context identifier. Can be a course key (course-v1:OpenedX+DemoX+DemoCourse) "
|
|
43
|
+
"or learning path key (path-v1:OpenedX+DemoX+DemoPath+Demo)"
|
|
44
|
+
),
|
|
45
|
+
),
|
|
46
|
+
],
|
|
47
|
+
responses={
|
|
48
|
+
200: "Boolean indicating if credentials are configured.",
|
|
49
|
+
400: "Invalid context key format.",
|
|
50
|
+
404: "Learning context not found or user does not have access.",
|
|
51
|
+
},
|
|
52
|
+
)
|
|
53
|
+
def get(self, _request: "Request", learning_context_key: str) -> Response:
|
|
54
|
+
"""
|
|
55
|
+
Check if any credentials are configured for the given learning context.
|
|
56
|
+
|
|
57
|
+
**Example Request**
|
|
58
|
+
|
|
59
|
+
GET /api/learning_credentials/v1/configured/course-v1:OpenedX+DemoX+DemoCourse/
|
|
60
|
+
|
|
61
|
+
**Response Values**
|
|
62
|
+
|
|
63
|
+
If the request is successful, an HTTP 200 "OK" response is returned.
|
|
64
|
+
|
|
65
|
+
**Example Response**
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"has_credentials": true,
|
|
70
|
+
"credential_count": 2
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Response Fields**
|
|
75
|
+
- `has_credentials`: Boolean indicating if any credentials are configured
|
|
76
|
+
- `credential_count`: Number of credential configurations available
|
|
77
|
+
|
|
78
|
+
**Note**
|
|
79
|
+
This endpoint does not perform learning context existence validation, so it will not return 404 for staff users.
|
|
80
|
+
"""
|
|
81
|
+
credential_count = CredentialConfiguration.objects.filter(learning_context_key=learning_context_key).count()
|
|
82
|
+
|
|
83
|
+
response_data = {
|
|
84
|
+
'has_credentials': credential_count > 0,
|
|
85
|
+
'credential_count': credential_count,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return Response(response_data, status=status.HTTP_200_OK)
|
|
89
|
+
|
|
90
|
+
|
|
31
91
|
class CredentialEligibilityView(APIView):
|
|
32
92
|
"""
|
|
33
93
|
API view for credential eligibility and generation.
|
|
@@ -37,6 +97,10 @@ class CredentialEligibilityView(APIView):
|
|
|
37
97
|
Supported Learning Contexts:
|
|
38
98
|
- Course keys: `course-v1:org+course+run`
|
|
39
99
|
- Learning path keys: `path-v1:org+path+run+group`
|
|
100
|
+
|
|
101
|
+
**Staff Features**:
|
|
102
|
+
- Staff users can view eligibility for any user by providing `username` parameter
|
|
103
|
+
- Non-staff users can only view their own eligibility
|
|
40
104
|
"""
|
|
41
105
|
|
|
42
106
|
permission_classes = (IsAuthenticated, IsAdminOrSelf, CanAccessLearningContext)
|
|
@@ -93,6 +157,9 @@ class CredentialEligibilityView(APIView):
|
|
|
93
157
|
- Step-by-step progress for learning paths
|
|
94
158
|
- Eligibility status for each credential type
|
|
95
159
|
|
|
160
|
+
**Query Parameters:**
|
|
161
|
+
- `username` (staff only): View eligibility for a specific user
|
|
162
|
+
|
|
96
163
|
**Example Request**
|
|
97
164
|
|
|
98
165
|
GET /api/learning_credentials/v1/eligibility/course-v1:OpenedX+DemoX+DemoCourse/
|
|
@@ -150,11 +217,14 @@ class CredentialEligibilityView(APIView):
|
|
|
150
217
|
}
|
|
151
218
|
```
|
|
152
219
|
"""
|
|
220
|
+
username = request.query_params.get('username')
|
|
221
|
+
user = get_object_or_404(User, username=username) if username else request.user
|
|
222
|
+
|
|
153
223
|
configurations = CredentialConfiguration.objects.filter(
|
|
154
224
|
learning_context_key=learning_context_key
|
|
155
225
|
).select_related('credential_type')
|
|
156
226
|
|
|
157
|
-
eligibility_data = [self._get_eligibility_data(
|
|
227
|
+
eligibility_data = [self._get_eligibility_data(user, config) for config in configurations]
|
|
158
228
|
|
|
159
229
|
response_data = {
|
|
160
230
|
'context_key': learning_context_key,
|
|
@@ -210,6 +280,9 @@ class CredentialEligibilityView(APIView):
|
|
|
210
280
|
**Notification:**
|
|
211
281
|
Users will receive an email notification when credential generation completes.
|
|
212
282
|
|
|
283
|
+
**Query Parameters:**
|
|
284
|
+
- `username` (staff only): Trigger credential generation for a specific user
|
|
285
|
+
|
|
213
286
|
**Example Request**
|
|
214
287
|
|
|
215
288
|
POST /api/learning_credentials/v1/eligibility/course-v1:OpenedX+DemoX+DemoCourse/1/
|
|
@@ -228,6 +301,9 @@ class CredentialEligibilityView(APIView):
|
|
|
228
301
|
}
|
|
229
302
|
```
|
|
230
303
|
"""
|
|
304
|
+
username = request.query_params.get('username')
|
|
305
|
+
user = get_object_or_404(User, username=username) if username else request.user
|
|
306
|
+
|
|
231
307
|
config = get_object_or_404(
|
|
232
308
|
CredentialConfiguration.objects.select_related('credential_type'),
|
|
233
309
|
learning_context_key=learning_context_key,
|
|
@@ -236,7 +312,7 @@ class CredentialEligibilityView(APIView):
|
|
|
236
312
|
|
|
237
313
|
existing_credential = (
|
|
238
314
|
Credential.objects.filter(
|
|
239
|
-
user_id=
|
|
315
|
+
user_id=user.id,
|
|
240
316
|
learning_context_key=learning_context_key,
|
|
241
317
|
credential_type=config.credential_type.name,
|
|
242
318
|
)
|
|
@@ -247,10 +323,10 @@ class CredentialEligibilityView(APIView):
|
|
|
247
323
|
if existing_credential:
|
|
248
324
|
return Response({"detail": "User already has a credential of this type."}, status=status.HTTP_409_CONFLICT)
|
|
249
325
|
|
|
250
|
-
if not config.get_eligible_user_ids(user_id=
|
|
326
|
+
if not config.get_eligible_user_ids(user_id=user.id):
|
|
251
327
|
return Response({"detail": "User is not eligible for this credential."}, status=status.HTTP_400_BAD_REQUEST)
|
|
252
328
|
|
|
253
|
-
generate_credential_for_user_task.delay(config.id,
|
|
329
|
+
generate_credential_for_user_task.delay(config.id, user.id)
|
|
254
330
|
return Response({"detail": "Credential generation started."}, status=status.HTTP_201_CREATED)
|
|
255
331
|
|
|
256
332
|
|
|
@@ -307,8 +383,6 @@ class CredentialListView(APIView):
|
|
|
307
383
|
|
|
308
384
|
**Query Parameters:**
|
|
309
385
|
- `username` (staff only): View credentials for a specific user
|
|
310
|
-
|
|
311
|
-
**Path Parameters:**
|
|
312
386
|
- `learning_context_key` (optional): Filter credentials by learning context
|
|
313
387
|
|
|
314
388
|
**Response includes:**
|
learning_credentials/apps.py
CHANGED
|
@@ -26,9 +26,9 @@ class LearningCredentialsConfig(AppConfig):
|
|
|
26
26
|
'common': {'relative_path': 'settings.common'},
|
|
27
27
|
'production': {'relative_path': 'settings.production'},
|
|
28
28
|
},
|
|
29
|
-
'cms.djangoapp': {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
},
|
|
29
|
+
# 'cms.djangoapp': {
|
|
30
|
+
# 'common': {'relative_path': 'settings.common'},
|
|
31
|
+
# 'production': {'relative_path': 'settings.production'},
|
|
32
|
+
# },
|
|
33
33
|
},
|
|
34
34
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
learning_credentials/__init__.py,sha256=8Q0-3Hdnfmcj41EKu1GSfzEfwWcYNDlItyEEke2r9bs,62
|
|
2
2
|
learning_credentials/admin.py,sha256=ynK3tVJwLsIeV7Jk66t1FAVyVsU1G-KRIAdRkycVTmA,10439
|
|
3
|
-
learning_credentials/apps.py,sha256=
|
|
3
|
+
learning_credentials/apps.py,sha256=COijTeVYYFZgQP_-gHJNy2ITqhFT0dfimC0PtYwnynM,1136
|
|
4
4
|
learning_credentials/compat.py,sha256=g32MLgTnSZLGa6H3Qsq1WIPJXWPlFaNVuyZfbZXsKR8,4832
|
|
5
5
|
learning_credentials/core_api.py,sha256=ZVNPm7SQk54roZ8rmZ0Bc6T6NLvumKWJZjJepAxM6L8,3232
|
|
6
6
|
learning_credentials/exceptions.py,sha256=UaqBVXFMWR2Iob7_LMb3j4NNVmWQFAgLi_MNMRUvGsI,290
|
|
@@ -14,9 +14,9 @@ learning_credentials/api/__init__.py,sha256=q8sLFfwo5RwQu8FY6BJUL_Jrt3TUojbZK-Zl
|
|
|
14
14
|
learning_credentials/api/urls.py,sha256=JfGSbzvC5d7s9dRq4C0d-AzTDuOVnen3wvFYSJQoEdQ,255
|
|
15
15
|
learning_credentials/api/v1/__init__.py,sha256=A7ZqENtM4QM1A7j_cAfnzw4zn0kuyfXSWtylFIE0_f8,43
|
|
16
16
|
learning_credentials/api/v1/permissions.py,sha256=DZMa2A6Q1ltPdWCksr7I8Ltv_qV46B9yvMM0UXXWo8E,2379
|
|
17
|
-
learning_credentials/api/v1/serializers.py,sha256=
|
|
18
|
-
learning_credentials/api/v1/urls.py,sha256=
|
|
19
|
-
learning_credentials/api/v1/views.py,sha256=
|
|
17
|
+
learning_credentials/api/v1/serializers.py,sha256=etBfIlTnb3vC8GcwTOQUUVRnTP3d_f_7Ca-azCm2O_0,2703
|
|
18
|
+
learning_credentials/api/v1/urls.py,sha256=sS7KK-GUgKsGYmhbCqx86O3SWofNvg1KnxeBAJpzwtk,831
|
|
19
|
+
learning_credentials/api/v1/views.py,sha256=OBORrqOVm1OhqGoqXp1yYt-6FqnXs8LdqAmDSs3g_dM,16319
|
|
20
20
|
learning_credentials/conf/locale/config.yaml,sha256=jPen2DmckNDKK30axCKEd2Q2ha9oOG3IBxrJ63Pvznk,2280
|
|
21
21
|
learning_credentials/migrations/0001_initial.py,sha256=61EvThCv-0UAnhCE5feyQVfjRodbp-6cDaAr4CY5PMA,8435
|
|
22
22
|
learning_credentials/migrations/0002_migrate_to_learning_credentials.py,sha256=vUhcnQKDdwOsppkXsjz2zZwOGMwIJ-fkQRsaj-K7l1o,1779
|
|
@@ -37,9 +37,9 @@ learning_credentials/templates/learning_credentials/edx_ace/certificate_generate
|
|
|
37
37
|
learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/from_name.txt,sha256=-n8tjPSwfwAfeOSZ1WhcCTrpOah4VswzMZ5mh63Pxow,20
|
|
38
38
|
learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/head.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
39
|
learning_credentials/templates/learning_credentials/edx_ace/certificate_generated/email/subject.txt,sha256=S7Hc5T_sZSsSBXm5_H5HBNNv16Ohl0oZn0nVqqeWL0g,132
|
|
40
|
-
learning_credentials-0.3.
|
|
41
|
-
learning_credentials-0.3.
|
|
42
|
-
learning_credentials-0.3.
|
|
43
|
-
learning_credentials-0.3.
|
|
44
|
-
learning_credentials-0.3.
|
|
45
|
-
learning_credentials-0.3.
|
|
40
|
+
learning_credentials-0.3.0rc4.dist-info/licenses/LICENSE.txt,sha256=GDpsPnW_1NKhPvZpZL9imz25P2nIpbwJPEhrlq4vPAU,34523
|
|
41
|
+
learning_credentials-0.3.0rc4.dist-info/METADATA,sha256=Twx2Sgek5BKOv8UZYWsIQd0Z5Ph-1sONWyjIfY61_2I,6864
|
|
42
|
+
learning_credentials-0.3.0rc4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
43
|
+
learning_credentials-0.3.0rc4.dist-info/entry_points.txt,sha256=6aT7brHXkmLp829ZgV8t9R__wXLsLOsYKmZWOvPlGyc,166
|
|
44
|
+
learning_credentials-0.3.0rc4.dist-info/top_level.txt,sha256=Ce-4_leZe_nny7CpmkeRiemcDV6jIHpIvLjlcQBuf18,21
|
|
45
|
+
learning_credentials-0.3.0rc4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{learning_credentials-0.3.0rc2.dist-info → learning_credentials-0.3.0rc4.dist-info}/top_level.txt
RENAMED
|
File without changes
|