Skip to content

Commit 9ab3cd3

Browse files
committed
ability for dashboard filters in table chart views
1 parent f6896c8 commit 9ab3cd3

File tree

2 files changed

+240
-13
lines changed

2 files changed

+240
-13
lines changed

ddpui/api/charts_api.py

Lines changed: 147 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99
from ninja import Router, Schema, Field
1010
from ninja.errors import HttpError
1111
from django.shortcuts import get_object_or_404
12-
from django.http import HttpResponse, StreamingHttpResponse
12+
from django.http import StreamingHttpResponse
1313

1414
from ddpui.auth import has_permission
1515
from ddpui.models.org_user import OrgUser
1616
from ddpui.models.org import OrgWarehouse
17+
from ddpui.models.dashboard import DashboardFilter
1718
from ddpui.models.visualization import Chart
1819
from ddpui.core.charts import charts_service
1920
from ddpui.core.charts.echarts_config_generator import EChartsConfigGenerator
@@ -571,8 +572,16 @@ def get_chart_data(request, payload: ChartDataPayload):
571572

572573
@charts_router.post("/chart-data-preview/", response=DataPreviewResponse)
573574
@has_permission(["can_view_charts"])
574-
def get_chart_data_preview(request, payload: ChartDataPayload, page: int = 0, limit: int = 100):
575+
def get_chart_data_preview(
576+
request,
577+
payload: ChartDataPayload,
578+
page: int = 0,
579+
limit: int = 100,
580+
dashboard_filters: Optional[str] = None,
581+
):
575582
"""Get paginated data preview for chart using the same query as chart data"""
583+
import json
584+
576585
orguser = request.orguser
577586

