edx-enterprise-data 9.5.2__py3-none-any.whl → 9.7.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: edx-enterprise-data
3
- Version: 9.5.2
3
+ Version: 9.7.0
4
4
  Summary: Enterprise Reporting
5
5
  Home-page: https://github.com/openedx/edx-enterprise-data
6
6
  Author: edX
@@ -1,4 +1,4 @@
1
- enterprise_data/__init__.py,sha256=XVbRCh1_SLwP0t-ET7aDPIx_96e0qavT1x_jfk26SuI,123
1
+ enterprise_data/__init__.py,sha256=-XYlQBRXnJXltF46PlBf71t4JXIIDeiUw42P7lcjs5E,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
@@ -20,9 +20,9 @@ enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py,s
20
20
  enterprise_data/admin_analytics/database/queries/skills_daily_rollup_admin_dash.py,sha256=PgWwvtVCK5lbiq6z44lH0fwbkdWYukhyXZL9X8lNWCY,4099
21
21
  enterprise_data/admin_analytics/database/tables/__init__.py,sha256=Z-c3P9hqR-dC9uYKe63qHkQG9Nms8cLE2jRN-4jeMM0,289
22
22
  enterprise_data/admin_analytics/database/tables/base.py,sha256=1KyKsC18pW3m-5U-T6pdt5rIwsz6Wp3QFFbD3r6L6YQ,395
23
- enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py,sha256=bfuNuEUF-x4f_y7WGY2-qapMYuqjZ_S-LDHYvk2OMOU,10324
24
- enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py,sha256=Hu2NUtq8vdX9kyz1lhs0P5l2sgg-nglygTsIb4g-exM,11047
25
- enterprise_data/admin_analytics/database/tables/skills_daily_rollup_admin_dash.py,sha256=9PqLeVqByrz7R0qumRbwJlr5lzIWn7Fl7WEGM0aJVlw,3131
23
+ enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py,sha256=8aJsMsnMw67M6NGeUtWbQ-2g-OwN69XSAlD7KiMfOoA,10523
24
+ enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py,sha256=FFpGtv2PUIDSdTqSZV4F3tVP9uDvS64ZqQXMGygCGok,11294
25
+ enterprise_data/admin_analytics/database/tables/skills_daily_rollup_admin_dash.py,sha256=3xNwSi0wfCyBHcXPd6-9Ujs1NUm8kmZRg_gPrZzp9nQ,3233
26
26
  enterprise_data/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  enterprise_data/api/urls.py,sha256=POqc_KATHdnpMf9zHtpO46pKD5KAlAExtx7G6iylLcU,273
28
28
  enterprise_data/api/v0/__init__.py,sha256=1aAzAYU5hk-RW6cKUxa1645cbZMxn7GIZ7OMjWc9MKI,46
@@ -30,17 +30,19 @@ 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=qkHjQcqMSsJKBo2C5NpQTm1Nmrh0VBisGbosAp-w71E,10593
34
- enterprise_data/api/v1/urls.py,sha256=vPBZ7p9hsj-lMowqbp29sd2hKhHtICCMVal6th4eQxg,3995
33
+ enterprise_data/api/v1/serializers.py,sha256=oS09fMJyb3DkSc2y5TH5Yknd9NjYrlmWMRPNqBZ741U,10902
34
+ enterprise_data/api/v1/urls.py,sha256=IpOyS9UWuyip6fw6gtrxBw9SZLOdxh3sQ2j0Gdk4eOw,4180
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=4jg2I50TNn2Q6InLCI9eS1MBiFo4SMRsTx4kZJtNmpI,6268
37
37
  enterprise_data/api/v1/views/analytics_engagements.py,sha256=W9DNalWIgoeo_KXqapDj2_ireRyXU47nKHw95sAEtxM,6306
38
38
  enterprise_data/api/v1/views/analytics_enrollments.py,sha256=HXgoFwvXk-CZcf6qbVOfYSCnkfDxGvfHxl0j3RcKz7A,6290
39
39
  enterprise_data/api/v1/views/analytics_leaderboard.py,sha256=zwd-2A2sctjAdx5BUjCqiwcZ5aNvM8wfm386pR-RwGQ,4114
40
40
  enterprise_data/api/v1/views/base.py,sha256=Kkmd5zgEBAhvwS_GoGXSK6lgbDNwSPioYn-QbnizI3w,3416
