edx-enterprise-data 9.1.1__tar.gz → 9.2.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-9.1.1 → edx_enterprise_data-9.2.0}/CHANGELOG.rst +4 -0
- {edx_enterprise_data-9.1.1/edx_enterprise_data.egg-info → edx_enterprise_data-9.2.0}/PKG-INFO +1 -1
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0/edx_enterprise_data.egg-info}/PKG-INFO +1 -1
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/edx_enterprise_data.egg-info/SOURCES.txt +0 -3
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/__init__.py +1 -1
- edx_enterprise_data-9.2.0/enterprise_data/admin_analytics/constants.py +11 -0
- edx_enterprise_data-9.2.0/enterprise_data/admin_analytics/data_loaders.py +24 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py +85 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py +50 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/v1/serializers.py +1 -38
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/v1/urls.py +2 -2
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/v1/views/analytics_completions.py +0 -2
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/v1/views/analytics_engagements.py +0 -2
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/v1/views/analytics_enrollments.py +0 -2
- edx_enterprise_data-9.2.0/enterprise_data/api/v1/views/analytics_leaderboard.py +107 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/renderers.py +2 -2
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/admin_analytics/mock_analytics_data.py +15 -60
- edx_enterprise_data-9.2.0/enterprise_data/tests/admin_analytics/test_analytics_leaderboard.py +130 -0
- edx_enterprise_data-9.2.0/enterprise_data/tests/admin_analytics/test_data_loaders.py +29 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/utils.py +0 -16
- edx_enterprise_data-9.1.1/enterprise_data/admin_analytics/constants.py +0 -25
- edx_enterprise_data-9.1.1/enterprise_data/admin_analytics/data_loaders.py +0 -149
- edx_enterprise_data-9.1.1/enterprise_data/admin_analytics/utils.py +0 -180
- edx_enterprise_data-9.1.1/enterprise_data/api/v1/paginators.py +0 -121
- edx_enterprise_data-9.1.1/enterprise_data/api/v1/views/analytics_leaderboard.py +0 -144
- edx_enterprise_data-9.1.1/enterprise_data/tests/admin_analytics/test_analytics_leaderboard.py +0 -163
- edx_enterprise_data-9.1.1/enterprise_data/tests/admin_analytics/test_data_loaders.py +0 -86
- edx_enterprise_data-9.1.1/enterprise_data/tests/admin_analytics/test_utils.py +0 -102
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/LICENSE +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/MANIFEST.in +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/README.md +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/edx_enterprise_data.egg-info/dependency_links.txt +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/edx_enterprise_data.egg-info/not-zip-safe +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/edx_enterprise_data.egg-info/requires.txt +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/edx_enterprise_data.egg-info/top_level.txt +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/admin_analytics/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/admin_analytics/database/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/admin_analytics/database/queries/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/admin_analytics/database/queries/skills_daily_rollup_admin_dash.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/admin_analytics/database/tables/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/admin_analytics/database/tables/base.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/admin_analytics/database/tables/skills_daily_rollup_admin_dash.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/admin_analytics/database/utils.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/urls.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/v0/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/v0/serializers.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/v0/urls.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/v0/views.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/v1/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/v1/views/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/v1/views/base.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/v1/views/enterprise_admin.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/v1/views/enterprise_learner.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/v1/views/enterprise_offers.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/apps.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/clients.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/constants.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/filters.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/fixtures/enterprise_enrollment.json +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/fixtures/enterprise_user.json +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/management/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/management/commands/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/management/commands/create_dummy_data.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/management/commands/create_dummy_data_lpr_v1.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/management/commands/create_enterprise_enrollment.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/management/commands/create_enterprise_learner_enrollment_lpr_v1.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/management/commands/create_enterprise_learner_lpr_v1.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/management/commands/create_enterprise_offer.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/management/commands/create_enterprise_user.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/management/commands/tests/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/management/commands/tests/test_create_dummy_data_lpr_v1.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/management/commands/tests/test_create_enterprise_enrollment.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/management/commands/tests/test_create_enterprise_learner_enrollment_lpr_v1.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/management/commands/tests/test_create_enterprise_learner_lpr_v1.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/management/commands/tests/test_create_enterprise_user.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0001_initial.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0002_auto_20180430_1358.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0003_auto_20180501_0603.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0004_auto_20180501_0928.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0004_auto_20180508_1652.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0005_auto_20180524_2204.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0006_auto_20180612_0336.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0007_auto_20180612_0534.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0008_auto_20180614_0108.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0009_auto_20180628_1152.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0010_enterpriseenrollment_created.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0011_enterpriseuser.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0012_auto_20180831_1930.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0013_auto_20180831_1931.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0014_enterpriseuser_created.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0015_auto_20180907_1757.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0016_auto_20180924_2138.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0017_enterpriseenrollment_unenrollment_timestamp.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0018_enterprisedatafeaturerole_enterprisedataroleassignment.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0019_add_enterprise_data_feature_roles.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0020_add_role_based_access_control_switch.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0021_auto_20190329_1241.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0022_remove_role_based_access_control_switch.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0023_enterpriselearner_enterpriselearnerenrollment.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0024_auto_20210602_1811.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0025_auto_20210703_1854.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0026_auto_20210916_0414.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0027_enterpriselearnerenrollment_total_learning_time_seconds.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0028_enterpriselearnerenrollment_offer_id.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0029_enterpriseoffer.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0030_auto_20230609_1353.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0031_auto_20230615_0705.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0032_auto_20230704_0818.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0033_enterpriseadminlearnerprogress_enterpriseadminsummarizeinsights.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0034_auto_20230907_0834.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0035_auto_20230907_1154.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0036_enterprisesubsidybudget_subsidy_access_policy_display_name.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0037_alter_enterpriseenrollment_consent_granted.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0038_enterpriseoffer_export_timestamp.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0039_auto_20240212_1403.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0040_auto_20240718_0536_squashed_0043_alter_enterpriselearnerenrollment_enterprise_enrollment_id.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/0044_enterpriseexecedlcmoduleperformance.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/migrations/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/models.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/paginators.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/settings/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/settings/test.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/signals.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/admin_analytics/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/admin_analytics/mock_enrollments.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/admin_analytics/test_analytics_engagements.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/admin_analytics/test_analytics_enrollments.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/admin_analytics/test_enterprise_completions.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/api/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/api/v0/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/api/v0/test_serializers.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/api/v1/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/api/v1/test_serializers.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/api/v1/test_views.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/api/v1/views/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/api/v1/views/test_enterprise_admin.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/factories.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/mixins.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/test_clients.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/test_filters.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/test_models.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/test_utils.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/tests/test_views.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/urls.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data_roles/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data_roles/admin.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data_roles/apps.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data_roles/constants.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data_roles/migrations/0001_initial.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data_roles/migrations/0002_add_enterprise_data_feature_roles.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data_roles/migrations/0003_add_role_based_access_control_switch.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data_roles/migrations/0004_enterprisedataroleassignment_enterprise_id.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data_roles/migrations/0005_turn_on_role_based_access_control_switch.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data_roles/migrations/0006_remove_role_based_access_control_switch.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data_roles/migrations/0007_enterprisedataroleassignment_applies_to_all_contexts.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data_roles/migrations/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data_roles/models.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data_roles/rules.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data_roles/tests/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data_roles/tests/factories.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data_roles/tests/test_models.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/clients/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/clients/enterprise.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/clients/s3.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/clients/snowflake.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/clients/vertica.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/delivery_method.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/external_resource_link_report.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/fixtures/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/fixtures/enterprise_customer_reporting.json +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/reporter.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/send_enterprise_reports.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/tests/__init__.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/tests/test_clients.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/tests/test_delivery_method.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/tests/test_enterprise_client.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/tests/test_external_link_report.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/tests/test_reporter.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/tests/test_send_enterprise_reports.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/tests/test_utils.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/tests/test_vertica_client.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/tests/utils.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_reporting/utils.py +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/requirements/base.in +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/requirements/base.txt +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/requirements/ci.txt +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/requirements/common_constraints.txt +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/requirements/constraints.txt +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/requirements/dev.txt +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/requirements/django.txt +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/requirements/pip.txt +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/requirements/pip_tools.txt +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/requirements/quality.txt +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/requirements/reporting.in +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/requirements/test-master.txt +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/requirements/test-reporting.txt +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/requirements/test.txt +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/setup.cfg +0 -0
- {edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/setup.py +0 -0
@@ -16,6 +16,10 @@ Unreleased
|
|
16
16
|
|
17
17
|
=========================
|
18
18
|
|
19
|
+
[9.2.0] - 2024-09-25
|
20
|
+
---------------------
|
21
|
+
* refactor: Performance optimizations for leaderboard API endpoints
|
22
|
+
|
19
23
|
[9.1.1] - 2024-09-24
|
20
24
|
---------------------
|
21
25
|
* fix: disable caching for EnterpriseLearnerEnrollmentViewSet
|
{edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/edx_enterprise_data.egg-info/SOURCES.txt
RENAMED
@@ -23,7 +23,6 @@ enterprise_data/utils.py
|
|
23
23
|
enterprise_data/admin_analytics/__init__.py
|
24
24
|
enterprise_data/admin_analytics/constants.py
|
25
25
|
enterprise_data/admin_analytics/data_loaders.py
|
26
|
-
enterprise_data/admin_analytics/utils.py
|
27
26
|
enterprise_data/admin_analytics/database/__init__.py
|
28
27
|
enterprise_data/admin_analytics/database/utils.py
|
29
28
|
enterprise_data/admin_analytics/database/queries/__init__.py
|
@@ -42,7 +41,6 @@ enterprise_data/api/v0/serializers.py
|
|
42
41
|
enterprise_data/api/v0/urls.py
|
43
42
|
enterprise_data/api/v0/views.py
|
44
43
|
enterprise_data/api/v1/__init__.py
|
45
|
-
enterprise_data/api/v1/paginators.py
|
46
44
|
enterprise_data/api/v1/serializers.py
|
47
45
|
enterprise_data/api/v1/urls.py
|
48
46
|
enterprise_data/api/v1/views/__init__.py
|
@@ -132,7 +130,6 @@ enterprise_data/tests/admin_analytics/test_analytics_enrollments.py
|
|
132
130
|
enterprise_data/tests/admin_analytics/test_analytics_leaderboard.py
|
133
131
|
enterprise_data/tests/admin_analytics/test_data_loaders.py
|
134
132
|
enterprise_data/tests/admin_analytics/test_enterprise_completions.py
|
135
|
-
enterprise_data/tests/admin_analytics/test_utils.py
|
136
133
|
enterprise_data/tests/api/__init__.py
|
137
134
|
enterprise_data/tests/api/v0/__init__.py
|
138
135
|
enterprise_data/tests/api/v0/test_serializers.py
|
@@ -0,0 +1,24 @@
|
|
1
|
+
"""
|
2
|
+
Utility functions for fetching data from the database.
|
3
|
+
"""
|
4
|
+
from logging import getLogger
|
5
|
+
|
6
|
+
import pandas
|
7
|
+
|
8
|
+
from enterprise_data.admin_analytics.database import run_query
|
9
|
+
|
10
|
+
LOGGER = getLogger(__name__)
|
11
|
+
|
12
|
+
|
13
|
+
def fetch_max_enrollment_datetime():
|
14
|
+
"""
|
15
|
+
Fetch the latest created date from the enterprise_learner_enrollment table.
|
16
|
+
|
17
|
+
created will be same for all records as this is added at the time of data load. Which is when the async process
|
18
|
+
populates the data in the table. We can use this to get the latest data load time.
|
19
|
+
"""
|
20
|
+
query = "SELECT MAX(created) FROM enterprise_learner_enrollment"
|
21
|
+
results = run_query(query)
|
22
|
+
if not results:
|
23
|
+
return None
|
24
|
+
return pandas.to_datetime(results[0][0])
|
@@ -109,3 +109,88 @@ class FactEngagementAdminDashQueries:
|
|
109
109
|
GROUP BY activity_date, enroll_type
|
110
110
|
ORDER BY activity_date;
|
111
111
|
"""
|
112
|
+
|
113
|
+
@staticmethod
|
114
|
+
def get_all_leaderboard_data_query():
|
115
|
+
"""
|
116
|
+
Get the query to fetch the leaderboard data.
|
117
|
+
|
118
|
+
Query should fetch the leaderboard data for the enterprise customer to show in the data table.
|
119
|
+
|
120
|
+
Returns:
|
121
|
+
(str): Query to fetch the leaderboard data.
|
122
|
+
"""
|
123
|
+
return """
|
124
|
+
WITH Engagement AS (
|
125
|
+
SELECT
|
126
|
+
email,
|
127
|
+
ROUND(SUM(learning_time_seconds) / 3600, 1) as learning_time_hours,
|
128
|
+
SUM(is_engaged) as session_count,
|
129
|
+
CASE
|
130
|
+
WHEN SUM(is_engaged) = 0 THEN 0.0
|
131
|
+
ELSE ROUND(SUM(learning_time_seconds) / 3600 / SUM(is_engaged), 1)
|
132
|
+
END AS average_session_length
|
133
|
+
FROM fact_enrollment_engagement_day_admin_dash
|
134
|
+
WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
|
135
|
+
(activity_date BETWEEN %(start_date)s AND %(end_date)s) AND
|
136
|
+
is_engaged = 1
|
137
|
+
GROUP BY email
|
138
|
+
),
|
139
|
+
Completions AS (
|
140
|
+
SELECT email, count(course_key) as course_completion_count
|
141
|
+
FROM fact_enrollment_admin_dash
|
142
|
+
WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
|
143
|
+
(passed_date BETWEEN %(start_date)s AND %(end_date)s) AND
|
144
|
+
has_passed = 1
|
145
|
+
GROUP BY email
|
146
|
+
)
|
147
|
+
SELECT
|
148
|
+
Engagement.email,
|
149
|
+
Engagement.learning_time_hours,
|
150
|
+
Engagement.session_count,
|
151
|
+
Engagement.average_session_length,
|
152
|
+
Completions.course_completion_count
|
153
|
+
FROM Engagement
|
154
|
+
LEFT JOIN Completions
|
155
|
+
ON Engagement.email = Completions.email
|
156
|
+
ORDER BY
|
157
|
+
Engagement.learning_time_hours DESC,
|
158
|
+
Engagement.session_count DESC,
|
159
|
+
Completions.course_completion_count DESC
|
160
|
+
LIMIT %(limit)s OFFSET %(offset)s;
|
161
|
+
"""
|
162
|
+
|
163
|
+
@staticmethod
|
164
|
+
def get_leaderboard_data_count_query():
|
165
|
+
"""
|
166
|
+
Get the query to fetch the leaderboard row count.
|
167
|
+
|
168
|
+
Query should fetch the count of rows for the leaderboard data for the enterprise customer.
|
169
|
+
|
170
|
+
Returns:
|
171
|
+
(str): Query to fetch the leaderboard row count.
|
172
|
+
"""
|
173
|
+
return """
|
174
|
+
WITH Engagement AS (
|
175
|
+
SELECT
|
176
|
+
email
|
177
|
+
FROM fact_enrollment_engagement_day_admin_dash
|
178
|
+
WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
|
179
|
+
(activity_date BETWEEN %(start_date)s AND %(end_date)s) AND
|
180
|
+
is_engaged = 1
|
181
|
+
GROUP BY email
|
182
|
+
),
|
183
|
+
Completions AS (
|
184
|
+
SELECT email, count(course_key) as course_completion_count
|
185
|
+
FROM fact_enrollment_admin_dash
|
186
|
+
WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
|
187
|
+
(passed_date BETWEEN %(start_date)s AND %(end_date)s) AND
|
188
|
+
has_passed = 1
|
189
|
+
GROUP BY email
|
190
|
+
)
|
191
|
+
SELECT
|
192
|
+
count(*)
|
193
|
+
FROM Engagement
|
194
|
+
LEFT JOIN Completions
|
195
|
+
ON Engagement.email = Completions.email
|
196
|
+
"""
|
@@ -152,3 +152,53 @@ class FactEngagementAdminDashTable(BaseTable):
|
|
152
152
|
},
|
153
153
|
as_dict=True,
|
154
154
|
)
|
155
|
+
|
156
|
+
def get_all_leaderboard_data(
|
157
|
+
self, enterprise_customer_uuid: UUID, start_date: date, end_date: date, limit: int, offset: int
|
158
|
+
):
|
159
|
+
"""
|
160
|
+
Get the leaderboard data for the given enterprise customer.
|
161
|
+
|
162
|
+
Arguments:
|
163
|
+
enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
|
164
|
+
start_date (date): The start date.
|
165
|
+
end_date (date): The end date.
|
166
|
+
limit (int): The maximum number of records to return.
|
167
|
+
offset (int): The number of records to skip.
|
168
|
+
|
169
|
+
Returns:
|
170
|
+
list[dict]: The leaderboard data.
|
171
|
+
"""
|
172
|
+
return run_query(
|
173
|
+
query=self.queries.get_all_leaderboard_data_query(),
|
174
|
+
params={
|
175
|
+
'enterprise_customer_uuid': enterprise_customer_uuid,
|
176
|
+
'start_date': start_date,
|
177
|
+
'end_date': end_date,
|
178
|
+
'limit': limit,
|
179
|
+
'offset': offset,
|
180
|
+
},
|
181
|
+
as_dict=True,
|
182
|
+
)
|
183
|
+
|
184
|
+
def get_leaderboard_data_count(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
|
185
|
+
"""
|
186
|
+
Get the total number of leaderboard records for the given enterprise customer.
|
187
|
+
|
188
|
+
Arguments:
|
189
|
+
enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
|
190
|
+
start_date (date): The start date.
|
191
|
+
end_date (date): The end date.
|
192
|
+
|
193
|
+
Returns:
|
194
|
+
(int): The total number of leaderboard records.
|
195
|
+
"""
|
196
|
+
results = run_query(
|
197
|
+
query=self.queries.get_leaderboard_data_count_query(),
|
198
|
+
params={
|
199
|
+
'enterprise_customer_uuid': enterprise_customer_uuid,
|
200
|
+
'start_date': start_date,
|
201
|
+
'end_date': end_date,
|
202
|
+
}
|
203
|
+
)
|
204
|
+
return results[0][0]
|
{edx_enterprise_data-9.1.1 → edx_enterprise_data-9.2.0}/enterprise_data/api/v1/serializers.py
RENAMED
@@ -5,7 +5,7 @@ from uuid import UUID
|
|
5
5
|
|
6
6
|
from rest_framework import serializers
|
7
7
|
|
8
|
-
from enterprise_data.admin_analytics.constants import
|
8
|
+
from enterprise_data.admin_analytics.constants import ResponseType
|
9
9
|
from enterprise_data.models import (
|
10
10
|
EnterpriseAdminLearnerProgress,
|
11
11
|
EnterpriseAdminSummarizeInsights,
|
@@ -241,23 +241,8 @@ class AdvanceAnalyticsQueryParamSerializer(serializers.Serializer): # pylint: d
|
|
241
241
|
ResponseType.JSON.value,
|
242
242
|
ResponseType.CSV.value
|
243
243
|
]
|
244
|
-
GRANULARITY_CHOICES = [
|
245
|
-
Granularity.DAILY.value,
|
246
|
-
Granularity.WEEKLY.value,
|
247
|
-
Granularity.MONTHLY.value,
|
248
|
-
Granularity.QUARTERLY.value
|
249
|
-
]
|
250
|
-
CALCULATION_CHOICES = [
|
251
|
-
Calculation.TOTAL.value,
|
252
|
-
Calculation.RUNNING_TOTAL.value,
|
253
|
-
Calculation.MOVING_AVERAGE_3_PERIOD.value,
|
254
|
-
Calculation.MOVING_AVERAGE_7_PERIOD.value
|
255
|
-
]
|
256
|
-
|
257
244
|
start_date = serializers.DateField(required=False)
|
258
245
|
end_date = serializers.DateField(required=False)
|
259
|
-
granularity = serializers.CharField(required=False)
|
260
|
-
calculation = serializers.CharField(required=False)
|
261
246
|
response_type = serializers.CharField(required=False)
|
262
247
|
page = serializers.IntegerField(required=False, min_value=1)
|
263
248
|
page_size = serializers.IntegerField(required=False, min_value=2)
|
@@ -287,25 +272,3 @@ class AdvanceAnalyticsQueryParamSerializer(serializers.Serializer): # pylint: d
|
|
287
272
|
if value not in self.RESPONSE_TYPES:
|
288
273
|
raise serializers.ValidationError(f"response_type must be one of {self.RESPONSE_TYPES}")
|
289
274
|
return value
|
290
|
-
|
291
|
-
def validate_granularity(self, value):
|
292
|
-
"""
|
293
|
-
Validate the granularity value.
|
294
|
-
|
295
|
-
Raises:
|
296
|
-
serializers.ValidationError: If granularity is not one of the valid choices.
|
297
|
-
"""
|
298
|
-
if value not in self.GRANULARITY_CHOICES:
|
299
|
-
raise serializers.ValidationError(f"Granularity must be one of {self.GRANULARITY_CHOICES}")
|
300
|
-
return value
|
301
|
-
|
302
|
-
def validate_calculation(self, value):
|
303
|
-
"""
|
304
|
-
Validate the calculation value.
|
305
|
-
|
306
|
-
Raises:
|
307
|
-
serializers.ValidationError: If calculation is not one of the valid choices
|
308
|
-
"""
|
309
|
-
if value not in self.CALCULATION_CHOICES:
|
310
|
-
raise serializers.ValidationError(f"Calculation must be one of {self.CALCULATION_CHOICES}")
|
311
|
-
return value
|
@@ -58,8 +58,8 @@ urlpatterns = [
|
|
58
58
|
),
|
59
59
|
re_path(
|
60
60
|
fr'^admin/analytics/(?P<enterprise_uuid>{UUID4_REGEX})/leaderboard$',
|
61
|
-
AdvanceAnalyticsLeaderboardView.as_view(),
|
62
|
-
name='enterprise-admin-analytics-leaderboard'
|
61
|
+
AdvanceAnalyticsLeaderboardView.as_view({'get': 'list'}),
|
62
|
+
name='enterprise-admin-analytics-leaderboard-list'
|
63
63
|
),
|
64
64
|
re_path(
|
65
65
|
fr'^admin/analytics/(?P<enterprise_uuid>{UUID4_REGEX})/enrollments/stats$',
|
@@ -15,7 +15,6 @@ from django.http import StreamingHttpResponse
|
|
15
15
|
|
16
16
|
from enterprise_data.admin_analytics.constants import ResponseType
|
17
17
|
from enterprise_data.admin_analytics.database.tables import FactEnrollmentAdminDashTable
|
18
|
-
from enterprise_data.api.v1.paginators import AdvanceAnalyticsPagination
|
19
18
|
from enterprise_data.api.v1.serializers import AdvanceAnalyticsQueryParamSerializer
|
20
19
|
from enterprise_data.api.v1.views.base import AnalyticsPaginationMixin
|
21
20
|
from enterprise_data.renderers import IndividualCompletionsCSVRenderer
|
@@ -33,7 +32,6 @@ class AdvanceAnalyticsCompletionsView(AnalyticsPaginationMixin, ViewSet):
|
|
33
32
|
2. `enterprise_data_api_v1.enterprise-learner-completion-stats`: Get completion stats data.
|
34
33
|
"""
|
35
34
|
authentication_classes = (JwtAuthentication,)
|
36
|
-
pagination_class = AdvanceAnalyticsPagination
|
37
35
|
http_method_names = ('get', )
|
38
36
|
|
39
37
|
@permission_required('can_access_enterprise', fn=lambda request, enterprise_uuid: enterprise_uuid)
|
@@ -15,7 +15,6 @@ from django.http import StreamingHttpResponse
|
|
15
15
|
|
16
16
|
from enterprise_data.admin_analytics.constants import ResponseType
|
17
17
|
from enterprise_data.admin_analytics.database.tables import FactEngagementAdminDashTable, FactEnrollmentAdminDashTable
|
18
|
-
from enterprise_data.api.v1.paginators import AdvanceAnalyticsPagination
|
19
18
|
from enterprise_data.api.v1.serializers import AdvanceAnalyticsQueryParamSerializer
|
20
19
|
from enterprise_data.api.v1.views.base import AnalyticsPaginationMixin
|
21
20
|
from enterprise_data.renderers import IndividualEngagementsCSVRenderer
|
@@ -33,7 +32,6 @@ class AdvanceAnalyticsEngagementView(AnalyticsPaginationMixin, ViewSet):
|
|
33
32
|
2. `enterprise_data_api_v1.enterprise-learner-engagement-stats`: Get engagement stats data.
|
34
33
|
"""
|
35
34
|
authentication_classes = (JwtAuthentication,)
|
36
|
-
pagination_class = AdvanceAnalyticsPagination
|
37
35
|
http_method_names = ('get', )
|
38
36
|
|
39
37
|
@permission_required('can_access_enterprise', fn=lambda request, enterprise_uuid: enterprise_uuid)
|
@@ -14,7 +14,6 @@ from django.http import StreamingHttpResponse
|
|
14
14
|
|
15
15
|
from enterprise_data.admin_analytics.constants import ResponseType
|
16
16
|
from enterprise_data.admin_analytics.database.tables import FactEnrollmentAdminDashTable
|
17
|
-
from enterprise_data.api.v1.paginators import AdvanceAnalyticsPagination
|
18
17
|
from enterprise_data.api.v1.serializers import AdvanceAnalyticsQueryParamSerializer
|
19
18
|
from enterprise_data.api.v1.views.base import AnalyticsPaginationMixin
|
20
19
|
from enterprise_data.renderers import IndividualEnrollmentsCSVRenderer
|
@@ -32,7 +31,6 @@ class AdvanceAnalyticsEnrollmentsView(AnalyticsPaginationMixin, ViewSet):
|
|
32
31
|
2. `enterprise_data_api_v1.enterprise-learner-enrollment-stats`: Get enrollment stats data.
|
33
32
|
"""
|
34
33
|
authentication_classes = (JwtAuthentication,)
|
35
|
-
pagination_class = AdvanceAnalyticsPagination
|
36
34
|
http_method_names = ('get', )
|
37
35
|
|
38
36
|
@permission_required('can_access_enterprise', fn=lambda request, enterprise_uuid: enterprise_uuid)
|
@@ -0,0 +1,107 @@
|
|
1
|
+
"""
|
2
|
+
Views for fetching leaderboard data.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from datetime import datetime
|
6
|
+
from logging import getLogger
|
7
|
+
|
8
|
+
from edx_rbac.decorators import permission_required
|
9
|
+
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
|
10
|
+
from rest_framework.viewsets import ViewSet
|
11
|
+
|
12
|
+
from django.http import StreamingHttpResponse
|
13
|
+
|
14
|
+
from enterprise_data.admin_analytics.constants import ResponseType
|
15
|
+
from enterprise_data.admin_analytics.database.tables import FactEngagementAdminDashTable, FactEnrollmentAdminDashTable
|
16
|
+
from enterprise_data.api.v1.serializers import AdvanceAnalyticsQueryParamSerializer
|
17
|
+
from enterprise_data.api.v1.views.base import AnalyticsPaginationMixin
|
18
|
+
from enterprise_data.renderers import LeaderboardCSVRenderer
|
19
|
+
|
20
|
+
LOGGER = getLogger(__name__)
|
21
|
+
|
22
|
+
|
23
|
+
class AdvanceAnalyticsLeaderboardView(AnalyticsPaginationMixin, ViewSet):
|
24
|
+
"""
|
25
|
+
View to handle requests for enterprise leaderboard data.
|
26
|
+
|
27
|
+
Here is the list of URLs that are handled by this view:
|
28
|
+
1. `enterprise_data_api_v1.enterprise-admin-analytics-leaderboard-list`: Get leaderboard data.
|
29
|
+
"""
|
30
|
+
authentication_classes = (JwtAuthentication,)
|
31
|
+
http_method_names = ('get', )
|
32
|
+
|
33
|
+
@permission_required('can_access_enterprise', fn=lambda request, enterprise_uuid: enterprise_uuid)
|
34
|
+
def list(self, request, enterprise_uuid):
|
35
|
+
"""
|
36
|
+
Get individual leaderboard data for the enterprise.
|
37
|
+
"""
|
38
|
+
# Remove hyphens from the UUID
|
39
|
+
enterprise_uuid = enterprise_uuid.replace('-', '')
|
40
|
+
|
41
|
+
serializer = AdvanceAnalyticsQueryParamSerializer(data=request.GET)
|
42
|
+
serializer.is_valid(raise_exception=True)
|
43
|
+
min_enrollment_date, _ = FactEnrollmentAdminDashTable().get_enrollment_date_range(
|
44
|
+
enterprise_uuid,
|
45
|
+
)
|
46
|
+
|
47
|
+
# get values from query params or use default values
|
48
|
+
start_date = serializer.data.get('start_date', min_enrollment_date)
|
49
|
+
end_date = serializer.data.get('end_date', datetime.now())
|
50
|
+
page = serializer.data.get('page', 1)
|
51
|
+
page_size = serializer.data.get('page_size', 100)
|
52
|
+
leaderboard = FactEngagementAdminDashTable().get_all_leaderboard_data(
|
53
|
+
enterprise_customer_uuid=enterprise_uuid,
|
54
|
+
start_date=start_date,
|
55
|
+
end_date=end_date,
|
56
|
+
limit=page_size,
|
57
|
+
offset=(page - 1) * page_size,
|
58
|
+
)
|
59
|
+
total_count = FactEngagementAdminDashTable().get_leaderboard_data_count(
|
60
|
+
enterprise_customer_uuid=enterprise_uuid,
|
61
|
+
start_date=start_date,
|
62
|
+
end_date=end_date,
|
63
|
+
)
|
64
|
+
response_type = request.query_params.get('response_type', ResponseType.JSON.value)
|
65
|
+
|
66
|
+
LOGGER.info(
|
67
|
+
'Leaderboard data requested for enterprise [%s] from [%s] to [%s]',
|
68
|
+
enterprise_uuid,
|
69
|
+
start_date,
|
70
|
+
end_date,
|
71
|
+
)
|
72
|
+
|
73
|
+
if response_type == ResponseType.CSV.value:
|
74
|
+
filename = f'Leaderboard, {start_date} - {end_date}.csv'
|
75
|
+
|
76
|
+
return StreamingHttpResponse(
|
77
|
+
LeaderboardCSVRenderer().render(self._stream_serialized_data(
|
78
|
+
enterprise_uuid, start_date, end_date, total_count
|
79
|
+
)),
|
80
|
+
content_type='text/csv',
|
81
|
+
headers={'Content-Disposition': f'attachment; filename="{filename}"'},
|
82
|
+
)
|
83
|
+
|
84
|
+
return self.get_paginated_response(
|
85
|
+
request=request,
|
86
|
+
records=leaderboard,
|
87
|
+
page=page,
|
88
|
+
page_size=page_size,
|
89
|
+
total_count=total_count,
|
90
|
+
)
|
91
|
+
|
92
|
+
@staticmethod
|
93
|
+
def _stream_serialized_data(enterprise_uuid, start_date, end_date, total_count, page_size=50000):
|
94
|
+
"""
|
95
|
+
Stream the serialized data.
|
96
|
+
"""
|
97
|
+
offset = 0
|
98
|
+
while offset < total_count:
|
99
|
+
leaderboard = FactEngagementAdminDashTable().get_all_leaderboard_data(
|
100
|
+
enterprise_customer_uuid=enterprise_uuid,
|
101
|
+
start_date=start_date,
|
102
|
+
end_date=end_date,
|
103
|
+
limit=page_size,
|
104
|
+
offset=offset,
|
105
|
+
)
|
106
|
+
yield from leaderboard
|
107
|
+
offset += page_size
|
@@ -1,6 +1,6 @@
|
|
1
|
-
"""
|
2
|
-
|
3
|
-
|
1
|
+
"""
|
2
|
+
Mock data for admin analytics tests.
|
3
|
+
"""
|
4
4
|
|
5
5
|
ENROLLMENTS = [
|
6
6
|
{
|
@@ -328,132 +328,87 @@ ENGAGEMENTS = [
|
|
328
328
|
]
|
329
329
|
|
330
330
|
|
331
|
-
def enrollments_dataframe():
|
332
|
-
"""Return a DataFrame of enrollments."""
|
333
|
-
enrollments = pd.DataFrame(ENROLLMENTS)
|
334
|
-
|
335
|
-
enrollments['enterprise_enrollment_date'] = enrollments['enterprise_enrollment_date'].astype('datetime64[ns]')
|
336
|
-
enrollments['date_certificate_awarded'] = enrollments['date_certificate_awarded'].astype('datetime64[ns]')
|
337
|
-
enrollments['date_certificate_created_raw'] = enrollments['date_certificate_created_raw'].astype('datetime64[ns]')
|
338
|
-
enrollments['passed_date_raw'] = enrollments['passed_date_raw'].astype('datetime64[ns]')
|
339
|
-
enrollments['passed_date'] = enrollments['passed_date'].astype('datetime64[ns]')
|
340
|
-
|
341
|
-
return enrollments
|
342
|
-
|
343
|
-
|
344
|
-
def engagements_dataframe():
|
345
|
-
"""Return a DataFrame of engagements."""
|
346
|
-
engagements = pd.DataFrame(ENGAGEMENTS)
|
347
|
-
engagements['activity_date'] = engagements['activity_date'].astype('datetime64[ns]')
|
348
|
-
return engagements
|
349
|
-
|
350
|
-
|
351
|
-
def leaderboard_csv_content():
|
352
|
-
"""Return the CSV content of leaderboard."""
|
353
|
-
return (
|
354
|
-
b'email,learning_time_hours,daily_sessions,average_session_length,course_completions\r\n'
|
355
|
-
b'paul77@example.org,4.4,1,4.4,\r\nseth57@example.org,2.7,1,2.7,\r\n'
|
356
|
-
b'weaverpatricia@example.net,2.6,1,2.6,\r\nwebertodd@example.com,1.5,1,1.5,\r\n'
|
357
|
-
b'yferguson@example.net,1.3,1,1.3,\r\nyallison@example.org,1.2,1,1.2,\r\n'
|
358
|
-
b'padillamichelle@example.org,1.0,1,1.0,\r\ncaseyjohnny@example.com,0.0,0,0.0,\r\n'
|
359
|
-
b'crystal86@example.net,0.0,0,0.0,\r\ngraceperez@example.com,0.0,0,0.0,\r\n'
|
360
|
-
b'mackwilliam@example.com,0.0,0,0.0,\r\nsamanthaclarke@example.org,0.0,0,0.0,\r\n'
|
361
|
-
)
|
362
|
-
|
363
|
-
|
364
331
|
LEADERBOARD_RESPONSE = [
|
365
332
|
{
|
366
333
|
"email": "paul77@example.org",
|
367
|
-
"
|
368
|
-
"learning_time_seconds": 15753,
|
334
|
+
"sessions": 1,
|
369
335
|
"learning_time_hours": 4.4,
|
370
336
|
"average_session_length": 4.4,
|
371
337
|
"course_completions": None,
|
372
338
|
},
|
373
339
|
{
|
374
340
|
"email": "seth57@example.org",
|
375
|
-
"
|
376
|
-
"learning_time_seconds": 9898,
|
341
|
+
"sessions": 1,
|
377
342
|
"learning_time_hours": 2.7,
|
378
343
|
"average_session_length": 2.7,
|
379
344
|
"course_completions": None,
|
380
345
|
},
|
381
346
|
{
|
382
347
|
"email": "weaverpatricia@example.net",
|
383
|
-
"
|
384
|
-
"learning_time_seconds": 9441,
|
348
|
+
"sessions": 1,
|
385
349
|
"learning_time_hours": 2.6,
|
386
350
|
"average_session_length": 2.6,
|
387
351
|
"course_completions": None,
|
388
352
|
},
|
389
353
|
{
|
390
354
|
"email": "webertodd@example.com",
|
391
|
-
"
|
392
|
-
"learning_time_seconds": 5285,
|
355
|
+
"sessions": 1,
|
393
356
|
"learning_time_hours": 1.5,
|
394
357
|
"average_session_length": 1.5,
|
395
358
|
"course_completions": None,
|
396
359
|
},
|
397
360
|
{
|
398
361
|
"email": "yferguson@example.net",
|
399
|
-
"
|
400
|
-
"learning_time_seconds": 4747,
|
362
|
+
"sessions": 1,
|
401
363
|
"learning_time_hours": 1.3,
|
402
364
|
"average_session_length": 1.3,
|
403
365
|
"course_completions": None,
|
404
366
|
},
|
405
367
|
{
|
406
368
|
"email": "yallison@example.org",
|
407
|
-
"
|
408
|
-
"learning_time_seconds": 4335,
|
369
|
+
"sessions": 1,
|
409
370
|
"learning_time_hours": 1.2,
|
410
371
|
"average_session_length": 1.2,
|
411
372
|
"course_completions": None,
|
412
373
|
},
|
413
374
|
{
|
414
375
|
"email": "padillamichelle@example.org",
|
415
|
-
"
|
416
|
-
"learning_time_seconds": 3724,
|
376
|
+
"sessions": 1,
|
417
377
|
"learning_time_hours": 1.0,
|
418
378
|
"average_session_length": 1.0,
|
419
379
|
"course_completions": None,
|
420
380
|
},
|
421
381
|
{
|
422
382
|
"email": "caseyjohnny@example.com",
|
423
|
-
"
|
424
|
-
"learning_time_seconds": 0,
|
383
|
+
"sessions": 0,
|
425
384
|
"learning_time_hours": 0.0,
|
426
385
|
"average_session_length": 0.0,
|
427
386
|
"course_completions": None,
|
428
387
|
},
|
429
388
|
{
|
430
389
|
"email": "crystal86@example.net",
|
431
|
-
"
|
432
|
-
"learning_time_seconds": 0,
|
390
|
+
"sessions": 0,
|
433
391
|
"learning_time_hours": 0.0,
|
434
392
|
"average_session_length": 0.0,
|
435
393
|
"course_completions": None,
|
436
394
|
},
|
437
395
|
{
|
438
396
|
"email": "graceperez@example.com",
|
439
|
-
"
|
440
|
-
"learning_time_seconds": 21,
|
397
|
+
"sessions": 0,
|
441
398
|
"learning_time_hours": 0.0,
|
442
399
|
"average_session_length": 0.0,
|
443
400
|
"course_completions": None,
|
444
401
|
},
|
445
402
|
{
|
446
403
|
"email": "mackwilliam@example.com",
|
447
|
-
"
|
448
|
-
"learning_time_seconds": 0,
|
404
|
+
"sessions": 0,
|
449
405
|
"learning_time_hours": 0.0,
|
450
406
|
"average_session_length": 0.0,
|
451
407
|
"course_completions": None,
|
452
408
|
},
|
453
409
|
{
|
454
410
|
"email": "samanthaclarke@example.org",
|
455
|
-
"
|
456
|
-
"learning_time_seconds": 29,
|
411
|
+
"sessions": 0,
|
457
412
|
"learning_time_hours": 0.0,
|
458
413
|
"average_session_length": 0.0,
|
459
414
|
"course_completions": None,
|