edx-enterprise-data 9.7.6__py3-none-any.whl → 9.7.8__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.
- {edx_enterprise_data-9.7.6.dist-info → edx_enterprise_data-9.7.8.dist-info}/METADATA +1 -1
- {edx_enterprise_data-9.7.6.dist-info → edx_enterprise_data-9.7.8.dist-info}/RECORD +17 -15
- enterprise_data/__init__.py +1 -1
- enterprise_data/api/v1/serializers.py +17 -3
- enterprise_data/api/v1/urls.py +5 -0
- enterprise_data/api/v1/views/enterprise_admin.py +22 -0
- enterprise_data/api/v1/views/enterprise_learner.py +14 -5
- enterprise_data/management/commands/tests/test_create_enterprise_learner_enrollment_lpr_v1.py +4 -0
- enterprise_data/migrations/0045_alter_enterpriseexecedlcmoduleperformance_options_and_more.py +27 -0
- enterprise_data/migrations/0046_enterprisegroupmembership.py +36 -0
- enterprise_data/models.py +46 -0
- enterprise_data/renderers.py +3 -3
- enterprise_data/tests/test_models.py +27 -0
- enterprise_data/tests/test_utils.py +26 -0
- {edx_enterprise_data-9.7.6.dist-info → edx_enterprise_data-9.7.8.dist-info}/LICENSE +0 -0
- {edx_enterprise_data-9.7.6.dist-info → edx_enterprise_data-9.7.8.dist-info}/WHEEL +0 -0
- {edx_enterprise_data-9.7.6.dist-info → edx_enterprise_data-9.7.8.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,11 @@
|
|
1
|
-
enterprise_data/__init__.py,sha256=
|
1
|
+
enterprise_data/__init__.py,sha256=1Dn8Tjmg0NDT1Fvs8SeZK_FCPkDQ5nXDlKWdaQLKa70,123
|
2
2
|
enterprise_data/apps.py,sha256=aF6hZwDfI2oWj95tUTm_2ikHueQj-jLj-u0GrgzpsQI,414
|
3
3
|
enterprise_data/clients.py,sha256=GvQupy5TVYfO_IKC3yzXSAgNP54r-PtIjidM5ws9Iks,3947
|
4
4
|
enterprise_data/constants.py,sha256=uCKjfpdlMYFZJsAj3n9RMw4Cmg5_6s3NuwocO-fch3s,238
|
5
5
|
enterprise_data/filters.py,sha256=D2EiK12MMpBoz6eOUmTpoJEhj_sH7bA93NRRAdvkDVo,6163
|
6
|
-
enterprise_data/models.py,sha256=
|
6
|
+
enterprise_data/models.py,sha256=INIETN4B6G1EtxVzgBQr6-JMy3ZsHj9K_FCyrS5OFAk,26312
|
7
7
|
enterprise_data/paginators.py,sha256=YPrC5TeXFt-ymenT2H8H2nCbDCnAzJQlH9kFPElRxWE,269
|
8
|
-
enterprise_data/renderers.py,sha256=
|
8
|
+
enterprise_data/renderers.py,sha256=qggCLZklL9ohVcLHLO1pSecPJSDCCR7e_-PVobl9Lj8,3063
|
9
9
|
enterprise_data/signals.py,sha256=8eqNPnlvmfsKf19lGWv5xTIuBgQIqR8EZSp9UYzC8Rc,1024
|
10
10
|
enterprise_data/urls.py,sha256=bqtKF5OEWEwrNmHG3os-pZNuNsmjlhxEqp7yM4TbPf4,243
|
11
11
|
enterprise_data/utils.py,sha256=sDrpBd62DpybCV41QCxRUaCuvch3qKjEhfUp9cA_GV0,2952
|
@@ -30,16 +30,16 @@ enterprise_data/api/v0/serializers.py,sha256=dngZTk6DhRxApchQKCMp1B_c8aVnQtH0NCq
|
|
30
30
|
enterprise_data/api/v0/urls.py,sha256=vzJjqIo_S3AXWs9Us8XTaJc3FnxLbYzAkmLyuDQqum0,699
|
31
31
|
enterprise_data/api/v0/views.py,sha256=4RslZ4NZOU-844bnebEQ71ji2utRY7jEijqC45oQQD0,14380
|
32
32
|
enterprise_data/api/v1/__init__.py,sha256=1aAzAYU5hk-RW6cKUxa1645cbZMxn7GIZ7OMjWc9MKI,46
|
33
|
-
enterprise_data/api/v1/serializers.py,sha256=
|
34
|
-
enterprise_data/api/v1/urls.py,sha256=
|
33
|
+
enterprise_data/api/v1/serializers.py,sha256=mya_ZvP7CTvUTF_ccYkMJZFuzOKIU-aM3eSCQmFazWY,11274
|
34
|
+
enterprise_data/api/v1/urls.py,sha256=Wl1xLboPg-Lq1ZvjAWf51JKYkHlmx02Kpq1nwfDyS8s,4372
|
35
35
|
enterprise_data/api/v1/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
36
36
|
enterprise_data/api/v1/views/analytics_completions.py,sha256=esFbJ5q8ssnm2Mfbc3rZXtiGHF-MeM4KQ4Ft3N7wwHU,6260
|
37
37
|
enterprise_data/api/v1/views/analytics_engagements.py,sha256=Yo-bpA-0xOQHUPTFF0jHWxjm6KpSC-l2nGxhYX0ajBk,6298
|
38
38
|
enterprise_data/api/v1/views/analytics_enrollments.py,sha256=hw87VZ0hFWpwf3QEHFn9cUgDy2s7SzsXt6Rf9TaNsS0,6282
|
39
39
|
enterprise_data/api/v1/views/analytics_leaderboard.py,sha256=3dyo7_OhyGEEeibemBrRsUOo0jbM4LbDgV5gw3YnVig,4186
|
40
40
|
enterprise_data/api/v1/views/base.py,sha256=Kkmd5zgEBAhvwS_GoGXSK6lgbDNwSPioYn-QbnizI3w,3416
|
41
|
-
enterprise_data/api/v1/views/enterprise_admin.py,sha256=
|
42
|
-
enterprise_data/api/v1/views/enterprise_learner.py,sha256=
|
41
|
+
enterprise_data/api/v1/views/enterprise_admin.py,sha256=pb63EG_fpCRvegy7cw9HNeSuzumh3nJVe6KSPcBzsXw,8969
|
42
|
+
enterprise_data/api/v1/views/enterprise_learner.py,sha256=lCB2guZeIDf-CbcdDzT2K5K-OW1Vt5i4R8K9aT1ZzuM,18627
|
43
43
|
enterprise_data/api/v1/views/enterprise_offers.py,sha256=VifxgqTLFLVw4extYPlHcN1N_yjXcsYsAlYEnAbpb10,1266
|
44
44
|
enterprise_data/cache/__init__.py,sha256=fiBUploll1kmDy2vCmnNpeZVTD4ewsgtRF14vVs0Rb4,1850
|
45
45
|
enterprise_data/cache/decorators.py,sha256=vLbXK9VSv-HzVkkXS1-TkuSMxudwyxz04WFsAXqmjuM,1273
|
@@ -58,7 +58,7 @@ enterprise_data/management/commands/pre_warm_analytics_cache.py,sha256=3MCThZiZg
|
|
58
58
|
enterprise_data/management/commands/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
59
59
|
enterprise_data/management/commands/tests/test_create_dummy_data_lpr_v1.py,sha256=wt9fqAFKQVTqllpZ42dch-n31JavUifUIB9CKNYcnYM,1086
|
60
60
|
enterprise_data/management/commands/tests/test_create_enterprise_enrollment.py,sha256=5CABLk8qAx8RP8mrFtnbJ4-xVkf9-5Mq6iQcx8jBfFc,1742
|
61
|
-
enterprise_data/management/commands/tests/test_create_enterprise_learner_enrollment_lpr_v1.py,sha256=
|
61
|
+
enterprise_data/management/commands/tests/test_create_enterprise_learner_enrollment_lpr_v1.py,sha256=cw8PTUhwkFZeI3agCRLRMxKBRO6k1Ukft2CJIRUUMoY,4139
|
62
62
|
enterprise_data/management/commands/tests/test_create_enterprise_learner_lpr_v1.py,sha256=P7wyyaZ4LMWv1umpCThp4By5DWjtI7kYAmY0AeuY2XI,1637
|
63
63
|
enterprise_data/management/commands/tests/test_create_enterprise_user.py,sha256=0uxG332-jYpaxUIrMtIVRTwvRfxTWEQBIwZlIQO7f2g,1469
|
64
64
|
enterprise_data/management/commands/tests/test_pre_warm_analytics_cache.py,sha256=Nbwsl5CYMbi5cN2IwmUtTs5b_MeU6T0lquk3x8Tuh_4,2108
|
@@ -104,6 +104,8 @@ enterprise_data/migrations/0038_enterpriseoffer_export_timestamp.py,sha256=8St3D
|
|
104
104
|
enterprise_data/migrations/0039_auto_20240212_1403.py,sha256=rMiJcYx26ZGqPdQzgiWkUgbcRhD-tp9BgXdgt67vC2Q,1002
|
105
105
|
enterprise_data/migrations/0040_auto_20240718_0536_squashed_0043_alter_enterpriselearnerenrollment_enterprise_enrollment_id.py,sha256=Kq_ResqoAsJjtyIysHjs4VaDxNh6p4fPMMyh93GIJxA,1693
|
106
106
|
enterprise_data/migrations/0044_enterpriseexecedlcmoduleperformance.py,sha256=1aJ0wIu9K2cT3AJdBIHuNCUpJysPWmq_37Ucp5ntEO8,6122
|
107
|
+
enterprise_data/migrations/0045_alter_enterpriseexecedlcmoduleperformance_options_and_more.py,sha256=HJkY2OvuE39ciWgaEescd--ixQhTVrwzbL0_bvNofSo,876
|
108
|
+
enterprise_data/migrations/0046_enterprisegroupmembership.py,sha256=38qfi3EySDLrpP3gDuO_oUl_7rphGPZIrPsCuBhQ_dk,1766
|
107
109
|
enterprise_data/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
108
110
|
enterprise_data/settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
109
111
|
enterprise_data/settings/test.py,sha256=4-flfrlf81AthGx9wTaT5PscyoOWyhsDDqbzBl-z7Eg,4191
|
@@ -112,8 +114,8 @@ enterprise_data/tests/factories.py,sha256=EqiB8xDVN9n6ZrFV0d4h6hEiRF57dpbmOjHezU
|
|
112
114
|
enterprise_data/tests/mixins.py,sha256=YifptI9mtOhAWnBGyPUy4kX5OJNSDP3DvW2vb1E2tvw,805
|
113
115
|
enterprise_data/tests/test_clients.py,sha256=xBPHF9cgEFqNJoL4klOoYh_sVS3scZGcX0Ltc9Ghp7A,6336
|
114
116
|
enterprise_data/tests/test_filters.py,sha256=ZBbLl9Sgj5mJ7lTWoaFcEPwuxPDpIbMo2n_Fhurc0T8,7263
|
115
|
-
enterprise_data/tests/test_models.py,sha256=
|
116
|
-
enterprise_data/tests/test_utils.py,sha256=
|
117
|
+
enterprise_data/tests/test_models.py,sha256=rRFrzjMnnUh1YpVvQMyA1OhFmX6BOCRt28SNAiPq6Ac,4179
|
118
|
+
enterprise_data/tests/test_utils.py,sha256=3sM3YWEoihHupgwhh0NiI66CbcQPLY4I_W0xjbLG0zg,19694
|
117
119
|
enterprise_data/tests/test_views.py,sha256=UvDRNTxruy5zBK_KgUy2cBMbwlaTW_vkM0-TCXbQZiY,69667
|
118
120
|
enterprise_data/tests/admin_analytics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
119
121
|
enterprise_data/tests/admin_analytics/mock_analytics_data.py,sha256=qN73YxoD1kAtKsQo94avR0DGMCsJClrpqFxxYC6reqE,15248
|
@@ -171,8 +173,8 @@ enterprise_reporting/tests/test_send_enterprise_reports.py,sha256=WtL-RqGgu2x5PP
|
|
171
173
|
enterprise_reporting/tests/test_utils.py,sha256=Zt_TA0LVb-B6fQGkUkAKKVlUKKnQh8jnw1US1jKe7g8,9493
|
172
174
|
enterprise_reporting/tests/test_vertica_client.py,sha256=-R2yNCGUjRtoXwLMBloVFQkFYrJoo613VCr61gwI3kQ,140
|
173
175
|
enterprise_reporting/tests/utils.py,sha256=xms2LM7DV3wczXEfctOK1ddel1EE0J_YSr17UzbCDy4,1401
|
174
|
-
edx_enterprise_data-9.7.
|
175
|
-
edx_enterprise_data-9.7.
|
176
|
-
edx_enterprise_data-9.7.
|
177
|
-
edx_enterprise_data-9.7.
|
178
|
-
edx_enterprise_data-9.7.
|
176
|
+
edx_enterprise_data-9.7.8.dist-info/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
|
177
|
+
edx_enterprise_data-9.7.8.dist-info/METADATA,sha256=hzNLnjmy0m9bHPG-_d51mBN7NWqN5muqnVCS5FzCBAU,1569
|
178
|
+
edx_enterprise_data-9.7.8.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
179
|
+
edx_enterprise_data-9.7.8.dist-info/top_level.txt,sha256=f5F2kU-dob6MqiHJpgZkFzoCD5VMhsdpkTV5n9Tvq3I,59
|
180
|
+
edx_enterprise_data-9.7.8.dist-info/RECORD,,
|
enterprise_data/__init__.py
CHANGED
@@ -10,6 +10,7 @@ from enterprise_data.models import (
|
|
10
10
|
EnterpriseAdminLearnerProgress,
|
11
11
|
EnterpriseAdminSummarizeInsights,
|
12
12
|
EnterpriseExecEdLCModulePerformance,
|
13
|
+
EnterpriseGroupMembership,
|
13
14
|
EnterpriseLearner,
|
14
15
|
EnterpriseLearnerEnrollment,
|
15
16
|
EnterpriseOffer,
|
@@ -41,9 +42,9 @@ class EnterpriseLearnerEnrollmentSerializer(serializers.ModelSerializer):
|
|
41
42
|
'course_primary_program', 'primary_program_type', 'course_primary_subject', 'has_passed',
|
42
43
|
'last_activity_date', 'progress_status', 'passed_date', 'current_grade',
|
43
44
|
'letter_grade', 'enterprise_user_id', 'user_email', 'user_account_creation_date',
|
44
|
-
'user_country_code', 'user_username', '
|
45
|
-
'enterprise_sso_uid', 'created', 'course_api_url', 'total_learning_time_hours',
|
46
|
-
'course_product_line', 'budget_id', 'enterprise_group_name', 'enterprise_group_uuid',
|
45
|
+
'user_country_code', 'user_username', 'user_first_name', 'user_last_name', 'enterprise_name',
|
46
|
+
'enterprise_customer_uuid', 'enterprise_sso_uid', 'created', 'course_api_url', 'total_learning_time_hours',
|
47
|
+
'is_subsidy', 'course_product_line', 'budget_id', 'enterprise_group_name', 'enterprise_group_uuid',
|
47
48
|
)
|
48
49
|
|
49
50
|
def get_course_api_url(self, obj):
|
@@ -253,6 +254,19 @@ class EnterpriseBudgetSerializer(serializers.ModelSerializer):
|
|
253
254
|
)
|
254
255
|
|
255
256
|
|
257
|
+
class EnterpriseGroupMembershipSerializer(serializers.ModelSerializer):
|
258
|
+
"""
|
259
|
+
Serializer for EnterpriseGroupMembership model.
|
260
|
+
"""
|
261
|
+
|
262
|
+
class Meta:
|
263
|
+
model = EnterpriseGroupMembership
|
264
|
+
fields = (
|
265
|
+
'enterprise_group_uuid',
|
266
|
+
'enterprise_group_name',
|
267
|
+
)
|
268
|
+
|
269
|
+
|
256
270
|
class AdvanceAnalyticsQueryParamSerializer(serializers.Serializer): # pylint: disable=abstract-method
|
257
271
|
"""Serializer for validating query params"""
|
258
272
|
RESPONSE_TYPES = [
|
enterprise_data/api/v1/urls.py
CHANGED
@@ -101,6 +101,11 @@ urlpatterns = [
|
|
101
101
|
enterprise_admin_views.EnterpriseBudgetView.as_view(),
|
102
102
|
name='enterprise-budgets'
|
103
103
|
),
|
104
|
+
re_path(
|
105
|
+
fr'^enterprise/(?P<enterprise_uuid>{UUID4_REGEX})/groups',
|
106
|
+
enterprise_admin_views.EnterpriseGroupMembershipView.as_view(),
|
107
|
+
name='enterprise-groups'
|
108
|
+
),
|
104
109
|
]
|
105
110
|
|
106
111
|
urlpatterns += router.urls
|
@@ -21,6 +21,7 @@ from enterprise_data.models import (
|
|
21
21
|
EnterpriseAdminLearnerProgress,
|
22
22
|
EnterpriseAdminSummarizeInsights,
|
23
23
|
EnterpriseExecEdLCModulePerformance,
|
24
|
+
EnterpriseGroupMembership,
|
24
25
|
EnterpriseSubsidyBudget,
|
25
26
|
)
|
26
27
|
from enterprise_data.utils import timer
|
@@ -230,3 +231,24 @@ class EnterpriseBudgetView(APIView):
|
|
230
231
|
|
231
232
|
serializer = serializers.EnterpriseBudgetSerializer(budgets, many=True)
|
232
233
|
return Response(serializer.data)
|
234
|
+
|
235
|
+
|
236
|
+
class EnterpriseGroupMembershipView(APIView):
|
237
|
+
"""
|
238
|
+
View for getting Group Memberships information for an enterprise.
|
239
|
+
"""
|
240
|
+
authentication_classes = (JwtAuthentication,)
|
241
|
+
http_method_names = ["get"]
|
242
|
+
|
243
|
+
@permission_required("can_access_enterprise", fn=lambda request, enterprise_uuid: enterprise_uuid)
|
244
|
+
def get(self, request, enterprise_uuid):
|
245
|
+
"""
|
246
|
+
Returns the groups and budgets for an enterprise.
|
247
|
+
"""
|
248
|
+
groups = EnterpriseGroupMembership.objects.filter(
|
249
|
+
enterprise_customer_id=enterprise_uuid,
|
250
|
+
group_type='flex',
|
251
|
+
)
|
252
|
+
|
253
|
+
serializer = serializers.EnterpriseGroupMembershipSerializer(groups, many=True)
|
254
|
+
return Response(serializer.data)
|
@@ -12,7 +12,7 @@ from rest_framework.response import Response
|
|
12
12
|
|
13
13
|
from django.conf import settings
|
14
14
|
from django.core.paginator import Paginator
|
15
|
-
from django.db.models import Count, Max, OuterRef, Prefetch, Q, Subquery, Value
|
15
|
+
from django.db.models import Count, Exists, Max, OuterRef, Prefetch, Q, Subquery, Value
|
16
16
|
from django.db.models.fields import IntegerField
|
17
17
|
from django.db.models.functions import Coalesce
|
18
18
|
from django.http import StreamingHttpResponse
|
@@ -20,7 +20,7 @@ from django.utils import timezone
|
|
20
20
|
|
21
21
|
from enterprise_data.admin_analytics.database.utils import LOGGER
|
22
22
|
from enterprise_data.api.v1 import serializers
|
23
|
-
from enterprise_data.models import EnterpriseLearner, EnterpriseLearnerEnrollment
|
23
|
+
from enterprise_data.models import EnterpriseGroupMembership, EnterpriseLearner, EnterpriseLearnerEnrollment
|
24
24
|
from enterprise_data.paginators import EnterpriseEnrollmentsPagination
|
25
25
|
from enterprise_data.renderers import EnrollmentsCSVRenderer
|
26
26
|
from enterprise_data.utils import subtract_one_month
|
@@ -59,9 +59,9 @@ class EnterpriseLearnerEnrollmentViewSet(EnterpriseViewSetMixin, viewsets.ReadOn
|
|
59
59
|
'course_primary_program', 'primary_program_type', 'course_primary_subject', 'has_passed',
|
60
60
|
'last_activity_date', 'progress_status', 'passed_date', 'current_grade',
|
61
61
|
'letter_grade', 'enterprise_user_id', 'user_email', 'user_account_creation_date',
|
62
|
-
'user_country_code', 'user_username', '
|
63
|
-
'enterprise_sso_uid', 'created', 'course_api_url', 'total_learning_time_hours',
|
64
|
-
'course_product_line', 'budget_id'
|
62
|
+
'user_country_code', 'user_username', 'user_first_name', 'user_last_name', 'enterprise_name',
|
63
|
+
'enterprise_customer_uuid', 'enterprise_sso_uid', 'created', 'course_api_url', 'total_learning_time_hours',
|
64
|
+
'is_subsidy', 'course_product_line', 'budget_id',
|
65
65
|
]
|
66
66
|
|
67
67
|
# TODO: Remove after we release the streaming csv changes
|
@@ -174,6 +174,15 @@ class EnterpriseLearnerEnrollmentViewSet(EnterpriseViewSetMixin, viewsets.ReadOn
|
|
174
174
|
if is_subsidy:
|
175
175
|
queryset = queryset.filter(is_subsidy=is_subsidy)
|
176
176
|
|
177
|
+
group_uuid = query_filters.get('group_uuid')
|
178
|
+
if group_uuid:
|
179
|
+
flex_group_exists = EnterpriseGroupMembership.objects.filter(
|
180
|
+
enterprise_customer_user_id=OuterRef('enterprise_user_id'),
|
181
|
+
enterprise_group_uuid=group_uuid,
|
182
|
+
group_type='flex'
|
183
|
+
)
|
184
|
+
queryset = queryset.filter(Exists(flex_group_exists))
|
185
|
+
|
177
186
|
return queryset
|
178
187
|
|
179
188
|
def filter_by_offer_id(self, queryset, offer_id):
|
enterprise_data/management/commands/tests/test_create_enterprise_learner_enrollment_lpr_v1.py
CHANGED
@@ -39,6 +39,8 @@ class TestCreateEnterpriseLearnerEnrollmentCommand(TestCase):
|
|
39
39
|
assert enterprise_learner_enrollment[0].letter_grade is None
|
40
40
|
assert enterprise_learner_enrollment[0].enterprise_user_id is None
|
41
41
|
assert enterprise_learner_enrollment[0].user_username is None
|
42
|
+
assert enterprise_learner_enrollment[0].user_first_name is None
|
43
|
+
assert enterprise_learner_enrollment[0].user_last_name is None
|
42
44
|
assert enterprise_learner_enrollment[0].user_email is None
|
43
45
|
assert enterprise_learner_enrollment[0].enterprise_user is None
|
44
46
|
|
@@ -62,6 +64,8 @@ class TestCreateEnterpriseLearnerEnrollmentCommand(TestCase):
|
|
62
64
|
assert enterprise_learner_enrollment[0].progress_status is not None
|
63
65
|
assert enterprise_learner_enrollment[0].enterprise_user_id is not None
|
64
66
|
assert enterprise_learner_enrollment[0].user_username is not None
|
67
|
+
assert enterprise_learner_enrollment[0].user_first_name is not None
|
68
|
+
assert enterprise_learner_enrollment[0].user_last_name is not None
|
65
69
|
assert enterprise_learner_enrollment[0].enterprise_user is not None
|
66
70
|
assert enterprise_learner_enrollment[0].user_email is not None
|
67
71
|
assert EnterpriseLearner.objects.count() == 1
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Generated by Django 4.2.15 on 2024-12-23 12:55
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
|
8
|
+
dependencies = [
|
9
|
+
('enterprise_data', '0044_enterpriseexecedlcmoduleperformance'),
|
10
|
+
]
|
11
|
+
|
12
|
+
operations = [
|
13
|
+
migrations.AlterModelOptions(
|
14
|
+
name='enterpriseexecedlcmoduleperformance',
|
15
|
+
options={'verbose_name': 'Exec Ed LC Module Performance', 'verbose_name_plural': 'Exec Ed LC Module Performance'},
|
16
|
+
),
|
17
|
+
migrations.AddField(
|
18
|
+
model_name='enterpriselearnerenrollment',
|
19
|
+
name='user_first_name',
|
20
|
+
field=models.CharField(max_length=255, null=True),
|
21
|
+
),
|
22
|
+
migrations.AddField(
|
23
|
+
model_name='enterpriselearnerenrollment',
|
24
|
+
name='user_last_name',
|
25
|
+
field=models.CharField(max_length=255, null=True),
|
26
|
+
),
|
27
|
+
]
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Generated by Django 4.2.16 on 2025-01-08 07:31
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
|
8
|
+
dependencies = [
|
9
|
+
('enterprise_data', '0045_alter_enterpriseexecedlcmoduleperformance_options_and_more'),
|
10
|
+
]
|
11
|
+
|
12
|
+
operations = [
|
13
|
+
migrations.CreateModel(
|
14
|
+
name='EnterpriseGroupMembership',
|
15
|
+
fields=[
|
16
|
+
('group_membership_unique_id', models.CharField(max_length=64, primary_key=True, serialize=False)),
|
17
|
+
('is_applies_to_all_contexts', models.BooleanField(default=False)),
|
18
|
+
('enterprise_customer_id', models.UUIDField(db_index=True, null=True)),
|
19
|
+
('enterprise_group_name', models.CharField(max_length=255, null=True)),
|
20
|
+
('enterprise_group_uuid', models.UUIDField(null=True)),
|
21
|
+
('group_is_removed', models.BooleanField(default=False)),
|
22
|
+
('group_type', models.CharField(max_length=128, null=True)),
|
23
|
+
('activated_at', models.DateTimeField(null=True)),
|
24
|
+
('enterprise_customer_user_id', models.PositiveIntegerField(null=True)),
|
25
|
+
('membership_is_removed', models.BooleanField(default=False)),
|
26
|
+
('membership_status', models.CharField(max_length=128, null=True)),
|
27
|
+
('enterprise_group_membership_uuid', models.UUIDField(null=True)),
|
28
|
+
],
|
29
|
+
options={
|
30
|
+
'verbose_name': 'Group Membership',
|
31
|
+
'verbose_name_plural': 'Group Memberships',
|
32
|
+
'db_table': 'group_membership',
|
33
|
+
'indexes': [models.Index(fields=['enterprise_group_uuid', 'group_type', 'enterprise_customer_user_id'], name='group_membe_enterpr_796f48_idx')],
|
34
|
+
},
|
35
|
+
),
|
36
|
+
]
|
enterprise_data/models.py
CHANGED
@@ -128,6 +128,8 @@ class EnterpriseLearnerEnrollment(models.Model):
|
|
128
128
|
user_account_creation_date = models.DateTimeField(null=True)
|
129
129
|
user_country_code = models.CharField(max_length=2, null=True)
|
130
130
|
user_username = models.CharField(max_length=255, null=True)
|
131
|
+
user_first_name = models.CharField(max_length=255, null=True)
|
132
|
+
user_last_name = models.CharField(max_length=255, null=True)
|
131
133
|
enterprise_name = models.CharField(max_length=255, db_index=True, null=False)
|
132
134
|
enterprise_customer_uuid = models.UUIDField(db_index=True, null=False)
|
133
135
|
enterprise_sso_uid = models.CharField(max_length=255, null=True)
|
@@ -396,6 +398,50 @@ class EnterpriseSubsidyBudget(models.Model):
|
|
396
398
|
return self.__str__()
|
397
399
|
|
398
400
|
|
401
|
+
class EnterpriseGroupMembership(models.Model):
|
402
|
+
"""
|
403
|
+
Details of group memberships in enterprise reports.
|
404
|
+
"""
|
405
|
+
|
406
|
+
objects = EnterpriseReportingModelManager()
|
407
|
+
|
408
|
+
class Meta:
|
409
|
+
app_label = 'enterprise_data'
|
410
|
+
db_table = 'group_membership'
|
411
|
+
verbose_name = _("Group Membership")
|
412
|
+
verbose_name_plural = _("Group Memberships")
|
413
|
+
indexes = [
|
414
|
+
models.Index(fields=['enterprise_group_uuid', 'group_type', 'enterprise_customer_user_id']),
|
415
|
+
]
|
416
|
+
|
417
|
+
group_membership_unique_id = models.CharField(max_length=64, primary_key=True)
|
418
|
+
is_applies_to_all_contexts = models.BooleanField(default=False)
|
419
|
+
enterprise_customer_id = models.UUIDField(db_index=True, null=True)
|
420
|
+
enterprise_group_name = models.CharField(max_length=255, null=True)
|
421
|
+
enterprise_group_uuid = models.UUIDField(null=True)
|
422
|
+
group_is_removed = models.BooleanField(default=False)
|
423
|
+
group_type = models.CharField(max_length=128, null=True)
|
424
|
+
activated_at = models.DateTimeField(null=True)
|
425
|
+
enterprise_customer_user_id = models.PositiveIntegerField(null=True)
|
426
|
+
membership_is_removed = models.BooleanField(default=False)
|
427
|
+
membership_status = models.CharField(max_length=128, null=True)
|
428
|
+
enterprise_group_membership_uuid = models.UUIDField(null=True)
|
429
|
+
|
430
|
+
def __str__(self):
|
431
|
+
"""
|
432
|
+
Return a human-readable string representation of the object.
|
433
|
+
"""
|
434
|
+
return (f'<Enterprise Group Membership: {self.enterprise_group_name} '
|
435
|
+
f'(Group UUID: {self.enterprise_group_uuid}) '
|
436
|
+
f'for Customer ID {self.enterprise_customer_id}>')
|
437
|
+
|
438
|
+
def __repr__(self):
|
439
|
+
"""
|
440
|
+
Return uniquely identifying string representation.
|
441
|
+
"""
|
442
|
+
return self.__str__()
|
443
|
+
|
444
|
+
|
399
445
|
class EnterpriseUser(models.Model):
|
400
446
|
"""Information includes a mix of the user's meta data.
|
401
447
|
|
enterprise_data/renderers.py
CHANGED
@@ -25,9 +25,9 @@ class EnrollmentsCSVRenderer(CSVStreamingRenderer):
|
|
25
25
|
'course_primary_program', 'primary_program_type', 'course_primary_subject', 'has_passed',
|
26
26
|
'last_activity_date', 'progress_status', 'passed_date', 'current_grade',
|
27
27
|
'letter_grade', 'enterprise_user_id', 'user_email', 'user_account_creation_date',
|
28
|
-
'user_country_code', 'user_username', '
|
29
|
-
'enterprise_sso_uid', 'created', 'course_api_url', 'total_learning_time_hours',
|
30
|
-
'course_product_line', 'budget_id', 'enterprise_group_name', 'enterprise_group_uuid',
|
28
|
+
'user_country_code', 'user_username', 'user_first_name', 'user_last_name', 'enterprise_name',
|
29
|
+
'enterprise_customer_uuid', 'enterprise_sso_uid', 'created', 'course_api_url', 'total_learning_time_hours',
|
30
|
+
'is_subsidy', 'course_product_line', 'budget_id', 'enterprise_group_name', 'enterprise_group_uuid',
|
31
31
|
]
|
32
32
|
|
33
33
|
|
@@ -10,6 +10,7 @@ from pytest import mark
|
|
10
10
|
|
11
11
|
from enterprise_data.tests.test_utils import (
|
12
12
|
EnterpriseEnrollmentFactory,
|
13
|
+
EnterpriseGroupMembershipFactory,
|
13
14
|
EnterpriseOfferFactory,
|
14
15
|
EnterpriseSubsidyBudgetFactory,
|
15
16
|
EnterpriseUserFactory,
|
@@ -91,6 +92,32 @@ class TestEnterpriseSubsidyBudget(unittest.TestCase):
|
|
91
92
|
assert expected_str == method(self.enterprise_subsidy_budget)
|
92
93
|
|
93
94
|
|
95
|
+
@mark.django_db
|
96
|
+
@ddt.ddt
|
97
|
+
class TestEnterpriseGroupMembership(unittest.TestCase):
|
98
|
+
"""
|
99
|
+
Tests for Enterprise Group Membership model
|
100
|
+
"""
|
101
|
+
|
102
|
+
def setUp(self):
|
103
|
+
self.enterprise_group_membership = EnterpriseGroupMembershipFactory(
|
104
|
+
enterprise_customer_id='ee5e6b3a-069a-4947-bb8d-d2dbc323396c',
|
105
|
+
enterprise_group_name='Test Group',
|
106
|
+
enterprise_group_uuid='ee5e6b3a-069a-4947-bb8d-d2dbc323396d',
|
107
|
+
)
|
108
|
+
super().setUp()
|
109
|
+
|
110
|
+
@ddt.data(str, repr)
|
111
|
+
def test_string_conversion(self, method):
|
112
|
+
"""
|
113
|
+
Test conversion to string.
|
114
|
+
"""
|
115
|
+
expected_str = ('<Enterprise Group Membership: Test Group '
|
116
|
+
'(Group UUID: ee5e6b3a-069a-4947-bb8d-d2dbc323396d) '
|
117
|
+
'for Customer ID ee5e6b3a-069a-4947-bb8d-d2dbc323396c>')
|
118
|
+
assert expected_str == method(self.enterprise_group_membership)
|
119
|
+
|
120
|
+
|
94
121
|
@mark.django_db
|
95
122
|
@ddt.ddt
|
96
123
|
class TestEnterpriseUser(unittest.TestCase):
|
@@ -14,6 +14,7 @@ from django.contrib.auth import get_user_model
|
|
14
14
|
|
15
15
|
from enterprise_data.models import (
|
16
16
|
EnterpriseEnrollment,
|
17
|
+
EnterpriseGroupMembership,
|
17
18
|
EnterpriseLearner,
|
18
19
|
EnterpriseLearnerEnrollment,
|
19
20
|
EnterpriseOffer,
|
@@ -180,6 +181,8 @@ class EnterpriseLearnerEnrollmentFactory(factory.django.DjangoModelFactory):
|
|
180
181
|
enterprise_user_id = factory.Sequence(lambda n: n)
|
181
182
|
user_email = factory.lazy_attribute(lambda x: FAKER.email()) # pylint: disable=no-member
|
182
183
|
user_username = factory.Sequence('robot{}'.format)
|
184
|
+
user_first_name = factory.Sequence('Robot First {}'.format)
|
185
|
+
user_last_name = factory.Sequence('Robot Last {}'.format)
|
183
186
|
user_account_creation_date = factory.lazy_attribute(lambda x: '2018-01-01')
|
184
187
|
user_country_code = factory.lazy_attribute(lambda x: FAKER.country_code())
|
185
188
|
is_subsidy = factory.lazy_attribute(lambda x: FAKER.boolean()) # pylint: disable=no-member
|
@@ -230,6 +233,8 @@ class EnterpriseLearnerEnrollmentFactory(factory.django.DjangoModelFactory):
|
|
230
233
|
'user_account_creation_date',
|
231
234
|
'user_country_code',
|
232
235
|
'user_username',
|
236
|
+
'user_first_name',
|
237
|
+
'user_last_name',
|
233
238
|
]
|
234
239
|
if create and not obj.is_consent_granted:
|
235
240
|
for field in dsc_dependent_fields:
|
@@ -284,6 +289,27 @@ class EnterpriseSubsidyBudgetFactory(factory.django.DjangoModelFactory):
|
|
284
289
|
)
|
285
290
|
|
286
291
|
|
292
|
+
class EnterpriseGroupMembershipFactory(factory.django.DjangoModelFactory):
|
293
|
+
"""
|
294
|
+
EnterpriseGroupMembership model Factory.
|
295
|
+
"""
|
296
|
+
class Meta:
|
297
|
+
model = EnterpriseGroupMembership
|
298
|
+
|
299
|
+
enterprise_customer_id = factory.LazyAttribute(lambda _: FAKER.uuid4())
|
300
|
+
enterprise_group_name = factory.LazyAttribute(lambda _: ' '.join(FAKER.words(nb=2)).title())
|
301
|
+
enterprise_group_uuid = factory.LazyAttribute(lambda _: FAKER.uuid4())
|
302
|
+
group_is_removed = factory.LazyAttribute(lambda _: FAKER.boolean())
|
303
|
+
group_type = factory.LazyAttribute(lambda _: 'budget')
|
304
|
+
activated_at = factory.LazyAttribute(lambda _: FAKER.date_time_this_decade(tzinfo=pytz.UTC))
|
305
|
+
enterprise_customer_user_id = factory.LazyAttribute(lambda _: FAKER.random_int(min=1, max=10000))
|
306
|
+
membership_is_removed = factory.LazyAttribute(lambda _: FAKER.boolean())
|
307
|
+
membership_status = factory.LazyAttribute(lambda _: FAKER.word())
|
308
|
+
enterprise_group_membership_uuid = factory.LazyAttribute(lambda _: FAKER.uuid4())
|
309
|
+
|
310
|
+
is_applies_to_all_contexts = factory.LazyAttribute(lambda _: FAKER.boolean())
|
311
|
+
|
312
|
+
|
287
313
|
class EnterpriseOfferFactory(factory.django.DjangoModelFactory):
|
288
314
|
"""
|
289
315
|
EnterpriseLearner model Factory.
|
File without changes
|
File without changes
|
File without changes
|