41
- enterprise_data/api/v1/views/enterprise_admin.py,sha256=DsR1oHFhe6LCIFjIJ4YLLZ7PUChvNlFfdZD-sxHoijY,7388
42
- enterprise_data/api/v1/views/enterprise_learner.py,sha256=NRo22ZvRBZ8nFvvOr6Tp1ZtMnjx55c9WcIdf2VoBYq4,18021
41
+ enterprise_data/api/v1/views/enterprise_admin.py,sha256=hAZR7dh8MIFMibs1rw0mPcdj_DyTjTpa0OqmrGPNdoM,8205
42
+ enterprise_data/api/v1/views/enterprise_learner.py,sha256=NqI_Tlz5v3p4fYZe2RRCg54AizslZdHB3Ckh8YQrhIM,18163
43
43
  enterprise_data/api/v1/views/enterprise_offers.py,sha256=VifxgqTLFLVw4extYPlHcN1N_yjXcsYsAlYEnAbpb10,1266
44
+ enterprise_data/cache/__init__.py,sha256=fiBUploll1kmDy2vCmnNpeZVTD4ewsgtRF14vVs0Rb4,1850
45
+ enterprise_data/cache/decorators.py,sha256=vLbXK9VSv-HzVkkXS1-TkuSMxudwyxz04WFsAXqmjuM,1273
44
46
  enterprise_data/fixtures/enterprise_enrollment.json,sha256=6onPXXR29pMdTdbl_mn81sDi3Re5jkLUZz2TPMB_1IY,5786
45
47
  enterprise_data/fixtures/enterprise_user.json,sha256=6g8GvNY9j_fh1dvAU80bTAMI2F5vXCkb8a4UjsftMvQ,1970
46
48
  enterprise_data/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -126,7 +128,7 @@ enterprise_data/tests/api/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
126
128
  enterprise_data/tests/api/v1/test_serializers.py,sha256=DwgEHcyOP3oqNUPB2O-NkJGeO_cYs9XJiq7791vJLZE,3682
127
129
  enterprise_data/tests/api/v1/test_views.py,sha256=rLqUHfar0HdBNtz33hQxd_0qUUgr7Ku3KwQSQ1B4Ypg,15213
128
130
  enterprise_data/tests/api/v1/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
129
- enterprise_data/tests/api/v1/views/test_enterprise_admin.py,sha256=ysdxVJU-beWQ7eCCTvnKR4br8VFz2lPg7w64z9mrado,9495
131
+ enterprise_data/tests/api/v1/views/test_enterprise_admin.py,sha256=OxeCL_-wotTOoe7cQxJTFDNataBRn5y8ei6GM5-EklA,11052
130
132
  enterprise_data_roles/__init__.py,sha256=toCpbypm2uDoWVw29_em9gPFNly8vNUS__C0b4TCqEg,112
131
133
  enterprise_data_roles/admin.py,sha256=QNP0VeWE092vZzpyxOA5UJK1nNGl5e71B1J0RCwo_nU,998
132
134
  enterprise_data_roles/apps.py,sha256=nKi8TyuQ5Q6WGtKs5QeXvUTc3N-YQjKhyBnm2EM3Bng,260
@@ -167,8 +169,8 @@ enterprise_reporting/tests/test_send_enterprise_reports.py,sha256=WtL-RqGgu2x5PP
167
169
  enterprise_reporting/tests/test_utils.py,sha256=Zt_TA0LVb-B6fQGkUkAKKVlUKKnQh8jnw1US1jKe7g8,9493
168
170
  enterprise_reporting/tests/test_vertica_client.py,sha256=-R2yNCGUjRtoXwLMBloVFQkFYrJoo613VCr61gwI3kQ,140
169
171
  enterprise_reporting/tests/utils.py,sha256=xms2LM7DV3wczXEfctOK1ddel1EE0J_YSr17UzbCDy4,1401
