edx-enterprise-data 8.5.0__tar.gz → 8.6.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.5.0 → edx_enterprise_data-8.6.0}/CHANGELOG.rst +4 -0
- {edx_enterprise_data-8.5.0/edx_enterprise_data.egg-info → edx_enterprise_data-8.6.0}/PKG-INFO +1 -1
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0/edx_enterprise_data.egg-info}/PKG-INFO +1 -1
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/edx_enterprise_data.egg-info/SOURCES.txt +5 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/__init__.py +1 -1
- edx_enterprise_data-8.6.0/enterprise_data/admin_analytics/constants.py +27 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/admin_analytics/utils.py +49 -0
- edx_enterprise_data-8.6.0/enterprise_data/api/v1/paginators.py +121 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/api/v1/serializers.py +90 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/api/v1/urls.py +14 -0
- edx_enterprise_data-8.6.0/enterprise_data/api/v1/views/analytics_enrollments.py +375 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/renderers.py +14 -0
- edx_enterprise_data-8.6.0/enterprise_data/tests/admin_analytics/mock_enrollments.py +169 -0
- edx_enterprise_data-8.6.0/enterprise_data/tests/admin_analytics/test_analytics_enrollments.py +393 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/LICENSE +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/MANIFEST.in +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/README.md +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/edx_enterprise_data.egg-info/dependency_links.txt +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/edx_enterprise_data.egg-info/not-zip-safe +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/edx_enterprise_data.egg-info/requires.txt +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/edx_enterprise_data.egg-info/top_level.txt +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/admin_analytics/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/admin_analytics/data_loaders.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/admin_analytics/database.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/api/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/api/urls.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/api/v0/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/api/v0/serializers.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/api/v0/urls.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/api/v0/views.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/api/v1/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/api/v1/views/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/api/v1/views/base.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/api/v1/views/enterprise_admin.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/api/v1/views/enterprise_learner.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/api/v1/views/enterprise_offers.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/apps.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/clients.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/constants.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/filters.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/fixtures/enterprise_enrollment.json +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/fixtures/enterprise_user.json +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/management/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/management/commands/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/management/commands/create_dummy_data.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/management/commands/create_dummy_data_lpr_v1.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/management/commands/create_enterprise_enrollment.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/management/commands/create_enterprise_learner_enrollment_lpr_v1.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/management/commands/create_enterprise_learner_lpr_v1.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/management/commands/create_enterprise_offer.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/management/commands/create_enterprise_user.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/management/commands/tests/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/management/commands/tests/test_create_dummy_data_lpr_v1.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/management/commands/tests/test_create_enterprise_enrollment.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/management/commands/tests/test_create_enterprise_learner_enrollment_lpr_v1.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/management/commands/tests/test_create_enterprise_learner_lpr_v1.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/management/commands/tests/test_create_enterprise_user.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0001_initial.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0002_auto_20180430_1358.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0003_auto_20180501_0603.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0004_auto_20180501_0928.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0004_auto_20180508_1652.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0005_auto_20180524_2204.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0006_auto_20180612_0336.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0007_auto_20180612_0534.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0008_auto_20180614_0108.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0009_auto_20180628_1152.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0010_enterpriseenrollment_created.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0011_enterpriseuser.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0012_auto_20180831_1930.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0013_auto_20180831_1931.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0014_enterpriseuser_created.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0015_auto_20180907_1757.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0016_auto_20180924_2138.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0017_enterpriseenrollment_unenrollment_timestamp.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0018_enterprisedatafeaturerole_enterprisedataroleassignment.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0019_add_enterprise_data_feature_roles.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0020_add_role_based_access_control_switch.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0021_auto_20190329_1241.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0022_remove_role_based_access_control_switch.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0023_enterpriselearner_enterpriselearnerenrollment.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0024_auto_20210602_1811.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0025_auto_20210703_1854.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0026_auto_20210916_0414.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0027_enterpriselearnerenrollment_total_learning_time_seconds.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0028_enterpriselearnerenrollment_offer_id.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0029_enterpriseoffer.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0030_auto_20230609_1353.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0031_auto_20230615_0705.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0032_auto_20230704_0818.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0033_enterpriseadminlearnerprogress_enterpriseadminsummarizeinsights.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0034_auto_20230907_0834.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0035_auto_20230907_1154.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0036_enterprisesubsidybudget_subsidy_access_policy_display_name.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0037_alter_enterpriseenrollment_consent_granted.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0038_enterpriseoffer_export_timestamp.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0039_auto_20240212_1403.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0040_auto_20240718_0536_squashed_0043_alter_enterpriselearnerenrollment_enterprise_enrollment_id.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/0044_enterpriseexecedlcmoduleperformance.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/migrations/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/models.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/paginators.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/settings/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/settings/test.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/signals.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/admin_analytics/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/admin_analytics/test_data_loaders.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/admin_analytics/test_utils.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/api/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/api/v0/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/api/v0/test_serializers.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/api/v1/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/api/v1/test_serializers.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/api/v1/test_views.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/api/v1/views/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/api/v1/views/test_enterprise_admin.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/factories.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/mixins.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/test_clients.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/test_filters.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/test_models.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/test_utils.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/tests/test_views.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/urls.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/utils.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data_roles/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data_roles/admin.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data_roles/apps.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data_roles/constants.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data_roles/migrations/0001_initial.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data_roles/migrations/0002_add_enterprise_data_feature_roles.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data_roles/migrations/0003_add_role_based_access_control_switch.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data_roles/migrations/0004_enterprisedataroleassignment_enterprise_id.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data_roles/migrations/0005_turn_on_role_based_access_control_switch.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data_roles/migrations/0006_remove_role_based_access_control_switch.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data_roles/migrations/0007_enterprisedataroleassignment_applies_to_all_contexts.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data_roles/migrations/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data_roles/models.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data_roles/rules.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data_roles/tests/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data_roles/tests/factories.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data_roles/tests/test_models.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/clients/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/clients/enterprise.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/clients/s3.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/clients/snowflake.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/clients/vertica.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/delivery_method.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/external_resource_link_report.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/fixtures/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/fixtures/enterprise_customer_reporting.json +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/reporter.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/send_enterprise_reports.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/tests/__init__.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/tests/test_clients.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/tests/test_delivery_method.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/tests/test_enterprise_client.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/tests/test_external_link_report.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/tests/test_reporter.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/tests/test_send_enterprise_reports.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/tests/test_utils.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/tests/test_vertica_client.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/tests/utils.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_reporting/utils.py +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/requirements/base.in +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/requirements/base.txt +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/requirements/ci.txt +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/requirements/common_constraints.txt +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/requirements/constraints.txt +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/requirements/dev.txt +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/requirements/django.txt +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/requirements/pip.txt +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/requirements/pip_tools.txt +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/requirements/quality.txt +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/requirements/reporting.in +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/requirements/test-master.txt +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/requirements/test-reporting.txt +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/requirements/test.txt +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/setup.cfg +0 -0
- {edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/setup.py +0 -0
@@ -16,6 +16,10 @@ Unreleased
|
|
16
16
|
|
17
17
|
=========================
|
18
18
|
|
19
|
+
[8.6.0] - 2024-08-12
|
20
|
+
---------------------
|
21
|
+
* Added API endpoints for advance analytics enrollments data.
|
22
|
+
|
19
23
|
[8.5.0] - 2024-08-12
|
20
24
|
---------------------
|
21
25
|
* Added a new model and REST endpoint to get Exec Ed LC Module Performance data.
|
{edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/edx_enterprise_data.egg-info/SOURCES.txt
RENAMED
@@ -21,6 +21,7 @@ enterprise_data/signals.py
|
|
21
21
|
enterprise_data/urls.py
|
22
22
|
enterprise_data/utils.py
|
23
23
|
enterprise_data/admin_analytics/__init__.py
|
24
|
+
enterprise_data/admin_analytics/constants.py
|
24
25
|
enterprise_data/admin_analytics/data_loaders.py
|
25
26
|
enterprise_data/admin_analytics/database.py
|
26
27
|
enterprise_data/admin_analytics/utils.py
|
@@ -31,9 +32,11 @@ enterprise_data/api/v0/serializers.py
|
|
31
32
|
enterprise_data/api/v0/urls.py
|
32
33
|
enterprise_data/api/v0/views.py
|
33
34
|
enterprise_data/api/v1/__init__.py
|
35
|
+
enterprise_data/api/v1/paginators.py
|
34
36
|
enterprise_data/api/v1/serializers.py
|
35
37
|
enterprise_data/api/v1/urls.py
|
36
38
|
enterprise_data/api/v1/views/__init__.py
|
39
|
+
enterprise_data/api/v1/views/analytics_enrollments.py
|
37
40
|
enterprise_data/api/v1/views/base.py
|
38
41
|
enterprise_data/api/v1/views/enterprise_admin.py
|
39
42
|
enterprise_data/api/v1/views/enterprise_learner.py
|
@@ -109,6 +112,8 @@ enterprise_data/tests/test_models.py
|
|
109
112
|
enterprise_data/tests/test_utils.py
|
110
113
|
enterprise_data/tests/test_views.py
|
111
114
|
enterprise_data/tests/admin_analytics/__init__.py
|
115
|
+
enterprise_data/tests/admin_analytics/mock_enrollments.py
|
116
|
+
enterprise_data/tests/admin_analytics/test_analytics_enrollments.py
|
112
117
|
enterprise_data/tests/admin_analytics/test_data_loaders.py
|
113
118
|
enterprise_data/tests/admin_analytics/test_utils.py
|
114
119
|
enterprise_data/tests/api/__init__.py
|
@@ -0,0 +1,27 @@
|
|
1
|
+
"""Advanced Analytics Constants"""
|
2
|
+
|
3
|
+
from enum import Enum
|
4
|
+
|
5
|
+
|
6
|
+
class GRANULARITY(Enum):
|
7
|
+
"""Granularity choices"""
|
8
|
+
DAILY = 'Daily'
|
9
|
+
WEEKLY = 'Weekly'
|
10
|
+
MONTHLY = 'Monthly'
|
11
|
+
QUARTERLY = 'Quarterly'
|
12
|
+
|
13
|
+
|
14
|
+
class CALCULATION(Enum):
|
15
|
+
"""Calculation choices"""
|
16
|
+
TOTAL = 'Total'
|
17
|
+
RUNNING_TOTAL = 'Running Total'
|
18
|
+
MOVING_AVERAGE_3_PERIOD = 'Moving Average (3 Period)'
|
19
|
+
MOVING_AVERAGE_7_PERIOD = 'Moving Average (7 Period)'
|
20
|
+
|
21
|
+
|
22
|
+
class ENROLLMENT_CSV(Enum):
|
23
|
+
"""CSV choices"""
|
24
|
+
ENROLLMENTS_OVER_TIME = 'enrollments_over_time'
|
25
|
+
TOP_COURSES_BY_ENROLLMENTS = 'top_courses_by_enrollments'
|
26
|
+
TOP_SUBJECTS_BY_ENROLLMENTS = 'top_subjects_by_enrollments'
|
27
|
+
INDIVIDUAL_ENROLLMENTS = 'individual_enrollments'
|
{edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/admin_analytics/utils.py
RENAMED
@@ -6,6 +6,7 @@ from enum import Enum
|
|
6
6
|
|
7
7
|
from edx_django_utils.cache import TieredCache, get_cache_key
|
8
8
|
|
9
|
+
from enterprise_data.admin_analytics.constants import CALCULATION, GRANULARITY
|
9
10
|
from enterprise_data.admin_analytics.data_loaders import fetch_engagement_data, fetch_enrollment_data, fetch_skills_data
|
10
11
|
from enterprise_data.utils import date_filter, primary_subject_truncate
|
11
12
|
|
@@ -19,6 +20,54 @@ class ChartType(Enum):
|
|
19
20
|
TOP_SKILLS_COMPLETION = 'top_skills_completion'
|
20
21
|
|
21
22
|
|
23
|
+
def granularity_aggregation(level, group, date, data_frame, aggregation_type="count"):
|
24
|
+
"""Aggregate data based on granularity"""
|
25
|
+
df = data_frame
|
26
|
+
|
27
|
+
period_mapping = {
|
28
|
+
GRANULARITY.WEEKLY.value: "W",
|
29
|
+
GRANULARITY.MONTHLY.value: "M",
|
30
|
+
GRANULARITY.QUARTERLY.value: "Q"
|
31
|
+
}
|
32
|
+
|
33
|
+
if level in period_mapping:
|
34
|
+
df[date] = df[date].dt.to_period(period_mapping[level]).dt.start_time
|
35
|
+
|
36
|
+
agg_column_name = "count"
|
37
|
+
if aggregation_type == "count":
|
38
|
+
df = df.groupby(group).size().reset_index()
|
39
|
+
elif aggregation_type == "sum":
|
40
|
+
df = df.groupby(group).sum().reset_index()
|
41
|
+
agg_column_name = "sum"
|
42
|
+
|
43
|
+
df.columns = group + [agg_column_name]
|
44
|
+
return df
|
45
|
+
|
46
|
+
|
47
|
+
def calculation_aggregation(calc, data_frame, aggregation_type="count"):
|
48
|
+
"""Aggregate data based on calculation"""
|
49
|
+
df = data_frame
|
50
|
+
|
51
|
+
window_mapping = {
|
52
|
+
CALCULATION.MOVING_AVERAGE_3_PERIOD.value: 3,
|
53
|
+
CALCULATION.MOVING_AVERAGE_7_PERIOD.value: 7,
|
54
|
+
}
|
55
|
+
|
56
|
+
aggregation_column = "count" if aggregation_type == "count" else "sum"
|
57
|
+
|
58
|
+
if calc == CALCULATION.RUNNING_TOTAL.value:
|
59
|
+
df[aggregation_column] = df.groupby("enroll_type")[aggregation_column].cumsum()
|
60
|
+
elif calc in [CALCULATION.MOVING_AVERAGE_3_PERIOD.value, CALCULATION.MOVING_AVERAGE_7_PERIOD.value]:
|
61
|
+
df[aggregation_column] = (
|
62
|
+
df.groupby("enroll_type")[aggregation_column]
|
63
|
+
.rolling(window_mapping[calc])
|
64
|
+
.mean()
|
65
|
+
.droplevel(level=[0])
|
66
|
+
)
|
67
|
+
|
68
|
+
return df
|
69
|
+
|
70
|
+
|
22
71
|
def get_cache_timeout(cache_expiry):
|
23
72
|
"""
|
24
73
|
Helper method to calculate cache timeout in seconds.
|
@@ -0,0 +1,121 @@
|
|
1
|
+
"""Custom paginator for the Advance Analytics API."""
|
2
|
+
|
3
|
+
import math
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from typing import Any
|
6
|
+
|
7
|
+
from rest_framework.exceptions import NotFound
|
8
|
+
from rest_framework.pagination import PageNumberPagination
|
9
|
+
from rest_framework.response import Response
|
10
|
+
|
11
|
+
|
12
|
+
@dataclass
|
13
|
+
class Page:
|
14
|
+
"""
|
15
|
+
A class representing a single page of paginated data.
|
16
|
+
|
17
|
+
Attributes:
|
18
|
+
data (Any): The data contained in the current page.
|
19
|
+
count (int): The total number of items across all pages.
|
20
|
+
num_pages (int): The total number of pages.
|
21
|
+
current_page (int): The current page number.
|
22
|
+
"""
|
23
|
+
data: Any
|
24
|
+
count: int
|
25
|
+
num_pages: int
|
26
|
+
current_page: int
|
27
|
+
|
28
|
+
def has_next(self):
|
29
|
+
"""
|
30
|
+
Check if there is a next page.
|
31
|
+
|
32
|
+
Returns:
|
33
|
+
bool: True if there is a next page, False otherwise.
|
34
|
+
"""
|
35
|
+
return self.current_page < self.num_pages
|
36
|
+
|
37
|
+
def has_previous(self):
|
38
|
+
"""
|
39
|
+
Check if there is a previous page.
|
40
|
+
|
41
|
+
Returns:
|
42
|
+
bool: True if there is a previous page, False otherwise.
|
43
|
+
"""
|
44
|
+
return self.current_page > 1
|
45
|
+
|
46
|
+
def next_page_number(self):
|
47
|
+
"""
|
48
|
+
Get the next page number.
|
49
|
+
|
50
|
+
Returns:
|
51
|
+
int: The next page number.
|
52
|
+
"""
|
53
|
+
return self.current_page + 1
|
54
|
+
|
55
|
+
def previous_page_number(self):
|
56
|
+
"""
|
57
|
+
Get the previous page number.
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
int: The previous page number.
|
61
|
+
"""
|
62
|
+
return self.current_page - 1
|
63
|
+
|
64
|
+
|
65
|
+
class AdvanceAnalyticsPagination(PageNumberPagination):
|
66
|
+
"""
|
67
|
+
Custom pagination class for advanced analytics.
|
68
|
+
|
69
|
+
Attributes:
|
70
|
+
page_size_query_param (str): The query parameter for the page size.
|
71
|
+
page_size (int): The default page size.
|
72
|
+
max_page_size (int): The maximum allowed page size.
|
73
|
+
"""
|
74
|
+
page_size_query_param = "page_size"
|
75
|
+
page_size = 10
|
76
|
+
max_page_size = 100
|
77
|
+
|
78
|
+
def paginate_queryset(self, queryset, request, view=None):
|
79
|
+
"""
|
80
|
+
Paginate a given dataframe based on the request parameters.
|
81
|
+
|
82
|
+
Args:
|
83
|
+
queryset (pd.DataFrame): The dataframe to paginate.
|
84
|
+
request (Request): The request object containing query parameters.
|
85
|
+
view (View, optional): The view that is calling the paginator.
|
86
|
+
|
87
|
+
Returns:
|
88
|
+
Page: A Page object. `data` attribute of the object will contain the paginated data.
|
89
|
+
"""
|
90
|
+
dataframe = queryset
|
91
|
+
|
92
|
+
self.request = request # pylint: disable=attribute-defined-outside-init
|
93
|
+
page_size = self.get_page_size(request)
|
94
|
+
if not page_size:
|
95
|
+
return None
|
96
|
+
|
97
|
+
total_rows = dataframe.shape[0]
|
98
|
+
num_pages = math.ceil(total_rows / page_size)
|
99
|
+
|
100
|
+
page_number = int(request.query_params.get(self.page_query_param) or 1)
|
101
|
+
if page_number <= 0 or page_number > num_pages:
|
102
|
+
raise NotFound('Invalid page.')
|
103
|
+
|
104
|
+
start_index = (page_number - 1) * page_size
|
105
|
+
end_index = min(start_index + page_size, total_rows)
|
106
|
+
data_frame_page = dataframe.iloc[start_index:end_index]
|
107
|
+
|
108
|
+
# pylint: disable=attribute-defined-outside-init
|
109
|
+
self.page = Page(data_frame_page, total_rows, num_pages, page_number)
|
110
|
+
|
111
|
+
return self.page
|
112
|
+
|
113
|
+
def get_paginated_response(self, data):
|
114
|
+
return Response({
|
115
|
+
'next': self.get_next_link(),
|
116
|
+
'previous': self.get_previous_link(),
|
117
|
+
'count': self.page.count,
|
118
|
+
'num_pages': self.page.num_pages,
|
119
|
+
'current_page': self.page.current_page,
|
120
|
+
'results': data
|
121
|
+
})
|
{edx_enterprise_data-8.5.0 → edx_enterprise_data-8.6.0}/enterprise_data/api/v1/serializers.py
RENAMED
@@ -5,6 +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 CALCULATION, ENROLLMENT_CSV, GRANULARITY
|
8
9
|
from enterprise_data.models import (
|
9
10
|
EnterpriseAdminLearnerProgress,
|
10
11
|
EnterpriseAdminSummarizeInsights,
|
@@ -227,3 +228,92 @@ class EnterpriseExecEdLCModulePerformanceSerializer(serializers.ModelSerializer)
|
|
227
228
|
class Meta:
|
228
229
|
model = EnterpriseExecEdLCModulePerformance
|
229
230
|
fields = '__all__'
|
231
|
+
|
232
|
+
|
233
|
+
class AdvanceAnalyticsQueryParamSerializer(serializers.Serializer): # pylint: disable=abstract-method
|
234
|
+
"""Serializer for validating query params"""
|
235
|
+
GRANULARITY_CHOICES = [
|
236
|
+
GRANULARITY.DAILY.value,
|
237
|
+
GRANULARITY.WEEKLY.value,
|
238
|
+
GRANULARITY.MONTHLY.value,
|
239
|
+
GRANULARITY.QUARTERLY.value
|
240
|
+
]
|
241
|
+
CALCULATION_CHOICES = [
|
242
|
+
CALCULATION.TOTAL.value,
|
243
|
+
CALCULATION.RUNNING_TOTAL.value,
|
244
|
+
CALCULATION.MOVING_AVERAGE_3_PERIOD.value,
|
245
|
+
CALCULATION.MOVING_AVERAGE_7_PERIOD.value
|
246
|
+
]
|
247
|
+
|
248
|
+
start_date = serializers.DateField(required=False)
|
249
|
+
end_date = serializers.DateField(required=False)
|
250
|
+
granularity = serializers.CharField(required=False)
|
251
|
+
calculation = serializers.CharField(required=False)
|
252
|
+
|
253
|
+
def validate(self, attrs):
|
254
|
+
"""
|
255
|
+
Validate the query params.
|
256
|
+
|
257
|
+
Raises:
|
258
|
+
serializers.ValidationError: If start_date is greater than end_date.
|
259
|
+
"""
|
260
|
+
start_date = attrs.get('start_date')
|
261
|
+
end_date = attrs.get('end_date')
|
262
|
+
|
263
|
+
if start_date and end_date and start_date > end_date:
|
264
|
+
raise serializers.ValidationError("start_date should be less than or equal to end_date.")
|
265
|
+
|
266
|
+
return attrs
|
267
|
+
|
268
|
+
def validate_granularity(self, value):
|
269
|
+
"""
|
270
|
+
Validate the granularity value.
|
271
|
+
|
272
|
+
Raises:
|
273
|
+
serializers.ValidationError: If granularity is not one of the valid choices.
|
274
|
+
"""
|
275
|
+
if value not in self.GRANULARITY_CHOICES:
|
276
|
+
raise serializers.ValidationError(f"Granularity must be one of {self.GRANULARITY_CHOICES}")
|
277
|
+
return value
|
278
|
+
|
279
|
+
def validate_calculation(self, value):
|
280
|
+
"""
|
281
|
+
Validate the calculation value.
|
282
|
+
|
283
|
+
Raises:
|
284
|
+
serializers.ValidationError: If calculation is not one of the valid choices
|
285
|
+
"""
|
286
|
+
if value not in self.CALCULATION_CHOICES:
|
287
|
+
raise serializers.ValidationError(f"Calculation must be one of {self.CALCULATION_CHOICES}")
|
288
|
+
return value
|
289
|
+
|
290
|
+
|
291
|
+
class AdvanceAnalyticsEnrollmentSerializer(AdvanceAnalyticsQueryParamSerializer): # pylint: disable=abstract-method
|
292
|
+
"""Serializer for validating Advance Analytics Enrollments API"""
|
293
|
+
CSV_TYPES = [
|
294
|
+
ENROLLMENT_CSV.INDIVIDUAL_ENROLLMENTS.value
|
295
|
+
]
|
296
|
+
|
297
|
+
csv_type = serializers.CharField(required=False)
|
298
|
+
|
299
|
+
def validate_csv_type(self, value):
|
300
|
+
"""
|
301
|
+
Validate the csv_type value.
|
302
|
+
|
303
|
+
Raises:
|
304
|
+
serializers.ValidationError: If csv_type is not one of the valid choices
|
305
|
+
"""
|
306
|
+
if value not in self.CSV_TYPES:
|
307
|
+
raise serializers.ValidationError(f"csv_type must be one of {self.CSV_TYPES}")
|
308
|
+
return value
|
309
|
+
|
310
|
+
|
311
|
+
class AdvanceAnalyticsEnrollmentStatsSerializer(
|
312
|
+
AdvanceAnalyticsEnrollmentSerializer
|
313
|
+
): # pylint: disable=abstract-method
|
314
|
+
"""Serializer for validating Advance Analytics Enrollments Stats API"""
|
315
|
+
CSV_TYPES = [
|
316
|
+
ENROLLMENT_CSV.ENROLLMENTS_OVER_TIME.value,
|
317
|
+
ENROLLMENT_CSV.TOP_COURSES_BY_ENROLLMENTS.value,
|
318
|
+
ENROLLMENT_CSV.TOP_SUBJECTS_BY_ENROLLMENTS.value
|
319
|
+
]
|
@@ -10,6 +10,10 @@ from django.urls import re_path
|
|
10
10
|
from enterprise_data.api.v1.views import enterprise_admin as enterprise_admin_views
|
11
11
|
from enterprise_data.api.v1.views import enterprise_learner as enterprise_learner_views
|
12
12
|
from enterprise_data.api.v1.views import enterprise_offers as enterprise_offers_views
|
13
|
+
from enterprise_data.api.v1.views.analytics_enrollments import (
|
14
|
+
AdvanceAnalyticsEnrollmentStatsView,
|
15
|
+
AdvanceAnalyticsIndividualEnrollmentsView,
|
16
|
+
)
|
13
17
|
from enterprise_data.constants import UUID4_REGEX
|
14
18
|
|
15
19
|
app_name = 'enterprise_data_api_v1'
|
@@ -52,6 +56,16 @@ urlpatterns = [
|
|
52
56
|
enterprise_admin_views.EnterpriseAdminAnalyticsAggregatesView.as_view(),
|
53
57
|
name='enterprise-admin-analytics-aggregates'
|
54
58
|
),
|
59
|
+
re_path(
|
60
|
+
fr'^admin/anlaytics/(?P<enterprise_uuid>{UUID4_REGEX})/enrollments/stats$',
|
61
|
+
AdvanceAnalyticsEnrollmentStatsView.as_view(),
|
62
|
+
name='enterprise-admin-analytics-enrollments-stats'
|
63
|
+
),
|
64
|
+
re_path(
|
65
|
+
fr'^admin/anlaytics/(?P<enterprise_uuid>{UUID4_REGEX})/enrollments$',
|
66
|
+
AdvanceAnalyticsIndividualEnrollmentsView.as_view(),
|
67
|
+
name='enterprise-admin-analytics-enrollments'
|
68
|
+
),
|
55
69
|
re_path(
|
56
70
|
fr'^admin/anlaytics/(?P<enterprise_id>{UUID4_REGEX})/skills/stats',
|
57
71
|
enterprise_admin_views.EnterpriseAdminAnalyticsSkillsView.as_view(),
|