edx-enterprise-data 8.11.1__tar.gz → 8.12.0__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 (202) hide show
  1. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/CHANGELOG.rst +4 -0
  2. {edx_enterprise_data-8.11.1/edx_enterprise_data.egg-info → edx_enterprise_data-8.12.0}/PKG-INFO +1 -1
  3. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0/edx_enterprise_data.egg-info}/PKG-INFO +1 -1
  4. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/__init__.py +1 -1
  5. edx_enterprise_data-8.12.0/enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py +139 -0
  6. edx_enterprise_data-8.12.0/enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py +201 -0
  7. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/database/utils.py +8 -3
  8. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/serializers.py +2 -0
  9. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/urls.py +3 -6
  10. edx_enterprise_data-8.12.0/enterprise_data/api/v1/views/analytics_enrollments.py +146 -0
  11. edx_enterprise_data-8.12.0/enterprise_data/api/v1/views/base.py +105 -0
  12. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/mock_analytics_data.py +0 -12
  13. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/test_analytics_enrollments.py +72 -115
  14. edx_enterprise_data-8.11.1/enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py +0 -61
  15. edx_enterprise_data-8.11.1/enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py +0 -85
  16. edx_enterprise_data-8.11.1/enterprise_data/api/v1/views/analytics_enrollments.py +0 -388
  17. edx_enterprise_data-8.11.1/enterprise_data/api/v1/views/base.py +0 -26
  18. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/LICENSE +0 -0
  19. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/MANIFEST.in +0 -0
  20. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/README.md +0 -0
  21. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/edx_enterprise_data.egg-info/SOURCES.txt +0 -0
  22. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/edx_enterprise_data.egg-info/dependency_links.txt +0 -0
  23. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/edx_enterprise_data.egg-info/not-zip-safe +0 -0
  24. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/edx_enterprise_data.egg-info/requires.txt +0 -0
  25. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/edx_enterprise_data.egg-info/top_level.txt +0 -0
  26. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/__init__.py +0 -0
  27. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/completions_utils.py +0 -0
  28. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/constants.py +0 -0
  29. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/data_loaders.py +0 -0
  30. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/database/__init__.py +0 -0
  31. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/database/queries/__init__.py +0 -0
  32. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py +0 -0
  33. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/database/tables/__init__.py +0 -0
  34. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/database/tables/base.py +0 -0
  35. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py +0 -0
  36. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/utils.py +0 -0
  37. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/api/__init__.py +0 -0
  38. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/api/urls.py +0 -0
  39. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/api/v0/__init__.py +0 -0
  40. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/api/v0/serializers.py +0 -0
  41. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/api/v0/urls.py +0 -0
  42. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/api/v0/views.py +0 -0
  43. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/__init__.py +0 -0
  44. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/paginators.py +0 -0
  45. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/views/__init__.py +0 -0
  46. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/views/analytics_engagements.py +0 -0
  47. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/views/analytics_leaderboard.py +0 -0
  48. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/views/enterprise_admin.py +0 -0
  49. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/views/enterprise_completions.py +0 -0
  50. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/views/enterprise_learner.py +0 -0
  51. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/views/enterprise_offers.py +0 -0
  52. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/apps.py +0 -0
  53. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/clients.py +0 -0
  54. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/constants.py +0 -0
  55. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/filters.py +0 -0
  56. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/fixtures/enterprise_enrollment.json +0 -0
  57. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/fixtures/enterprise_user.json +0 -0
  58. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/management/__init__.py +0 -0
  59. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/__init__.py +0 -0
  60. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/create_dummy_data.py +0 -0
  61. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/create_dummy_data_lpr_v1.py +0 -0
  62. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/create_enterprise_enrollment.py +0 -0
  63. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/create_enterprise_learner_enrollment_lpr_v1.py +0 -0
  64. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/create_enterprise_learner_lpr_v1.py +0 -0
  65. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/create_enterprise_offer.py +0 -0
  66. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/create_enterprise_user.py +0 -0
  67. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/tests/__init__.py +0 -0
  68. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/tests/test_create_dummy_data_lpr_v1.py +0 -0
  69. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/tests/test_create_enterprise_enrollment.py +0 -0
  70. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/tests/test_create_enterprise_learner_enrollment_lpr_v1.py +0 -0
  71. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/tests/test_create_enterprise_learner_lpr_v1.py +0 -0
  72. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/tests/test_create_enterprise_user.py +0 -0
  73. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0001_initial.py +0 -0
  74. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0002_auto_20180430_1358.py +0 -0
  75. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0003_auto_20180501_0603.py +0 -0
  76. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0004_auto_20180501_0928.py +0 -0
  77. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0004_auto_20180508_1652.py +0 -0
  78. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0005_auto_20180524_2204.py +0 -0
  79. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0006_auto_20180612_0336.py +0 -0
  80. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0007_auto_20180612_0534.py +0 -0
  81. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0008_auto_20180614_0108.py +0 -0
  82. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0009_auto_20180628_1152.py +0 -0
  83. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0010_enterpriseenrollment_created.py +0 -0
  84. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0011_enterpriseuser.py +0 -0
  85. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0012_auto_20180831_1930.py +0 -0
  86. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0013_auto_20180831_1931.py +0 -0
  87. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0014_enterpriseuser_created.py +0 -0
  88. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0015_auto_20180907_1757.py +0 -0
  89. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0016_auto_20180924_2138.py +0 -0
  90. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0017_enterpriseenrollment_unenrollment_timestamp.py +0 -0
  91. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0018_enterprisedatafeaturerole_enterprisedataroleassignment.py +0 -0
  92. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0019_add_enterprise_data_feature_roles.py +0 -0
  93. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0020_add_role_based_access_control_switch.py +0 -0
  94. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0021_auto_20190329_1241.py +0 -0
  95. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0022_remove_role_based_access_control_switch.py +0 -0
  96. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0023_enterpriselearner_enterpriselearnerenrollment.py +0 -0
  97. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0024_auto_20210602_1811.py +0 -0
  98. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0025_auto_20210703_1854.py +0 -0
  99. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0026_auto_20210916_0414.py +0 -0
  100. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0027_enterpriselearnerenrollment_total_learning_time_seconds.py +0 -0
  101. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0028_enterpriselearnerenrollment_offer_id.py +0 -0
  102. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0029_enterpriseoffer.py +0 -0
  103. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0030_auto_20230609_1353.py +0 -0
  104. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0031_auto_20230615_0705.py +0 -0
  105. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0032_auto_20230704_0818.py +0 -0
  106. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0033_enterpriseadminlearnerprogress_enterpriseadminsummarizeinsights.py +0 -0
  107. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0034_auto_20230907_0834.py +0 -0
  108. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0035_auto_20230907_1154.py +0 -0
  109. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0036_enterprisesubsidybudget_subsidy_access_policy_display_name.py +0 -0
  110. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0037_alter_enterpriseenrollment_consent_granted.py +0 -0
  111. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0038_enterpriseoffer_export_timestamp.py +0 -0
  112. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0039_auto_20240212_1403.py +0 -0
  113. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0040_auto_20240718_0536_squashed_0043_alter_enterpriselearnerenrollment_enterprise_enrollment_id.py +0 -0
  114. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0044_enterpriseexecedlcmoduleperformance.py +0 -0
  115. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/__init__.py +0 -0
  116. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/models.py +0 -0
  117. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/paginators.py +0 -0
  118. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/renderers.py +0 -0
  119. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/settings/__init__.py +0 -0
  120. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/settings/test.py +0 -0
  121. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/signals.py +0 -0
  122. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/__init__.py +0 -0
  123. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/__init__.py +0 -0
  124. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/mock_enrollments.py +0 -0
  125. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/test_analytics_engagements.py +0 -0
  126. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/test_analytics_leaderboard.py +0 -0
  127. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/test_data_loaders.py +0 -0
  128. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/test_enterprise_completions.py +0 -0
  129. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/test_utils.py +0 -0
  130. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/api/__init__.py +0 -0
  131. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/api/v0/__init__.py +0 -0
  132. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/api/v0/test_serializers.py +0 -0
  133. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/api/v1/__init__.py +0 -0
  134. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/api/v1/test_serializers.py +0 -0
  135. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/api/v1/test_views.py +0 -0
  136. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/api/v1/views/__init__.py +0 -0
  137. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/api/v1/views/test_enterprise_admin.py +0 -0
  138. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/factories.py +0 -0
  139. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/mixins.py +0 -0
  140. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/test_clients.py +0 -0
  141. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/test_filters.py +0 -0
  142. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/test_models.py +0 -0
  143. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/test_utils.py +0 -0
  144. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/tests/test_views.py +0 -0
  145. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/urls.py +0 -0
  146. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data/utils.py +0 -0
  147. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data_roles/__init__.py +0 -0
  148. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data_roles/admin.py +0 -0
  149. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data_roles/apps.py +0 -0
  150. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data_roles/constants.py +0 -0
  151. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data_roles/migrations/0001_initial.py +0 -0
  152. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data_roles/migrations/0002_add_enterprise_data_feature_roles.py +0 -0
  153. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data_roles/migrations/0003_add_role_based_access_control_switch.py +0 -0
  154. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data_roles/migrations/0004_enterprisedataroleassignment_enterprise_id.py +0 -0
  155. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data_roles/migrations/0005_turn_on_role_based_access_control_switch.py +0 -0
  156. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data_roles/migrations/0006_remove_role_based_access_control_switch.py +0 -0
  157. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data_roles/migrations/0007_enterprisedataroleassignment_applies_to_all_contexts.py +0 -0
  158. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data_roles/migrations/__init__.py +0 -0
  159. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data_roles/models.py +0 -0
  160. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data_roles/rules.py +0 -0
  161. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data_roles/tests/__init__.py +0 -0
  162. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data_roles/tests/factories.py +0 -0
  163. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_data_roles/tests/test_models.py +0 -0
  164. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/__init__.py +0 -0
  165. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/clients/__init__.py +0 -0
  166. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/clients/enterprise.py +0 -0
  167. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/clients/s3.py +0 -0
  168. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/clients/snowflake.py +0 -0
  169. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/clients/vertica.py +0 -0
  170. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/delivery_method.py +0 -0
  171. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/external_resource_link_report.py +0 -0
  172. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/fixtures/__init__.py +0 -0
  173. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/fixtures/enterprise_customer_reporting.json +0 -0
  174. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/reporter.py +0 -0
  175. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/send_enterprise_reports.py +0 -0
  176. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/__init__.py +0 -0
  177. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/test_clients.py +0 -0
  178. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/test_delivery_method.py +0 -0
  179. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/test_enterprise_client.py +0 -0
  180. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/test_external_link_report.py +0 -0
  181. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/test_reporter.py +0 -0
  182. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/test_send_enterprise_reports.py +0 -0
  183. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/test_utils.py +0 -0
  184. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/test_vertica_client.py +0 -0
  185. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/utils.py +0 -0
  186. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/enterprise_reporting/utils.py +0 -0
  187. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/requirements/base.in +0 -0
  188. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/requirements/base.txt +0 -0
  189. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/requirements/ci.txt +0 -0
  190. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/requirements/common_constraints.txt +0 -0
  191. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/requirements/constraints.txt +0 -0
  192. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/requirements/dev.txt +0 -0
  193. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/requirements/django.txt +0 -0
  194. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/requirements/pip.txt +0 -0
  195. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/requirements/pip_tools.txt +0 -0
  196. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/requirements/quality.txt +0 -0
  197. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/requirements/reporting.in +0 -0
  198. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/requirements/test-master.txt +0 -0
  199. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/requirements/test-reporting.txt +0 -0
  200. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/requirements/test.txt +0 -0
  201. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/setup.cfg +0 -0
  202. {edx_enterprise_data-8.11.1 → edx_enterprise_data-8.12.0}/setup.py +0 -0