170
- edx_enterprise_data-9.5.2.dist-info/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
171
- edx_enterprise_data-9.5.2.dist-info/METADATA,sha256=BZpBwOwl3dqqKaiEUMya3H3VAeuVf6fNpSmyY0x2_Tk,1569
172
- edx_enterprise_data-9.5.2.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
173
- edx_enterprise_data-9.5.2.dist-info/top_level.txt,sha256=f5F2kU-dob6MqiHJpgZkFzoCD5VMhsdpkTV5n9Tvq3I,59
174
- edx_enterprise_data-9.5.2.dist-info/RECORD,,
172
+ edx_enterprise_data-9.7.0.dist-info/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
173
+ edx_enterprise_data-9.7.0.dist-info/METADATA,sha256=bDigs0YXxVqQLa6h2E7tuDnZ-WVtg6VkvlEt6i5dWsk,1569
174
+ edx_enterprise_data-9.7.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
175
+ edx_enterprise_data-9.7.0.dist-info/top_level.txt,sha256=f5F2kU-dob6MqiHJpgZkFzoCD5VMhsdpkTV5n9Tvq3I,59
176
+ edx_enterprise_data-9.7.0.dist-info/RECORD,,
@@ -2,4 +2,4 @@
2
2
  Enterprise data api application. This Django app exposes API endpoints used by enterprises.
3
3
  """
4
4
 
5
- __version__ = "9.5.2"
5
+ __version__ = "9.7.0"
@@ -4,6 +4,8 @@ Module for interacting with the fact_enrollment_engagement_day_admin_dash table.
4
4
  from datetime import date
5
5
  from uuid import UUID
6
6
 
7
+ from enterprise_data.cache.decorators import cache_it
8
+
7
9
  from ..queries import FactEngagementAdminDashQueries
8
10
  from ..utils import run_query
9
11
  from .base import BaseTable
@@ -15,6 +17,7 @@ class FactEngagementAdminDashTable(BaseTable):
15
17
  """
16
18
  queries = FactEngagementAdminDashQueries()
17
19
 
20
+ @cache_it()
18
21
  def get_learning_hours_and_daily_sessions(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
19
22
  """
20
23
  Get the learning hours and daily sessions for the given enterprise customer.
@@ -40,6 +43,7 @@ class FactEngagementAdminDashTable(BaseTable):
40
43
 
41
44
  return tuple(results[0])
42
45
 
46
+ @cache_it()
43
47
  def get_engagement_count(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
44
48
  """
45
49
  Get the total number of engagements for the given enterprise customer.
@@ -64,6 +68,7 @@ class FactEngagementAdminDashTable(BaseTable):
64
68
  return 0
65
69
  return results[0][0]
66
70
 
71
+ @cache_it()
67
72
  def get_all_engagements(
68
73
  self, enterprise_customer_uuid: UUID, start_date: date, end_date: date, limit: int, offset: int
69
74
  ):
@@ -92,6 +97,7 @@ class FactEngagementAdminDashTable(BaseTable):
92
97
  as_dict=True,
93
98
  )
94
99
 
100
+ @cache_it()
95
101
  def get_top_courses_by_engagement(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
96
102
  """
97
103
  Get the top courses by user engagement for the given enterprise customer.
@@ -114,6 +120,7 @@ class FactEngagementAdminDashTable(BaseTable):
114
120
  as_dict=True,
115
121
  )
116
122
 
123
+ @cache_it()
117
124
  def get_top_subjects_by_engagement(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
118
125
  """
119
126
  Get the top subjects by user engagement for the given enterprise customer.
@@ -136,6 +143,7 @@ class FactEngagementAdminDashTable(BaseTable):
136
143
  as_dict=True,
137
144
  )
138
145
 