578587
# Validate user has access to schema/table
@@ -583,9 +592,73 @@ def get_chart_data_preview(request, payload: ChartDataPayload, page: int = 0, li
583592
if not org_warehouse:
584593
raise HttpError(404, "Warehouse not configured")
585594

595+
# Parse and resolve dashboard filters if provided (same logic as chart data endpoint)
596+
resolved_dashboard_filters = None
597+
if dashboard_filters:
598+
try:
599+
filter_values = json.loads(dashboard_filters)
600+
logger.info(f"Applying dashboard filters to chart data preview: {filter_values}")
601+
602+
# Resolve filter configurations to get column information
603+
604+
# Get warehouse client to check column existence
605+
warehouse_client = WarehouseFactory.get_warehouse_client(org_warehouse)
606+
resolved_filters = []
607+
608+
for filter_id, filter_value in filter_values.items():
609+
if filter_value is not None:
610+
try:
611+
# Get the filter configuration from the database
612+
dashboard_filter = DashboardFilter.objects.get(id=filter_id)
613+
614+
# Only apply this filter if it applies to the same table as the chart
615+
if warehouse_client.column_exists(
616+
payload.schema_name, payload.table_name, dashboard_filter.column_name
617+
):
618+
resolved_filters.append(
619+
{
620+
"filter_id": filter_id,
621+
"column": dashboard_filter.column_name,
622+
"type": dashboard_filter.filter_type,
623+
"value": filter_value,
624+
"settings": dashboard_filter.settings,
625+
}
626+
)
627+
except DashboardFilter.DoesNotExist:
628+
logger.warning(f"Dashboard filter {filter_id} not found")
629+
630+
resolved_dashboard_filters = resolved_filters
631+
632+
except json.JSONDecodeError:
633+
logger.error(f"Invalid dashboard_filters JSON: {dashboard_filters}")
634+
resolved_dashboard_filters = None
635+
636+
# Create a modified payload with dashboard filters
637+
modified_payload = ChartDataPayload(
638+
chart_type=payload.chart_type,
639+
computation_type=payload.computation_type,
640+
schema_name=payload.schema_name,
641+
table_name=payload.table_name,
642+
x_axis=payload.x_axis,
643+
y_axis=payload.y_axis,
644+
dimension_col=payload.dimension_col,
645+
extra_dimension=payload.extra_dimension,
646+
metrics=payload.metrics,
647+
geographic_column=payload.geographic_column,
648+
value_column=payload.value_column,
649+
selected_geojson_id=payload.selected_geojson_id,
650+
customizations=payload.customizations,
651+
offset=payload.offset,
652+
limit=payload.limit,
653+
extra_config=payload.extra_config,
654+
dashboard_filters=resolved_dashboard_filters, # Add resolved dashboard filters
655+
)
656+
586657
# Get table preview using the same query builder as chart data
587658
# This ensures preview shows exactly what will be used for the chart
588-
preview_data = charts_service.get_chart_data_table_preview(org_warehouse, payload, page, limit)
659+
preview_data = charts_service.get_chart_data_table_preview(
660+
org_warehouse, modified_payload, page, limit
661+
)
589662

590663
return DataPreviewResponse(
591664
columns=preview_data["columns"],
@@ -598,8 +671,12 @@ def get_chart_data_preview(request, payload: ChartDataPayload, page: int = 0, li
598671

599672
@charts_router.post("/chart-data-preview/total-rows/", response=int)
600673
@has_permission(["can_view_charts"])
601-
def get_chart_data_preview_total_rows(request, payload: ChartDataPayload):
674+
def get_chart_data_preview_total_rows(
675+
request, payload: ChartDataPayload, dashboard_filters: Optional[str] = None
676+
):
602677
"""Get total rows for chart data preview"""
678+
import json
679+
603680
orguser = request.orguser
604681

605682
# Validate user has access to schema/table
@@ -610,8 +687,73 @@ def get_chart_data_preview_total_rows(request, payload: ChartDataPayload):
610687
if not org_warehouse:
611688
raise HttpError(404, "Warehouse not configured")
612689

690+
# Parse and resolve dashboard filters if provided (same logic as chart data endpoint)
691+
resolved_dashboard_filters = None
692+
if dashboard_filters:
693+
try:
694+
filter_values = json.loads(dashboard_filters)
695+
logger.info(
696+
f"Applying dashboard filters to chart data preview total rows: {filter_values}"
697+
)
698+
699+
# Resolve filter configurations to get column information
700+
from ddpui.models.dashboard import DashboardFilter
701+
702+
# Get warehouse client to check column existence
703+
warehouse_client = WarehouseFactory.get_warehouse_client(org_warehouse)
704+
resolved_filters = []
705+
706+
for filter_id, filter_value in filter_values.items():
707+
if filter_value is not None:
708+
try:
709+
# Get the filter configuration from the database
710+
dashboard_filter = DashboardFilter.objects.get(id=filter_id)
711+
712+
# Only apply this filter if it applies to the same table as the chart
713+
if warehouse_client.column_exists(
714+
payload.schema_name, payload.table_name, dashboard_filter.column_name
715+
):
716+
resolved_filters.append(
717+
{
718+
"filter_id": filter_id,
719+
"column": dashboard_filter.column_name,
720+
"type": dashboard_filter.filter_type,
721+
"value": filter_value,
722+
"settings": dashboard_filter.settings,
723+
}
724+
)
725+
except DashboardFilter.DoesNotExist:
726+
logger.warning(f"Dashboard filter {filter_id} not found")
727+
728+
resolved_dashboard_filters = resolved_filters
729+
730+
except json.JSONDecodeError:
731+
logger.error(f"Invalid dashboard_filters JSON: {dashboard_filters}")
732+
resolved_dashboard_filters = None
733+
734+
# Create a modified payload with dashboard filters
735+
modified_payload = ChartDataPayload(
736+
chart_type=payload.chart_type,
737+
computation_type=payload.computation_type,
738+
schema_name=payload.schema_name,
739+
table_name=payload.table_name,
740+
x_axis=payload.x_axis,
741+
y_axis=payload.y_axis,
742+
dimension_col=payload.dimension_col,
743+
extra_dimension=payload.extra_dimension,
744+
metrics=payload.metrics,
745+
geographic_column=payload.geographic_column,
746+
value_column=payload.value_column,
747+
selected_geojson_id=payload.selected_geojson_id,
748+
customizations=payload.customizations,
749+
offset=payload.offset,
750+
limit=payload.limit,
751+
extra_config=payload.extra_config,
752+
dashboard_filters=resolved_dashboard_filters, # Add resolved dashboard filters
753+
)
754+
613755
# Get total rows using the same query builder as chart data
614-
total_rows = charts_service.get_chart_data_total_rows(org_warehouse, payload)
756+
total_rows = charts_service.get_chart_data_total_rows(org_warehouse, modified_payload)
615757

616758
return total_rows
617759

ddpui/api/public_api.py

Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from ninja.errors import HttpError
1313

1414
from ddpui.datainsights.warehouse.warehouse_factory import WarehouseFactory
15-
from ddpui.models.dashboard import Dashboard
15+
from ddpui.models.dashboard import Dashboard, DashboardFilter
1616
from ddpui.utils.custom_logger import CustomLogger
1717

1818
from ddpui.models.visualization import Chart
@@ -236,8 +236,6 @@ def get_public_chart_data(request, token: str, chart_id: int):
236236
resolved_dashboard_filters = None
237237
if filters:
238238
# Resolve filter configurations to get column information - same as authenticated API
239-
from ddpui.models.dashboard import DashboardFilter
240-
241239
resolved_filters = []
242240

243241
for filter_id, value in filters.items():
@@ -487,7 +485,12 @@ def validate_public_dashboard(request, token: str):
487485
response={200: dict, 404: PublicErrorResponse},
488486
)
489487
def get_public_chart_data_preview(
490-
request, token: str, chart_id: int, page: int = 0, limit: int = 100
488+
request,
489+
token: str,
490+
chart_id: int,
491+
page: int = 0,
492+
limit: int = 100,
493+
dashboard_filters: Optional[str] = None,
491494
):
492495
"""
493496
Get public chart data preview - ESSENTIAL for table charts
@@ -523,8 +526,45 @@ def get_public_chart_data_preview(
523526

524527
payload = json.loads(request.body) if request.body else {}
525528

526-
# Convert payload to ChartDataPayload
527-
chart_payload = ChartDataPayload(**payload)
529+
# Parse and resolve dashboard filters if provided (same logic as public chart data endpoint)
530+
resolved_dashboard_filters = None
531+
if dashboard_filters:
532+
# Resolve filter configurations to get column information
533+
filter_values = json.loads(dashboard_filters)
534+
535+
warehouse_client = WarehouseFactory.get_warehouse_client(org_warehouse)
536+
resolved_filters = []
537+
538+
for filter_id, filter_value in filter_values.items():
539+
if filter_value is not None:
540+
try:
541+
# Get the filter configuration from the database
542+
dashboard_filter = DashboardFilter.objects.get(id=filter_id)
543+
544+
# Only apply this filter if it applies to the same table as the chart
545+
# Use warehouse client to check column existence
546+
if warehouse_client.column_exists(
547+
chart.schema_name, chart.table_name, dashboard_filter.column_name
548+
):
549+
resolved_filters.append(
550+
{
551+
"filter_id": filter_id,
552+
"column": dashboard_filter.column_name,
553+
"type": dashboard_filter.filter_type,
554+
"value": filter_value,
555+
"settings": dashboard_filter.settings,
556+
}
557+
)
558+
except (DashboardFilter.DoesNotExist, ValueError):
559+
logger.warning(f"Public API: Dashboard filter {filter_id} not found")
560+
561+
resolved_dashboard_filters = resolved_filters
562+
563+
# Convert payload to ChartDataPayload with resolved dashboard filters
564+
chart_payload = ChartDataPayload(
565+
**payload,
566+
)
567+
chart_payload.dashboard_filters = resolved_dashboard_filters
528568

529569
# Get table preview using same function as authenticated API with pagination
530570
preview_data = charts_service.get_chart_data_table_preview(
@@ -975,6 +1015,8 @@ def get_public_chart_data_preview_total_rows(request, token: str, chart_id: int)
9751015
Get total row count for public chart data preview
9761016
This is essential for proper pagination in table charts
9771017
"""
1018+
import json
1019+
9781020
try:
9791021
# Verify dashboard is public
9801022
dashboard = Dashboard.objects.get(public_share_token=token, is_public=True)
@@ -988,13 +1030,56 @@ def get_public_chart_data_preview_total_rows(request, token: str, chart_id: int)
9881030
if not org_warehouse:
9891031
raise Exception("No warehouse configured for organization")
9901032

991-
# Get payload from request body
992-
import json
1033+
# Get dashboard filters from query params (same logic as public chart data endpoint)
1034+
filters = {}
1035+
filters_param = request.GET.get("dashboard_filters")
1036+
if filters_param:
1037+
try:
1038+
filters = json.loads(filters_param)
1039+
except json.JSONDecodeError:
1040+
logger.warning(f"Invalid dashboard filters JSON: {filters_param}")
9931041

9941042
payload = json.loads(request.body) if request.body else {}
9951043

1044+
# Parse and resolve dashboard filters if provided (same logic as public chart data endpoint)
1045+
resolved_dashboard_filters = None
1046+
if filters:
1047+
# Resolve filter configurations to get column information
1048+
from ddpui.models.dashboard import DashboardFilter
1049+
1050+
resolved_filters = []
1051+
1052+
for filter_id, filter_value in filters.items():
1053+
if filter_value is not None:
1054+
try:
1055+
# Get the filter configuration from the database
1056+
dashboard_filter = DashboardFilter.objects.get(id=filter_id)
1057+
1058+
# Only apply this filter if it applies to the same table as the chart
1059+
# Use warehouse client to check column existence
1060+
warehouse_client = WarehouseFactory.get_warehouse_client(org_warehouse)
1061+
if warehouse_client.column_exists(
1062+
chart.schema_name, chart.table_name, dashboard_filter.column_name
1063+
):
1064+
resolved_filters.append(
1065+
{
1066+
"filter_id": filter_id,
1067+
"column": dashboard_filter.column_name,
1068+
"type": dashboard_filter.filter_type,
1069+
"value": filter_value,
1070+
"settings": dashboard_filter.settings,
1071+
}
1072+
)
1073+
except (DashboardFilter.DoesNotExist, ValueError):
1074+
logger.warning(f"Public API: Dashboard filter {filter_id} not found")
1075+
1076+
resolved_dashboard_filters = resolved_filters
1077+
9961078
# Convert payload to ChartDataPayload
9971079
chart_payload = ChartDataPayload(**payload)
1080+
chart_payload.dashboard_filters = (
1081+
resolved_dashboard_filters # Add resolved dashboard filters
1082+
)
9981083

9991084
# Get total rows using same function as authenticated API
10001085
total_rows = charts_service.get_chart_data_total_rows(org_warehouse, chart_payload)

0 commit comments

Comments
 (0)