From d6796ce5d80226e4930c74d5d2cb1df6ef77732c Mon Sep 17 00:00:00 2001 From: rory sawyer Date: Fri, 5 Apr 2024 15:58:09 -0400 Subject: [PATCH 1/2] feat: add problem engagement dropoff chart to instructor dashboard --- ...blem_engagement_by_section_subsection.yaml | 68 +++++++++++ .../dashboards/Instructor_Dashboard.yaml | 57 ++++++++- .../datasets/fact_problem_engagement.yaml | 114 ++++++++++++++++++ .../queries/fact_problem_engagement.sql | 78 ++++++++++++ 4 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 tutoraspects/templates/aspects/build/aspects-superset/openedx-assets/assets/charts/Problem_engagement_by_section_subsection.yaml create mode 100644 tutoraspects/templates/aspects/build/aspects-superset/openedx-assets/assets/datasets/fact_problem_engagement.yaml create mode 100644 tutoraspects/templates/openedx-assets/queries/fact_problem_engagement.sql diff --git a/tutoraspects/templates/aspects/build/aspects-superset/openedx-assets/assets/charts/Problem_engagement_by_section_subsection.yaml b/tutoraspects/templates/aspects/build/aspects-superset/openedx-assets/assets/charts/Problem_engagement_by_section_subsection.yaml new file mode 100644 index 000000000..e4ee370cd --- /dev/null +++ b/tutoraspects/templates/aspects/build/aspects-superset/openedx-assets/assets/charts/Problem_engagement_by_section_subsection.yaml @@ -0,0 +1,68 @@ +_file_name: Problem_engagement_by_section_subsection.yaml +cache_timeout: null +certification_details: null +certified_by: null +dataset_uuid: 171c88e5-fd4b-4aab-b287-361fa6fa5415 +description: null +params: + adhoc_filters: [] + annotation_layers: [] + color_scheme: supersetColors + comparison_type: values + extra_form_data: {} + forecastInterval: 0.8 + forecastPeriods: 10 + groupby: [] + legendOrientation: top + legendType: scroll + metrics: + - aggregate: null + column: null + datasourceWarning: false + expressionType: SQL + hasCustomLabel: true + label: All problems attempted + optionName: metric_2q7yrcr33gf_xoor1p4yj4k + sqlExpression: |- + countIf("section/subsection problem engagement" = 'All problems attempted') + - aggregate: null + column: null + datasourceWarning: false + expressionType: SQL + hasCustomLabel: true + label: At least one problem attempted + optionName: metric_1ba5vqj4jds_vd7nek7dw9 + sqlExpression: |- + countIf("section/subsection problem engagement" = 'All problems attempted' or "section/subsection problem engagement" = 'At least one problem attempted') + only_total: true + order_desc: true + orientation: vertical + rich_tooltip: true + row_limit: 10000 + show_empty_columns: true + show_legend: true + sort_series_type: sum + time_grain_sqla: P1D + tooltipTimeFormat: smart_date + truncate_metric: true + viz_type: echarts_timeseries_bar + xAxisLabelRotation: 45 + x_axis: section/subsection name + x_axis_sort_asc: true + x_axis_sort_series: name + x_axis_sort_series_ascending: true + x_axis_time_format: smart_date + x_axis_title_margin: 15 + y_axis_bounds: + - null + - null + y_axis_format: SMART_NUMBER + y_axis_title: Number of learners + y_axis_title_margin: 30 + y_axis_title_position: Left +query_context: |- + {"datasource":{"id":537,"type":"table"},"force":false,"queries":[{"filters":[],"extras":{"having":"","where":""},"applied_time_extras":{},"columns":[{"timeGrain":"P1D","columnType":"BASE_AXIS","sqlExpression":"section/subsection name","label":"section/subsection name","expressionType":"SQL"}],"metrics":[{"expressionType":"SQL","sqlExpression":"countIf(\"section/subsection problem engagement\" = 'All problems attempted')","column":null,"aggregate":null,"datasourceWarning":false,"hasCustomLabel":true,"label":"All problems attempted","optionName":"metric_2q7yrcr33gf_xoor1p4yj4k"},{"expressionType":"SQL","sqlExpression":"countIf(\"section/subsection problem engagement\" = 'All problems attempted' or \"section/subsection problem engagement\" = 'At least one problem attempted')","column":null,"aggregate":null,"datasourceWarning":false,"hasCustomLabel":true,"label":"At least one problem attempted","optionName":"metric_1ba5vqj4jds_vd7nek7dw9"}],"orderby":[[{"expressionType":"SQL","sqlExpression":"countIf(\"section/subsection problem engagement\" = 'All problems attempted')","column":null,"aggregate":null,"datasourceWarning":false,"hasCustomLabel":true,"label":"All problems attempted","optionName":"metric_2q7yrcr33gf_xoor1p4yj4k"},false]],"annotation_layers":[],"row_limit":10000,"series_columns":[],"series_limit":0,"order_desc":true,"url_params":{},"custom_params":{},"custom_form_data":{},"time_offsets":[],"post_processing":[{"operation":"pivot","options":{"index":["section/subsection name"],"columns":[],"aggregates":{"All problems attempted":{"operator":"mean"},"At least one problem attempted":{"operator":"mean"}},"drop_missing_columns":false}},{"operation":"flatten"}]}],"form_data":{"datasource":"537__table","viz_type":"echarts_timeseries_bar","x_axis":"section/subsection name","time_grain_sqla":"P1D","x_axis_sort_asc":true,"x_axis_sort_series":"name","x_axis_sort_series_ascending":true,"metrics":[{"expressionType":"SQL","sqlExpression":"countIf(\"section/subsection problem engagement\" = 'All problems attempted')","column":null,"aggregate":null,"datasourceWarning":false,"hasCustomLabel":true,"label":"All problems attempted","optionName":"metric_2q7yrcr33gf_xoor1p4yj4k"},{"expressionType":"SQL","sqlExpression":"countIf(\"section/subsection problem engagement\" = 'All problems attempted' or \"section/subsection problem engagement\" = 'At least one problem attempted')","column":null,"aggregate":null,"datasourceWarning":false,"hasCustomLabel":true,"label":"At least one problem attempted","optionName":"metric_1ba5vqj4jds_vd7nek7dw9"}],"groupby":[],"adhoc_filters":[],"order_desc":true,"row_limit":10000,"truncate_metric":true,"show_empty_columns":true,"comparison_type":"values","annotation_layers":[],"forecastPeriods":10,"forecastInterval":0.8,"orientation":"vertical","x_axis_title_margin":15,"y_axis_title":"Number of learners","y_axis_title_margin":30,"y_axis_title_position":"Left","sort_series_type":"sum","color_scheme":"supersetColors","only_total":true,"show_legend":true,"legendType":"scroll","legendOrientation":"top","x_axis_time_format":"smart_date","xAxisLabelRotation":45,"y_axis_format":"SMART_NUMBER","y_axis_bounds":[null,null],"rich_tooltip":true,"tooltipTimeFormat":"smart_date","extra_form_data":{},"dashboards":[1642],"force":false,"result_format":"json","result_type":"full"},"result_format":"json","result_type":"full"} +slice_name: Problem engagement by section/subsection +uuid: 158b1b84-0784-4cc7-8e67-3ee3be7460c2 +version: 1.0.0 +viz_type: echarts_timeseries_bar diff --git a/tutoraspects/templates/aspects/build/aspects-superset/openedx-assets/assets/dashboards/Instructor_Dashboard.yaml b/tutoraspects/templates/aspects/build/aspects-superset/openedx-assets/assets/dashboards/Instructor_Dashboard.yaml index 5882176ac..dfba0fb17 100644 --- a/tutoraspects/templates/aspects/build/aspects-superset/openedx-assets/assets/dashboards/Instructor_Dashboard.yaml +++ b/tutoraspects/templates/aspects/build/aspects-superset/openedx-assets/assets/dashboards/Instructor_Dashboard.yaml @@ -24,6 +24,7 @@ metadata: - 770 - 783 - 816 + - 901 scope: global id: 114 '156': @@ -44,6 +45,7 @@ metadata: - 770 - 783 - 816 + - 901 scope: global id: 156 '325': @@ -64,6 +66,7 @@ metadata: - 770 - 783 - 816 + - 901 scope: global id: 325 '816': @@ -84,8 +87,30 @@ metadata: - 701 - 770 - 783 + - 901 scope: global id: 816 + '901': + crossFilters: + chartsInScope: + - 114 + - 154 + - 156 + - 229 + - 325 + - 327 + - 339 + - 516 + - 597 + - 625 + - 682 + - 695 + - 701 + - 770 + - 783 + - 816 + scope: global + id: 901 color_scheme: '' color_scheme_domain: [] cross_filters_enabled: false @@ -109,6 +134,7 @@ metadata: - 770 - 783 - 816 + - 901 scope: excluded: [] rootPath: @@ -353,7 +379,9 @@ metadata: datasetUuid: 417b2035-8fa1-4c60-a405-4b1947c3c966 type: NATIVE_FILTER refresh_frequency: 0 - shared_label_colors: {} + shared_label_colors: + audit: '#454E7C' + registered: '#1FA8C9' timed_refresh_immune_slices: [] position: CHART-AZZnl_lpMv: @@ -461,6 +489,21 @@ position: - TAB-7PGDduCA7 - ROW-BO2IHCiYF8 type: CHART + CHART-VOn-xTBbI5: + children: [] + id: CHART-VOn-xTBbI5 + meta: + chartId: 901 + height: 50 + sliceName: Problem engagement by section/subsection + uuid: 158b1b84-0784-4cc7-8e67-3ee3be7460c2 + width: 12 + parents: + - ROOT_ID + - TABS-SNeKAJcjhd + - TAB-NR4UTAs9K + - ROW-GWTTmZ2jDJ + type: CHART CHART-evjVO-ZSSd: children: [] id: CHART-evjVO-ZSSd @@ -780,6 +823,17 @@ position: - TABS-SNeKAJcjhd - TAB-pOd4znTAV type: ROW + ROW-GWTTmZ2jDJ: + children: + - CHART-VOn-xTBbI5 + id: ROW-GWTTmZ2jDJ + meta: + background: BACKGROUND_TRANSPARENT + parents: + - ROOT_ID + - TABS-SNeKAJcjhd + - TAB-NR4UTAs9K + type: ROW ROW-HPGIhOIs65: children: - CHART-jfm-20qPKn @@ -953,6 +1007,7 @@ position: - HEADER-v9QDHi2Dxm - ROW-87X0ypdHH_ - ROW-AG2xAOM044 + - ROW-GWTTmZ2jDJ - DIVIDER-G6rO0La8YT id: TAB-NR4UTAs9K meta: diff --git a/tutoraspects/templates/aspects/build/aspects-superset/openedx-assets/assets/datasets/fact_problem_engagement.yaml b/tutoraspects/templates/aspects/build/aspects-superset/openedx-assets/assets/datasets/fact_problem_engagement.yaml new file mode 100644 index 000000000..17a17178a --- /dev/null +++ b/tutoraspects/templates/aspects/build/aspects-superset/openedx-assets/assets/datasets/fact_problem_engagement.yaml @@ -0,0 +1,114 @@ +_file_name: fact_problem_engagement.yaml +cache_timeout: null +columns: +- advanced_data_type: null + column_name: section/subsection problem engagement + description: null + expression: null + extra: null + filterable: true + groupby: true + is_active: true + is_dttm: false + python_date_format: null + type: String + verbose_name: null +- advanced_data_type: null + column_name: section/subsection name + description: null + expression: null + extra: null + filterable: true + groupby: true + is_active: true + is_dttm: false + python_date_format: null + type: String + verbose_name: null +- advanced_data_type: null + column_name: content level + description: null + expression: null + extra: null + filterable: true + groupby: true + is_active: true + is_dttm: false + python_date_format: null + type: String + verbose_name: null +- advanced_data_type: null + column_name: actor_id + description: null + expression: null + extra: null + filterable: true + groupby: true + is_active: true + is_dttm: false + python_date_format: null + type: String + verbose_name: null +- advanced_data_type: null + column_name: course_key + description: null + expression: null + extra: null + filterable: true + groupby: true + is_active: true + is_dttm: false + python_date_format: null + type: String + verbose_name: null +- advanced_data_type: null + column_name: course_run + description: null + expression: null + extra: null + filterable: true + groupby: true + is_active: true + is_dttm: false + python_date_format: null + type: String + verbose_name: null +- advanced_data_type: null + column_name: org + description: null + expression: null + extra: null + filterable: true + groupby: true + is_active: true + is_dttm: false + python_date_format: null + type: String + verbose_name: null +database_uuid: 21174b6c-4d40-4958-8161-d6c3cf5e77b6 +default_endpoint: null +description: null +extra: null +fetch_values_predicate: null +filter_select_enabled: false +main_dttm_col: null +metrics: +- currency: null + d3format: null + description: null + expression: count(*) + extra: null + metric_name: count + metric_type: null + verbose_name: null + warning_text: null +normalize_columns: true +offset: 0 +params: null +schema: main +sql: |- + {% filter indent(width=2) %}{% include 'openedx-assets/queries/fact_problem_engagement.sql' %}{% endfilter %} +table_name: fact_problem_engagement +template_params: {} +uuid: 171c88e5-fd4b-4aab-b287-361fa6fa5415 +version: 1.0.0 diff --git a/tutoraspects/templates/openedx-assets/queries/fact_problem_engagement.sql b/tutoraspects/templates/openedx-assets/queries/fact_problem_engagement.sql new file mode 100644 index 000000000..83b5918f2 --- /dev/null +++ b/tutoraspects/templates/openedx-assets/queries/fact_problem_engagement.sql @@ -0,0 +1,78 @@ +with + subsection_counts as ( + select + org, + course_key, + course_run, + section_with_name, + subsection_with_name, + actor_id, + item_count, + count(distinct problem_id) as problems_attempted, + case + when problems_attempted = 0 + then 'No problems attempted yet' + when problems_attempted = item_count + then 'All problems attempted' + else 'At least one problem attempted' + end as engagement_level + from {{ DBT_PROFILE_TARGET_DATABASE }}.fact_problem_engagement + where + 1 = 1 + {% raw %} + {% if from_dttm is not none %} + and attempted_on > date('{{ from_dttm }}') + {% endif %} + {% if to_dttm is not none %} + and attempted_on < date('{{ to_dttm }}') + {% endif %} + {% endraw %} + {% include 'openedx-assets/queries/common_filters.sql' %} + group by + org, + course_key, + course_run, + section_with_name, + subsection_with_name, + actor_id, + item_count + ), + section_counts as ( + select + org, + course_key, + course_run, + section_with_name, + actor_id, + sum(item_count) as item_count, + sum(problems_attempted) as problems_attempted, + case + when problems_attempted = 0 + then 'No problems attempted yet' + when problems_attempted = item_count + then 'All problems attempted' + else 'At least one problem attempted' + end as engagement_level + from subsection_counts + group by org, course_key, course_run, section_with_name, actor_id + ) + +select + org, + course_key, + course_run, + subsection_with_name as `section/subsection name`, + 'subsection' as `content level`, + actor_id as actor_id, + engagement_level as `section/subsection problem engagement` +from subsection_counts +union all +select + org, + course_key, + course_run, + section_with_name as `section/subsection name`, + 'section' as `content level`, + actor_id as actor_id, + engagement_level as `section/subsection problem engagement` +from section_counts From 791e37291e5d315c76ae18a1cb2b64949738f70d Mon Sep 17 00:00:00 2001 From: rory sawyer Date: Fri, 5 Apr 2024 17:01:52 -0400 Subject: [PATCH 2/2] fix: bump aspects-dbt version --- tutoraspects/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutoraspects/plugin.py b/tutoraspects/plugin.py index 4583db6f8..145ea741d 100644 --- a/tutoraspects/plugin.py +++ b/tutoraspects/plugin.py @@ -379,7 +379,7 @@ # For now we are pulling this from github, which should allow maximum # flexibility for forking, running branches, specific versions, etc. ("DBT_REPOSITORY", "https://github.com/openedx/aspects-dbt"), - ("DBT_BRANCH", "v3.11.0"), + ("DBT_BRANCH", "v3.12.0"), ("DBT_SSH_KEY", ""), ("DBT_STATE_DIR", "/app/aspects/dbt_state/"), # This is a pip compliant list of Python packages to install to run dbt