146
+ @cache_it()
139
147
  def get_engagement_time_series_data(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
140
148
  """
141
149
  Get the engagement time series data.
@@ -158,6 +166,7 @@ class FactEngagementAdminDashTable(BaseTable):
158
166
  as_dict=True,
159
167
  )
160
168
 
169
+ @cache_it()
161
170
  def _get_engagement_data_for_leaderboard(
162
171
  self, enterprise_customer_uuid: UUID, start_date: date, end_date: date, limit: int, offset: int
163
172
  ):
@@ -189,6 +198,7 @@ class FactEngagementAdminDashTable(BaseTable):
189
198
  as_dict=True,
190
199
  )
191
200
 
201
+ @cache_it()
192
202
  def _get_completion_data_for_leaderboard_query(
193
203
  self, enterprise_customer_uuid: UUID, start_date: date, end_date: date, email_list: list
194
204
  ):
@@ -257,6 +267,7 @@ class FactEngagementAdminDashTable(BaseTable):
257
267
 
258
268
  return list(engagement_data_dict.values())
259
269
 
270
+ @cache_it()
260
271
  def get_leaderboard_data_count(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
261
272
  """
262
273
  Get the total number of leaderboard records for the given enterprise customer.
@@ -4,6 +4,8 @@ Module for interacting with the fact_enrollment_admin_dash table.
4
4
  from datetime import date, datetime
5
5
  from uuid import UUID
6
6
 
7
+ from enterprise_data.cache.decorators import cache_it
8
+
7
9
  from ..queries import FactEnrollmentAdminDashQueries
8
10
  from ..utils import run_query
9
11
  from .base import BaseTable
@@ -15,6 +17,7 @@ class FactEnrollmentAdminDashTable(BaseTable):
15
17
  """
16
18
  queries = FactEnrollmentAdminDashQueries()
17
19
 
20
+ @cache_it()
18
21
  def get_enrollment_count(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
19
22
  """
20
23
  Get the total number of enrollments for the given enterprise customer.
@@ -39,6 +42,7 @@ class FactEnrollmentAdminDashTable(BaseTable):
39
42
  return 0
40
43
  return int(results[0][0] or 0)
41
44
 
45
+ @cache_it()
42
46
  def get_all_enrollments(
43
47
  self, enterprise_customer_uuid: UUID, start_date: date, end_date: date, limit: int, offset: int
44
48
  ):
@@ -67,6 +71,7 @@ class FactEnrollmentAdminDashTable(BaseTable):
67
71
  as_dict=True,
68
72
  )
69
73
 
74
+ @cache_it()
70
75
  def get_enrollment_date_range(self, enterprise_customer_uuid: UUID):
71
76
  """
72
77
  Get the enrollment date range for the given enterprise customer.
@@ -94,6 +99,7 @@ class FactEnrollmentAdminDashTable(BaseTable):
94
99
 
95
100
  return min_date, max_date
96
101
 
102
+ @cache_it()
97
103
  def get_enrollment_and_course_count(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
98
104
  """
99
105
  Get the enrollment and course count for the given enterprise customer.
@@ -118,6 +124,7 @@ class FactEnrollmentAdminDashTable(BaseTable):
118
124
  return 0, 0
119
125
  return tuple(results[0])
120
126
 
127
+ @cache_it()
121
128
  def get_completion_count(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
122
129
  """
123
130
  Get the completion count for the given enterprise customer.
@@ -143,6 +150,7 @@ class FactEnrollmentAdminDashTable(BaseTable):
143
150
 
144
151
  return int(results[0][0] or 0)
145
152
 
153
+ @cache_it()
146
154
  def get_top_courses_by_enrollments(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
147
155
  """
148
156
  Get the top courses enrollments for the given enterprise customer.
@@ -165,6 +173,7 @@ class FactEnrollmentAdminDashTable(BaseTable):
165
173
  as_dict=True,
166
174
  )
167
175
 
176
+ @cache_it()
168
177
  def get_top_subjects_by_enrollments(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
169
178
  """
170
179
  Get the top subjects by enrollments for the given enterprise customer.
@@ -187,6 +196,7 @@ class FactEnrollmentAdminDashTable(BaseTable):
187
196
  as_dict=True,
188
197
  )
189
198
 
199
+ @cache_it()
190
200
  def get_enrolment_time_series_data(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
191
201
  """
192
202
  Get the enrollment time series data for the given enterprise customer.
@@ -209,6 +219,7 @@ class FactEnrollmentAdminDashTable(BaseTable):
209
219
  as_dict=True,
210
220
  )
211
221
 
222
+ @cache_it()
212
223
  def get_all_completions(
213
224
  self, enterprise_customer_uuid: UUID, start_date: date, end_date: date, limit: int, offset: int
214
225
  ):
@@ -237,6 +248,7 @@ class FactEnrollmentAdminDashTable(BaseTable):
237
248
  as_dict=True,
238
249
  )
239
250
 
