edx-enterprise-data 8.11.0__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.
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/CHANGELOG.rst +8 -0
- {edx_enterprise_data-8.11.0/edx_enterprise_data.egg-info → edx_enterprise_data-8.12.0}/PKG-INFO +1 -1
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0/edx_enterprise_data.egg-info}/PKG-INFO +1 -1
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/__init__.py +1 -1
- edx_enterprise_data-8.12.0/enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py +139 -0
- edx_enterprise_data-8.12.0/enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py +201 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/database/utils.py +8 -3
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/serializers.py +2 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/urls.py +3 -6
- edx_enterprise_data-8.12.0/enterprise_data/api/v1/views/analytics_enrollments.py +146 -0
- edx_enterprise_data-8.12.0/enterprise_data/api/v1/views/base.py +105 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/views/enterprise_admin.py +5 -3
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/mock_analytics_data.py +0 -12
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/test_analytics_enrollments.py +72 -115
- edx_enterprise_data-8.11.0/enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py +0 -61
- edx_enterprise_data-8.11.0/enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py +0 -76
- edx_enterprise_data-8.11.0/enterprise_data/api/v1/views/analytics_enrollments.py +0 -388
- edx_enterprise_data-8.11.0/enterprise_data/api/v1/views/base.py +0 -26
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/LICENSE +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/MANIFEST.in +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/README.md +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/edx_enterprise_data.egg-info/SOURCES.txt +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/edx_enterprise_data.egg-info/dependency_links.txt +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/edx_enterprise_data.egg-info/not-zip-safe +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/edx_enterprise_data.egg-info/requires.txt +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/edx_enterprise_data.egg-info/top_level.txt +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/completions_utils.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/constants.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/data_loaders.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/database/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/database/queries/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/database/tables/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/database/tables/base.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/admin_analytics/utils.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/urls.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/v0/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/v0/serializers.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/v0/urls.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/v0/views.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/paginators.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/views/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/views/analytics_engagements.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/views/analytics_leaderboard.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/views/enterprise_completions.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/views/enterprise_learner.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/views/enterprise_offers.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/apps.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/clients.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/constants.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/filters.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/fixtures/enterprise_enrollment.json +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/fixtures/enterprise_user.json +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/management/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/create_dummy_data.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/create_dummy_data_lpr_v1.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/create_enterprise_enrollment.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/create_enterprise_learner_enrollment_lpr_v1.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/create_enterprise_learner_lpr_v1.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/create_enterprise_offer.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/create_enterprise_user.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/tests/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/tests/test_create_dummy_data_lpr_v1.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/tests/test_create_enterprise_enrollment.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/tests/test_create_enterprise_learner_enrollment_lpr_v1.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/tests/test_create_enterprise_learner_lpr_v1.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/management/commands/tests/test_create_enterprise_user.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0001_initial.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0002_auto_20180430_1358.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0003_auto_20180501_0603.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0004_auto_20180501_0928.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0004_auto_20180508_1652.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0005_auto_20180524_2204.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0006_auto_20180612_0336.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0007_auto_20180612_0534.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0008_auto_20180614_0108.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0009_auto_20180628_1152.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0010_enterpriseenrollment_created.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0011_enterpriseuser.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0012_auto_20180831_1930.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0013_auto_20180831_1931.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0014_enterpriseuser_created.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0015_auto_20180907_1757.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0016_auto_20180924_2138.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0017_enterpriseenrollment_unenrollment_timestamp.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0018_enterprisedatafeaturerole_enterprisedataroleassignment.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0019_add_enterprise_data_feature_roles.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0020_add_role_based_access_control_switch.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0021_auto_20190329_1241.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0022_remove_role_based_access_control_switch.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0023_enterpriselearner_enterpriselearnerenrollment.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0024_auto_20210602_1811.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0025_auto_20210703_1854.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0026_auto_20210916_0414.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0027_enterpriselearnerenrollment_total_learning_time_seconds.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0028_enterpriselearnerenrollment_offer_id.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0029_enterpriseoffer.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0030_auto_20230609_1353.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0031_auto_20230615_0705.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0032_auto_20230704_0818.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0033_enterpriseadminlearnerprogress_enterpriseadminsummarizeinsights.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0034_auto_20230907_0834.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0035_auto_20230907_1154.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0036_enterprisesubsidybudget_subsidy_access_policy_display_name.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0037_alter_enterpriseenrollment_consent_granted.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0038_enterpriseoffer_export_timestamp.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0039_auto_20240212_1403.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0040_auto_20240718_0536_squashed_0043_alter_enterpriselearnerenrollment_enterprise_enrollment_id.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/0044_enterpriseexecedlcmoduleperformance.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/migrations/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/models.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/paginators.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/renderers.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/settings/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/settings/test.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/signals.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/mock_enrollments.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/test_analytics_engagements.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/test_analytics_leaderboard.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/test_data_loaders.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/test_enterprise_completions.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/admin_analytics/test_utils.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/api/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/api/v0/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/api/v0/test_serializers.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/api/v1/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/api/v1/test_serializers.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/api/v1/test_views.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/api/v1/views/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/api/v1/views/test_enterprise_admin.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/factories.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/mixins.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/test_clients.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/test_filters.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/test_models.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/test_utils.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/tests/test_views.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/urls.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/utils.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data_roles/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data_roles/admin.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data_roles/apps.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data_roles/constants.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data_roles/migrations/0001_initial.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data_roles/migrations/0002_add_enterprise_data_feature_roles.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data_roles/migrations/0003_add_role_based_access_control_switch.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data_roles/migrations/0004_enterprisedataroleassignment_enterprise_id.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data_roles/migrations/0005_turn_on_role_based_access_control_switch.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data_roles/migrations/0006_remove_role_based_access_control_switch.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data_roles/migrations/0007_enterprisedataroleassignment_applies_to_all_contexts.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data_roles/migrations/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data_roles/models.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data_roles/rules.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data_roles/tests/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data_roles/tests/factories.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data_roles/tests/test_models.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/clients/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/clients/enterprise.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/clients/s3.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/clients/snowflake.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/clients/vertica.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/delivery_method.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/external_resource_link_report.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/fixtures/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/fixtures/enterprise_customer_reporting.json +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/reporter.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/send_enterprise_reports.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/__init__.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/test_clients.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/test_delivery_method.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/test_enterprise_client.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/test_external_link_report.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/test_reporter.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/test_send_enterprise_reports.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/test_utils.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/test_vertica_client.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/tests/utils.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_reporting/utils.py +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/requirements/base.in +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/requirements/base.txt +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/requirements/ci.txt +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/requirements/common_constraints.txt +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/requirements/constraints.txt +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/requirements/dev.txt +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/requirements/django.txt +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/requirements/pip.txt +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/requirements/pip_tools.txt +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/requirements/quality.txt +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/requirements/reporting.in +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/requirements/test-master.txt +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/requirements/test-reporting.txt +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/requirements/test.txt +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/setup.cfg +0 -0
- {edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/setup.py +0 -0
@@ -15,6 +15,14 @@ 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
|
+
|
22
|
+
[8.11.1] - 2024-08-29
|
23
|
+
---------------------
|
24
|
+
* fix: Fixed a datetime conversion error appearing on production.
|
25
|
+
|
18
26
|
[8.11.0] - 2024-08-29
|
19
27
|
---------------------
|
20
28
|
* perf: Performance enhancements for admin analytics aggregates endpoint.
|
@@ -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
|
-
|
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
|
{edx_enterprise_data-8.11.0 → edx_enterprise_data-8.12.0}/enterprise_data/api/v1/serializers.py
RENAMED
@@ -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
|
-
|
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
|
-
|
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)
|