@@ -15,6 +15,10 @@ Unreleased
15
15
  ----------
16
16
 
17
17
  =========================
18
+ [8.12.0] - 2024-09-06
19
+ ---------------------
20
+ * refactor: Performance optimizations for enrollments related API endpoints.
21
+
18
22
  [8.11.1] - 2024-08-29
19
23
  ---------------------
20
24
  * fix: Fixed a datetime conversion error appearing on production.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: edx-enterprise-data
3
- Version: 8.11.1
3
+ Version: 8.12.0
4
4
  Summary: Enterprise Reporting
5
5
  Home-page: https://github.com/openedx/edx-enterprise-data
6
6
  Author: edX
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: edx-enterprise-data
3
- Version: 8.11.1
3
+ Version: 8.12.0
4
4
  Summary: Enterprise Reporting
5
5
  Home-page: https://github.com/openedx/edx-enterprise-data
6
6
  Author: edX
@@ -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__ = "8.11.1"
5
+ __version__ = "8.12.0"
@@ -0,0 +1,139 @@
1
+ """
2
+ Module containing queries for the fact_enrollment_admin_dash table.
3
+ """
4
+
5
+
6
+ class FactEnrollmentAdminDashQueries:
7
+ """
8
+ Queries related to the fact_enrollment_admin_dash table.
9
+ """
10
+ @staticmethod
11
+ def get_enrollment_count_query():
12
+ """
13
+ Get the query to fetch the total number of enrollments for an enterprise customer.
14
+ """
15
+ return """
16
+ SELECT count(*)
17
+ FROM fact_enrollment_admin_dash
18
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
19
+ enterprise_enrollment_date BETWEEN %(start_date)s AND %(end_date)s;
20
+ """
21
+
22
+ @staticmethod
23
+ def get_all_enrollments_query():
24
+ """
25
+ Get the query to fetch all enrollments.
26
+ """
27
+ return """
28
+ SELECT email, course_title, course_subject, enroll_type, enterprise_enrollment_date
29
+ FROM fact_enrollment_admin_dash
30
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
31
+ enterprise_enrollment_date BETWEEN %(start_date)s AND %(end_date)s
32
+ ORDER BY ENTERPRISE_ENROLLMENT_DATE DESC LIMIT %(limit)s OFFSET %(offset)s
33
+ """
34
+
35
+ @staticmethod
36
+ def get_enrollment_date_range_query():
37
+ """
38
+ Get the query to fetch the enrollment date range.
39
+ """
40
+ return """
41
+ SELECT
42
+ MIN(enterprise_enrollment_date) AS min_enrollment_date,
43
+ MAX(enterprise_enrollment_date) AS max_enrollment_date
44
+ FROM fact_enrollment_admin_dash
45
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s;
46
+ """
47
+
48
+ @staticmethod
49
+ def get_enrollment_and_course_count_query():
50
+ """
51
+ Get the query to fetch the enrollment and course count.
52
+ """
53
+ return """
54
+ SELECT
55
+ count(*) as enrolls, count(DISTINCT course_key) as courses
56
+ FROM fact_enrollment_admin_dash
57
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
58
+ enterprise_enrollment_date BETWEEN %(start_date)s AND %(end_date)s;
59
+ """
60
+
61
+ @staticmethod
62
+ def get_completion_count_query():
63
+ """
64
+ Get the query to fetch the completion count.
65
+ """
66
+ return """
67
+ SELECT
68
+ SUM(has_passed) as completions
69
+ FROM fact_enrollment_admin_dash
70
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
71
+ passed_date BETWEEN %(start_date)s AND %(end_date)s;
72
+ """
73
+
74
+ @staticmethod
75
+ def get_learning_hours_and_daily_sessions_query():
76
+ """
77
+ Get the query to fetch the learning hours and daily sessions.
78
+ """
79
+ return """
80
+ SELECT
81
+ ROUND(SUM(learning_time_seconds) / 60 / 60, 1) as hours, SUM(is_engaged) as sessions
82
+ FROM fact_engagement_admin_dash
83
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
84
+ activity_date BETWEEN %(start_date)s AND %(end_date)s;
85
+ """
86
+
87
+ @staticmethod
88
+ def get_top_courses_enrollments_query(record_count=20):
89
+ """
90
+ Get the query to fetch the enrollment count by courses.
91
+
92
+ Query will fetch the top N courses by enrollment count. Where N is the value of record_count.
93
+
94
+ Arguments:
95
+ record_count (int): Number of records to fetch.
96
+ """
97
+ # Some local environments raise error when course_title is added in SELECT without GROUP BY.
98
+ # If you face this issue, you can remove course_title from SELECT.
99
+
100
+ # TODO: Re-add course_title after testing.
101
+ return f"""
102
+ SELECT course_key, enroll_type, count(course_key) as enrollment_count
103
+ FROM fact_enrollment_admin_dash
104
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
105
+ enterprise_enrollment_date BETWEEN %(start_date)s AND %(end_date)s
106
+ GROUP BY course_key, enroll_type ORDER BY enrollment_count DESC LIMIT {record_count};
107
+ """
108
+
109
+ @staticmethod
110
+ def get_top_subjects_by_enrollments_query(record_count=20):
111
+ """
112
+ Get the query to fetch the enrollment count by subjects.
113
+
114
+ Query will fetch the top N subjects by enrollment count. Where N is the value of record_count.
115
+
116
+ Arguments:
117
+ record_count (int): Number of records to fetch.
118
+ """
119
+ return f"""
120
+ SELECT course_subject, enroll_type, count(course_subject) enrollment_count
121
+ FROM fact_enrollment_admin_dash
122
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
123
+ enterprise_enrollment_date BETWEEN %(start_date)s AND %(end_date)s
124
+ GROUP BY course_subject, enroll_type ORDER BY enrollment_count DESC LIMIT {record_count};
125
+ """
126
+
127
+ @staticmethod
128
+ def get_enrolment_time_series_data_query():
129
+ """
130
+ Get the query to fetch the enrollment time series data with daily aggregation.
131
+ """
132
+ return """
133
+ SELECT enterprise_enrollment_date, enroll_type, COUNT(*) as enrollment_count
134
+ FROM fact_enrollment_admin_dash
135
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
136
+ enterprise_enrollment_date BETWEEN %(start_date)s AND %(end_date)s
137
+ GROUP BY enterprise_enrollment_date, enroll_type
138
+ ORDER BY enterprise_enrollment_date;
139
+ """
@@ -0,0 +1,201 @@
1
+ """
2
+ Module for interacting with the fact_enrollment_admin_dash table.
3
+ """
4
+ from datetime import date, datetime
5
+ from uuid import UUID
6
+
7
+ from ..queries import FactEnrollmentAdminDashQueries
8
+ from ..utils import run_query
9
+ from .base import BaseTable
10
+
11
+
12
+ class FactEnrollmentAdminDashTable(BaseTable):
13
+ """
14
+ Class for communicating with the fact_enrollment_admin_dash table.
15
+ """
16
+ queries = FactEnrollmentAdminDashQueries()
17
+
18
+ def get_enrollment_count(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
19
+ """
20
+ Get the total number of enrollments for the given enterprise customer.
21
+
22
+ Arguments:
23
+ enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
24
+ start_date (date): The start date.
25
+ end_date (date): The end date.
26
+
27
+ Returns:
28
+ (int): The total number of enrollments.
29
+ """
30
+ results = run_query(
31
+ query=self.queries.get_enrollment_count_query(),
32
+ params={
33
+ 'enterprise_customer_uuid': enterprise_customer_uuid,
34
+ 'start_date': start_date,
35
+ 'end_date': end_date,
36
+ }
37
+ )
38
+ return results[0][0]
39
+
40
+ def get_all_enrollments(
41
+ self, enterprise_customer_uuid: UUID, start_date: date, end_date: date, limit: int, offset: int
42
+ ):
43
+ """
44
+ Get all enrollments for the given enterprise customer.
45
+
46
+ Arguments:
47
+ enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
48
+ start_date (date): The start date.
49
+ end_date (date): The end date.
50
+ limit (int): The maximum number of records to return.
51
+ offset (int): The number of records to skip.
52
+
53
+ Returns:
54
+ list<dict>: A list of dictionaries containing the enrollment data.
55
+ """
56
+ return run_query(
57
+ query=self.queries.get_all_enrollments_query(),
58
+ params={
59
+ 'enterprise_customer_uuid': enterprise_customer_uuid,
60
+ 'start_date': start_date,
61
+ 'end_date': end_date,
62
+ 'limit': limit,
63
+ 'offset': offset,
64
+ },
65
+ as_dict=True,
66
+ )
67
+
68
+ def get_enrollment_date_range(self, enterprise_customer_uuid: UUID):
69
+ """
70
+ Get the enrollment date range for the given enterprise customer.
71
+
72
+ Arguments:
73
+ enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
74
+
75
+ Returns:
76
+ (tuple<date, date>): The minimum and maximum enrollment dates.
77
+ """
78
+ results = run_query(
79
+ query=self.queries.get_enrollment_date_range_query(),
80
+ params={'enterprise_customer_uuid': enterprise_customer_uuid}
81
+ )
82
+ min_date, max_date = results[0]
83
+
84
+ # We should return date objects, not datetime objects
85
+ # This is done to counter cases where database values are datetime objects.
86
+ if min_date and isinstance(min_date, datetime):
87
+ min_date = min_date.date()
88
+ if max_date and isinstance(max_date, datetime):
89
+ max_date = max_date.date()
90
+
91
+ return min_date, max_date
92
+
93
+ def get_enrollment_and_course_count(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
94
+ """
95
+ Get the enrollment and course count for the given enterprise customer.
96
+
97
+ Arguments:
98
+ enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
99
+ start_date (date): The start date.
100
+ end_date (date): The end date.
101
+
102
+ Returns:
103
+ (tuple<int, int>): The enrollment and course count.
104
+ """
105
+ results = run_query(
106
+ query=self.queries.get_enrollment_and_course_count_query(),
107
+ params={
108
+ 'enterprise_customer_uuid': enterprise_customer_uuid,
109
+ 'start_date': start_date,
110
+ 'end_date': end_date,
111
+ }
112
+ )
113
+ return tuple(results[0])
114
+
115
+ def get_completion_count(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
116
+ """
117
+ Get the completion count for the given enterprise customer.
118
+
119
+ Arguments:
120
+ enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
121
+ start_date (date): The start date.
122
+ end_date (date): The end date.
123
+
124
+ Returns:
125
+ int: The completion count.
126
+ """
127
+ results = run_query(
128
+ query=self.queries.get_completion_count_query(),
129
+ params={
130
+ 'enterprise_customer_uuid': enterprise_customer_uuid,
131
+ 'start_date': start_date,
132
+ 'end_date': end_date,
133
+ }
134
+ )
135
+ return results[0][0]
136
+
137
+ def get_top_courses_by_enrollments(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
138
+ """
139
+ Get the top courses enrollments for the given enterprise customer.
140
+
141
+ Arguments:
142
+ enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
143
+ start_date (date): The start date.
144
+ end_date (date): The end date.
145
+
146
+ Returns:
147
+ list<dict>: A list of dictionaries containing the course key, course_title and enrollment count.
148
+ """
149
+ return run_query(
150
+ query=self.queries.get_top_courses_enrollments_query(),
151
+ params={
152
+ 'enterprise_customer_uuid': enterprise_customer_uuid,
153
+ 'start_date': start_date,
154
+ 'end_date': end_date,
155
+ },
156
+ as_dict=True,
157
+ )
158
+
159
+ def get_top_subjects_by_enrollments(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
160
+ """
161
+ Get the top subjects by enrollments for the given enterprise customer.
162
+
163
+ Arguments:
164
+ enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
165
+ start_date (date): The start date.
166
+ end_date (date): The end date.
167
+
168
+ Returns:
169
+ list<dict>: A list of dictionaries containing the subject and enrollment count.
170
+ """
171
+ return run_query(
172
+ query=self.queries.get_top_subjects_by_enrollments_query(),
173
+ params={
174
+ 'enterprise_customer_uuid': enterprise_customer_uuid,
175
+ 'start_date': start_date,
176
+ 'end_date': end_date,
177
+ },
178
+ as_dict=True,
179
+ )
180
+
181
+ def get_enrolment_time_series_data(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
182
+ """
183
+ Get the enrollment time series data for the given enterprise customer.
184
+
185
+ Arguments:
186
+ enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
187
+ start_date (date): The start date.
188
+ end_date (date): The end date.
189
+
190
+ Returns:
191
+ list<dict>: A list of dictionaries containing the date and enrollment count.
192
+ """
193
+ return run_query(
194
+ query=self.queries.get_enrolment_time_series_data_query(),
195
+ params={
196
+ 'enterprise_customer_uuid': enterprise_customer_uuid,
197
+ 'start_date': start_date,
198
+ 'end_date': end_date,
199
+ },
200
+ as_dict=True,
201
+ )
@@ -30,22 +30,27 @@ def get_db_connection(database=settings.ENTERPRISE_REPORTING_DB_ALIAS):
30
30
 
31
31
 
32
32
  @timeit
33
- def run_query(query, params: dict = None):
33
+ def run_query(query, params: dict = None, as_dict=False):
34
34
  """
35
35
  Run a query on the database and return the results.
36
36
 
37
37
  Arguments:
38
38
  query (str): The query to run.
39
39
  params (dict): The parameters to pass to the query.
40
+ as_dict (bool): When True, return the results as a dictionary.
40
41
 
41
42
  Returns:
42
- (list): The results of the query.
43
+ (list | dict): The results of the query.
43
44
  """
44
45
  try:
45
46
  with closing(get_db_connection()) as connection:
46
47
  with closing(connection.cursor()) as cursor:
47
48
  cursor.execute(query, params=params)
48
- return cursor.fetchall()
49
+ if as_dict:
50
+ columns = [column[0] for column in cursor.description]
51
+ return [dict(zip(columns, row)) for row in cursor.fetchall()]
52
+ else:
53
+ return cursor.fetchall()
49
54
  except Exception:
50
55
  LOGGER.exception(f'[run_query]: run_query failed for query "{query}".')
51
56
  raise
@@ -265,6 +265,8 @@ class AdvanceAnalyticsQueryParamSerializer(serializers.Serializer): # pylint: d
265
265
  granularity = serializers.CharField(required=False)
266
266
  calculation = serializers.CharField(required=False)
267
267
  response_type = serializers.CharField(required=False)
268
+ page = serializers.IntegerField(required=False, min_value=1)
269
+ page_size = serializers.IntegerField(required=False, min_value=2)
268
270
 
269
271
  def validate(self, attrs):
270
272
  """
@@ -15,10 +15,7 @@ from enterprise_data.api.v1.views.analytics_engagements import (
15
15
  AdvanceAnalyticsEngagementStatsView,
16
16
  AdvanceAnalyticsIndividualEngagementsView,
17
17
  )
18
- from enterprise_data.api.v1.views.analytics_enrollments import (
19
- AdvanceAnalyticsEnrollmentStatsView,
20
- AdvanceAnalyticsIndividualEnrollmentsView,
21
- )
18
+ from enterprise_data.api.v1.views.analytics_enrollments import AdvanceAnalyticsEnrollmentsView
22
19
  from enterprise_data.api.v1.views.analytics_leaderboard import AdvanceAnalyticsLeaderboardView
23
20
  from enterprise_data.constants import UUID4_REGEX
24
21
 
@@ -69,12 +66,12 @@ urlpatterns = [
69
66
  ),
70
67
  re_path(
71
68
  fr'^admin/analytics/(?P<enterprise_uuid>{UUID4_REGEX})/enrollments/stats$',
72
- AdvanceAnalyticsEnrollmentStatsView.as_view(),
69
+ AdvanceAnalyticsEnrollmentsView.as_view({'get': 'stats'}),
73
70
  name='enterprise-admin-analytics-enrollments-stats'
74
71
  ),
75
72
  re_path(
76
73
  fr'^admin/analytics/(?P<enterprise_uuid>{UUID4_REGEX})/enrollments$',
77
- AdvanceAnalyticsIndividualEnrollmentsView.as_view(),
74
+ AdvanceAnalyticsEnrollmentsView.as_view({'get': 'list'}),
78
75
  name='enterprise-admin-analytics-enrollments'
79
76
  ),
80
77
  re_path(
@@ -0,0 +1,146 @@
1
+ """
2
+ Advance Analytics for API endpoints to fetch enterprise enrollments data.
3
+ """
4
+ from datetime import datetime
5
+ from logging import getLogger
6
+
7
+ from edx_rbac.decorators import permission_required
8
+ from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
9
+ from rest_framework.decorators import action
10
+ from rest_framework.response import Response
11
+ from rest_framework.viewsets import ViewSet
12
+
13
+ from django.http import StreamingHttpResponse
14
+
15
+ from enterprise_data.admin_analytics.constants import ResponseType
16
+ from enterprise_data.admin_analytics.database.tables import FactEnrollmentAdminDashTable
17
+ from enterprise_data.api.v1.paginators import AdvanceAnalyticsPagination
18
+ from enterprise_data.api.v1.serializers import (
19
+ AdvanceAnalyticsEnrollmentStatsSerializer,
20
+ AdvanceAnalyticsQueryParamSerializer,
21
+ )
22
+ from enterprise_data.api.v1.views.base import AnalyticsPaginationMixin
23
+ from enterprise_data.renderers import IndividualEnrollmentsCSVRenderer
24
+ from enterprise_data.utils import timer
25
+
26
+ LOGGER = getLogger(__name__)
27
+
28
+
29
+ class AdvanceAnalyticsEnrollmentsView(AnalyticsPaginationMixin, ViewSet):
30
+ """
31
+ View to handle requests for enterprise enrollments data.
32
+
33
+ Here is the list of URLs that are handled by this view:
34
+ 1. `enterprise_data_api_v1.enterprise-learner-enrollment-list`: Get individual enrollment data.
35
+ 2. `enterprise_data_api_v1.enterprise-learner-enrollment-stats`: Get enrollment stats data.
36
+ """
37
+ authentication_classes = (JwtAuthentication,)
38
+ pagination_class = AdvanceAnalyticsPagination
39
+ http_method_names = ('get', )
40
+
41
+ @permission_required('can_access_enterprise', fn=lambda request, enterprise_uuid: enterprise_uuid)
42
+ def list(self, request, enterprise_uuid):
43
+ """
44
+ Get individual enrollments data for the enterprise.
45
+ """
46
+ serializer = AdvanceAnalyticsQueryParamSerializer(data=request.GET)
47
+ serializer.is_valid(raise_exception=True)
48
+ min_enrollment_date, _ = FactEnrollmentAdminDashTable().get_enrollment_date_range(
49
+ enterprise_uuid,
50
+ )
51
+
52
+ # get values from query params or use default values
53
+ start_date = serializer.data.get('start_date', min_enrollment_date)
54
+ end_date = serializer.data.get('end_date', datetime.now())
55
+ page = serializer.data.get('page', 1)
56
+ page_size = serializer.data.get('page_size', 100)
57
+ enrollments = FactEnrollmentAdminDashTable().get_all_enrollments(
58
+ enterprise_customer_uuid=enterprise_uuid,
59
+ start_date=start_date,
60
+ end_date=end_date,
61
+ limit=page_size,
62
+ offset=(page - 1) * page_size,
63
+ )
64
+ total_count = FactEnrollmentAdminDashTable().get_enrollment_count(
65
+ enterprise_customer_uuid=enterprise_uuid,
66
+ start_date=start_date,
67
+ end_date=end_date,
68
+ )
69
+ response_type = request.query_params.get('response_type', ResponseType.JSON.value)
70
+
71
+ LOGGER.info(
72
+ "Individual enrollments data requested for enterprise [%s] from [%s] to [%s]",
73
+ enterprise_uuid,
74
+ start_date,
75
+ end_date,
76
+ )
77
+
78
+ if response_type == ResponseType.CSV.value:
79
+ filename = f"""individual_enrollments, {start_date} - {end_date}.csv"""
80
+
81
+ return StreamingHttpResponse(
82
+ IndividualEnrollmentsCSVRenderer().render(self._stream_serialized_data(
83
+ enterprise_uuid, start_date, end_date, total_count
84
+ )),
85
+ content_type="text/csv",
86
+ headers={"Content-Disposition": f'attachment; filename="{filename}"'},
87
+ )
88
+
89
+ return self.get_paginated_response(
90
+ request=request,
91
+ records=enrollments,
92
+ page=page,
93
+ page_size=page_size,
94
+ total_count=total_count,
95
+ )
96
+
97
+ @staticmethod
98
+ def _stream_serialized_data(enterprise_uuid, start_date, end_date, total_count, page_size=50000):
99
+ """
100
+ Stream the serialized data.
101
+ """
102
+ offset = 0
103
+ while offset < total_count:
104
+ enrollments = FactEnrollmentAdminDashTable().get_all_enrollments(
105
+ enterprise_customer_uuid=enterprise_uuid,
106
+ start_date=start_date,
107
+ end_date=end_date,
108
+ limit=page_size,
109
+ offset=offset,
110
+ )
111
+ yield from enrollments
112
+ offset += page_size
113
+
114
+ @permission_required('can_access_enterprise', fn=lambda request, enterprise_uuid: enterprise_uuid)
115
+ @action(detail=False, methods=['get'], name='Charts Data', url_path='stats')
116
+ def stats(self, request, enterprise_uuid):
117
+ """
118
+ Get data to populate enterprise enrollment charts.
119
+
120
+ Here is the list of the charts and their corresponding data:
121
+ 1. `enrollments_over_time`: This will show time series data of enrollments over time.
122
+ 2. `top_courses_by_enrollments`: This will show the top courses by enrollments.
123
+ 3. `top_subjects_by_enrollments`: This will show the top subjects by enrollments.
124
+ """
125
+ serializer = AdvanceAnalyticsEnrollmentStatsSerializer(data=request.GET)
126
+ serializer.is_valid(raise_exception=True)
127
+
128
+ min_enrollment_date, _ = FactEnrollmentAdminDashTable().get_enrollment_date_range(
129
+ enterprise_uuid,
130
+ )
131
+ # get values from query params or use default
132
+ start_date = serializer.data.get('start_date', min_enrollment_date)
133
+ end_date = serializer.data.get('end_date', datetime.now())
134
+ with timer('construct_enrollment_all_stats'):
135
+ data = {
136
+ 'enrollments_over_time': FactEnrollmentAdminDashTable().get_enrolment_time_series_data(
137
+ enterprise_uuid, start_date, end_date
138
+ ),
139
+ 'top_courses_by_enrollments': FactEnrollmentAdminDashTable().get_top_courses_by_enrollments(
140
+ enterprise_uuid, start_date, end_date,
141
+ ),
142
+ 'top_subjects_by_enrollments': FactEnrollmentAdminDashTable().get_top_subjects_by_enrollments(
143
+ enterprise_uuid, start_date, end_date,
144
+ ),
145
+ }
146
+ return Response(data)