251
+ @cache_it()
240
252
  def get_top_courses_by_completions(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
241
253
  """
242
254
  Get the top courses by completion for the given enterprise customer.
@@ -259,6 +271,7 @@ class FactEnrollmentAdminDashTable(BaseTable):
259
271
  as_dict=True,
260
272
  )
261
273
 
274
+ @cache_it()
262
275
  def get_top_subjects_by_completions(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
263
276
  """
264
277
  Get the top subjects by completions for the given enterprise customer.
@@ -281,6 +294,7 @@ class FactEnrollmentAdminDashTable(BaseTable):
281
294
  as_dict=True,
282
295
  )
283
296
 
297
+ @cache_it()
284
298
  def get_completions_time_series_data(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
285
299
  """
286
300
  Get the completions time series data for the given enterprise customer.
@@ -9,6 +9,7 @@ from enterprise_data.admin_analytics.database.queries.skills_daily_rollup_admin_
9
9
  )
10
10
  from enterprise_data.admin_analytics.database.tables.base import BaseTable
11
11
  from enterprise_data.admin_analytics.database.utils import run_query
12
+ from enterprise_data.cache.decorators import cache_it
12
13
 
13
14
 
14
15
  class SkillsDailyRollupAdminDashTable(BaseTable):
@@ -17,6 +18,7 @@ class SkillsDailyRollupAdminDashTable(BaseTable):
17
18
  """
18
19
  queries = SkillsDailyRollupAdminDashQueries()
19
20
 
21
+ @cache_it()
20
22
  def get_top_skills(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
21
23
  """
22
24
  Get the top skills for the given enterprise customer.
@@ -39,6 +41,7 @@ class SkillsDailyRollupAdminDashTable(BaseTable):
39
41
  as_dict=True,
40
42
  )
41
43
 
44
+ @cache_it()
42
45
  def get_top_skills_by_enrollment(
43
46
  self,
44
47
  enterprise_customer_uuid: UUID,
@@ -66,6 +69,7 @@ class SkillsDailyRollupAdminDashTable(BaseTable):
66
69
  as_dict=True,
67
70
  )
68
71
 
72
+ @cache_it()
69
73
  def get_top_skills_by_completion(
70
74
  self,
71
75
  enterprise_customer_uuid: UUID,
@@ -240,6 +240,19 @@ class EnterpriseExecEdLCModulePerformanceSerializer(serializers.ModelSerializer)
240
240
  return obj.extensions_requested if obj.extensions_requested is not None else 0
241
241
 
242
242
 
243
+ class EnterpriseBudgetSerializer(serializers.ModelSerializer):
244
+ """
245
+ Serializer for EnterpriseSubsidyBudget model.
246
+ """
247
+
248
+ class Meta:
249
+ model = EnterpriseSubsidyBudget
250
+ fields = (
251
+ 'subsidy_access_policy_uuid',
252
+ 'subsidy_access_policy_display_name',
253
+ )
254
+
255
+
243
256
  class AdvanceAnalyticsQueryParamSerializer(serializers.Serializer): # pylint: disable=abstract-method
244
257
  """Serializer for validating query params"""
245
258
  RESPONSE_TYPES = [
@@ -96,6 +96,11 @@ urlpatterns = [
96
96
  enterprise_admin_views.EnterpriseAdminAnalyticsSkillsView.as_view(),
97
97
  name='enterprise-admin-analytics-skills'
98
98
  ),
99
+ re_path(
100
+ fr'^enterprise/(?P<enterprise_uuid>{UUID4_REGEX})/budgets',
101
+ enterprise_admin_views.EnterpriseBudgetView.as_view(),
102
+ name='enterprise-budgets'
103
+ ),
99
104
  ]
100
105
 
101
106
  urlpatterns += router.urls
@@ -21,6 +21,7 @@ from enterprise_data.models import (
21
21
  EnterpriseAdminLearnerProgress,
22
22
  EnterpriseAdminSummarizeInsights,
23
23
  EnterpriseExecEdLCModulePerformance,
24
+ EnterpriseSubsidyBudget,
24
25
  )
25
26
  from enterprise_data.utils import timer
26
27
 
@@ -206,3 +207,26 @@ class EnterpriseExecEdLCModulePerformanceViewSet(EnterpriseViewSetMixin, viewset
206
207
  return EnterpriseExecEdLCModulePerformance.objects.filter(
207
208
  enterprise_customer_uuid=self.kwargs['enterprise_id'],
208
209
  )
210
+
211
+
212
+ class EnterpriseBudgetView(APIView):
213
+ """
214
+ View for getting budgets information for an enterprise.
215
+ """
216
+ authentication_classes = (JwtAuthentication,)
217
+ http_method_names = ["get"]
218
+
219
+ @permission_required("can_access_enterprise", fn=lambda request, enterprise_uuid: enterprise_uuid)
220
+ def get(self, request, enterprise_uuid):
221
+ """
222
+ Return the queryset of EnterpriseSubsidyBudget objects.
223
+ """
224
+ budgets = EnterpriseSubsidyBudget.objects.filter(
225
+ enterprise_customer_uuid=enterprise_uuid,
226
+ ).values(
227
+ 'subsidy_access_policy_uuid',
228
+ 'subsidy_access_policy_display_name',
229
+ )
230
+
231
+ serializer = serializers.EnterpriseBudgetSerializer(budgets, many=True)
232
+ return Response(serializer.data)
@@ -150,6 +150,10 @@ class EnterpriseLearnerEnrollmentViewSet(EnterpriseViewSetMixin, viewsets.ReadOn
150
150
  if search_start_date:
151
151
  queryset = queryset.filter(course_start_date=search_start_date)
152
152
 
153
+ budget_uuid = query_filters.get('budget_uuid')
154
+ if budget_uuid:
155
+ queryset = queryset.filter(budget_id=budget_uuid)
156
+
153
157
  offer_id = query_filters.get('offer_id')
154
158
  if offer_id:
155
159
  queryset = self.filter_by_offer_id(queryset, offer_id)
@@ -0,0 +1,59 @@
1
+ """
2
+ Caching related utility classes and functions.
3
+ """
4
+ import hashlib
5
+
6
+ from edx_django_utils.cache import TieredCache
7
+
8
+ DEFAULT_TIMEOUT = 60 * 60 # 1 hour
9
+
10
+
11
+ def get_key(*args, **kwargs):
12
+ """
13
+ Get MD5 encoded cache key for given positional and keyword arguments.
14
+
15
+ MD5 encrytion is applied to a key that is generated by concatenating the positional and keyword arguments.
16
+ Following is the format of the generated key from arguments before applying the MD5 encryption.
17
+ arg1__arg2__key1:value1__key2:value2 ...
18
+
19
+ Example:
20
+ >>> get_key('ecommerce', site_domain='example.com', resource='catalogs')
21
+ 1892cd85a30b8fc9180369c17b472c38
22
+ >>> # The generated key for the above call before applying MD5 encryption will be as follows
23
+ >>> # "ecommerce__site_domain:example.com__resource:catalogs"
24
+
25
+ Arguments:
26
+ *args: Arguments that need to be present in cache key.
27
+ **kwargs: Key word arguments that need to be present in cache key.
28
+
29
+ Returns:
30
+ (str): An MD5 encoded key uniquely identified by the key word arguments.
31
+ """
32
+ key = '{}__{}'.format(
33
+ '__'.join(map(str, args)),
34
+ '__'.join(['{}:{}'.format(item, str(value)) for item, value in kwargs.items()])
35
+ )
36
+
37
+ return hashlib.md5(key.encode('utf-8')).hexdigest()
38
+
39
+
40
+ def get(key):
41
+ """
42
+ Get value from cache for given key.
43
+
44
+ Returns:
45
+ (CachedResponse): CachedResponse object.
46
+ """
47
+ return TieredCache.get_cached_response(key)
48
+
49
+
50
+ def set(key, value, timeout=DEFAULT_TIMEOUT): # pylint: disable=redefined-builtin
51
+ """
52
+ Set value in cache for given key.
53
+
54
+ Arguments:
55
+ key (str): Cache key.
56
+ value (object): Value to be stored in cache.
57
+ timeout (int): Cache timeout in seconds.
58
+ """
59
+ TieredCache.set_all_tiers(key, value, django_cache_timeout=timeout)
@@ -0,0 +1,42 @@
1
+ """
2
+ Decorators for caching the result of a function.
3
+ """
4
+ from functools import wraps
5
+ from logging import getLogger
6
+
7
+ from enterprise_data import cache
8
+
9
+ LOGGER = getLogger(__name__)
10
+
11
+
12
+ def cache_it(timeout=cache.DEFAULT_TIMEOUT):
13
+ """
14
+ Function to return the decorator to cache the result of a method.
15
+
16
+ Note: This decorator will only work for class methods.
17
+
18
+ Arguments:
19
+ timeout (int): Cache timeout in seconds.
20
+
21
+ Returns:
22
+ (function): Decorator function.
23
+ """
24
+
25
+ def inner_decorator(func):
26
+ @wraps(func)
27
+ def wrapper(self, *args, **kwargs):
28
+ """
29
+ Wrapper function to cache the result of the function.
30
+ """
31
+ cache_key = cache.get_key(func.__name__, *args, **kwargs)
32
+ cached_response = cache.get(cache_key)
33
+ if cached_response.is_found:
34
+ LOGGER.info("[ANALYTICS]: Cache hit for key: (%s)", (func.__name__, args, kwargs))
35
+ return cached_response.value
36
+
37
+ LOGGER.info("[ANALYTICS]: Cache miss for key: (%s)", (func.__name__, args, kwargs))
38
+ result = func(self, *args, **kwargs)
39
+ cache.set(cache_key, result, timeout=timeout)
40
+ return result
41
+ return wrapper
42
+ return inner_decorator
@@ -21,7 +21,7 @@ from enterprise_data.tests.admin_analytics.mock_analytics_data import (
21
21
  TOP_SKILLS_BY_ENROLLMENTS,
22
22
  )
23
23
  from enterprise_data.tests.mixins import JWTTestMixin
24
- from enterprise_data.tests.test_utils import UserFactory, get_dummy_enterprise_api_data
24
+ from enterprise_data.tests.test_utils import EnterpriseSubsidyBudgetFactory, UserFactory, get_dummy_enterprise_api_data
25
25
  from enterprise_data_roles.constants import ENTERPRISE_DATA_ADMIN_ROLE
26
26
  from enterprise_data_roles.models import EnterpriseDataFeatureRole, EnterpriseDataRoleAssignment
27
27
 
@@ -245,3 +245,49 @@ class TestSkillsStatsAPI(JWTTestMixin, APITransactionTestCase):
245
245
  response = self.client.get(self.url, params)
246
246
  assert response.status_code == status.HTTP_400_BAD_REQUEST
247
247
  assert response.json() == error
248
+
249
+
250
+ @ddt.ddt
251
+ class TestEnterpriseBudgetAPI(JWTTestMixin, APITransactionTestCase):
252
+ """Tests for EnterpriseBudgetView."""
253
+
254
+ def setUp(self):
255
+ """
256
+ Setup method.
257
+ """
258
+ super().setUp()
259
+ self.user = UserFactory(is_staff=True)
260
+ role, __ = EnterpriseDataFeatureRole.objects.get_or_create(
261
+ name=ENTERPRISE_DATA_ADMIN_ROLE
262
+ )
263
+ self.role_assignment = EnterpriseDataRoleAssignment.objects.create(
264
+ role=role, user=self.user
265
+ )
266
+ self.client.force_authenticate(user=self.user)
267
+
268
+ self.enterprise_uuid = "ee5e6b3a069a4947bb8dd2dbc323396c"
269
+ self.set_jwt_cookie()
270
+
271
+ self.url = reverse(
272
+ "v1:enterprise-budgets",
273
+ kwargs={"enterprise_uuid": self.enterprise_uuid},
274
+ )
275
+
276
+ self.enterprise_subsidy_budget = EnterpriseSubsidyBudgetFactory(
277
+ enterprise_customer_uuid=self.enterprise_uuid,
278
+ subsidy_access_policy_uuid='8d6503dd-e40d-42b8-442b-37dd4c5450e3',
279
+ subsidy_access_policy_display_name='test-budget'
280
+ )
281
+
282
+ def test_get(self):
283
+ """
284
+ Test the GET method for the EnterpriseBudgetView works.
285
+ """
286
+ response = self.client.get(self.url)
287
+ assert response.status_code == status.HTTP_200_OK
288
+ assert response.json() == [
289
+ {
290
+ 'subsidy_access_policy_uuid': '8d6503dd-e40d-42b8-442b-37dd4c5450e3',
291
+ 'subsidy_access_policy_display_name': 'test-budget',
292
+ }
293
+ ]