From 9bbce1015fe5747eace02e1c4eb115f392aa90e2 Mon Sep 17 00:00:00 2001 From: Petr Stribny <petr@stribny.name> Date: Tue, 11 Mar 2025 11:22:03 +0000 Subject: [PATCH] Display single select values properly in chart group by single select field --- .../local_baserow/service_types.py | 28 ++++++++++++++++ ...grouped_aggregate_rows_data_source_type.py | 22 ++++++++++++- .../dashboard/chartFieldFormatting.js | 32 +++++++++++++++++++ .../dashboard/components/widget/Chart.vue | 21 ++++++++++-- .../modules/baserow_enterprise/plugin.js | 5 +++ 5 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 enterprise/web-frontend/modules/baserow_enterprise/dashboard/chartFieldFormatting.js diff --git a/enterprise/backend/src/baserow_enterprise/integrations/local_baserow/service_types.py b/enterprise/backend/src/baserow_enterprise/integrations/local_baserow/service_types.py index 513e6d75b..5085041d3 100644 --- a/enterprise/backend/src/baserow_enterprise/integrations/local_baserow/service_types.py +++ b/enterprise/backend/src/baserow_enterprise/integrations/local_baserow/service_types.py @@ -5,7 +5,9 @@ from django.db.models import F from rest_framework.exceptions import ValidationError as DRFValidationError +from baserow.contrib.database.api.fields.serializers import FieldSerializer from baserow.contrib.database.fields.exceptions import FieldTypeDoesNotExist +from baserow.contrib.database.fields.registries import field_type_registry from baserow.contrib.database.views.exceptions import AggregationTypeDoesNotExist from baserow.contrib.database.views.utils import AnnotatedAggregation from baserow.contrib.integrations.local_baserow.integration_types import ( @@ -471,6 +473,32 @@ class LocalBaserowGroupedAggregateRowsUserServiceType( return service + def get_context_data( + self, + service: LocalBaserowGroupedAggregateRows, + allowed_fields: list[str] | None = None, + ) -> dict: + context_data = {} + + if service.table: + model = service.table.get_model() + + def get_group_by_field(field): + return model.get_primary_field() if field is None else field + + def serialize_field(field): + return field_type_registry.get_serializer(field, FieldSerializer).data + + fields = [ + get_group_by_field(group_by.field) + for group_by in service.service_aggregation_group_bys.all() + ] + context_data["fields"] = { + field.db_column: serialize_field(field) for field in fields + } + + return context_data + def dispatch_data( self, service: LocalBaserowGroupedAggregateRows, diff --git a/enterprise/backend/tests/baserow_enterprise_tests/api/dashboard/test_grouped_aggregate_rows_data_source_type.py b/enterprise/backend/tests/baserow_enterprise_tests/api/dashboard/test_grouped_aggregate_rows_data_source_type.py index 53011f54d..1ff5c3f68 100644 --- a/enterprise/backend/tests/baserow_enterprise_tests/api/dashboard/test_grouped_aggregate_rows_data_source_type.py +++ b/enterprise/backend/tests/baserow_enterprise_tests/api/dashboard/test_grouped_aggregate_rows_data_source_type.py @@ -74,7 +74,27 @@ def test_grouped_aggregate_rows_get_dashboard_data_sources( {"aggregation_type": "sum", "field_id": field.id, "order": 1}, {"aggregation_type": "sum", "field_id": field_2.id, "order": 1}, ], - "context_data": {}, + "context_data": { + "fields": { + f"field_{field_3.id}": { + "description": None, + "id": field_3.id, + "immutable_properties": False, + "immutable_type": False, + "name": field_3.name, + "number_decimal_places": 0, + "number_negative": False, + "number_prefix": "", + "number_separator": "", + "number_suffix": "", + "order": 0, + "primary": False, + "read_only": False, + "table_id": table.id, + "type": "number", + }, + }, + }, "context_data_schema": None, "dashboard_id": dashboard.id, "filter_type": "AND", diff --git a/enterprise/web-frontend/modules/baserow_enterprise/dashboard/chartFieldFormatting.js b/enterprise/web-frontend/modules/baserow_enterprise/dashboard/chartFieldFormatting.js new file mode 100644 index 000000000..0248802c3 --- /dev/null +++ b/enterprise/web-frontend/modules/baserow_enterprise/dashboard/chartFieldFormatting.js @@ -0,0 +1,32 @@ +import { Registerable } from '@baserow/modules/core/registry' + +export class ChartFieldFormattingType extends Registerable { + constructor(...args) { + super(...args) + this.type = this.getType() + + if (this.type === null) { + throw new Error('The type has to be set.') + } + } + + formatGroupByFieldValue(field, value) { + return value ?? '' + } +} + +export class SingleSelectFormattingType extends ChartFieldFormattingType { + static getType() { + return 'single_select' + } + + formatGroupByFieldValue(field, value) { + const selectOption = field.select_options.find((item) => item.id === value) + + if (selectOption) { + return selectOption.value + } + + return value ?? '' + } +} diff --git a/enterprise/web-frontend/modules/baserow_enterprise/dashboard/components/widget/Chart.vue b/enterprise/web-frontend/modules/baserow_enterprise/dashboard/components/widget/Chart.vue index 22b194a05..63ecb1ab8 100644 --- a/enterprise/web-frontend/modules/baserow_enterprise/dashboard/components/widget/Chart.vue +++ b/enterprise/web-frontend/modules/baserow_enterprise/dashboard/components/widget/Chart.vue @@ -120,9 +120,9 @@ export default { ).find((item) => item.metadata?.primary === true) const labels = this.result.map((item) => { if (item[`field_${groupByFieldId}`] !== undefined) { - return item[`field_${groupByFieldId}`] ?? '' + return this.getGroupByValue(`field_${groupByFieldId}`, item) } - return item[`field_${primaryField.metadata.id}`] + return this.getGroupByValue(`field_${primaryField.metadata.id}`, item) }) const datasets = [] for (const [index, series] of this.seriesConfig.entries()) { @@ -212,6 +212,23 @@ export default { } return label }, + getGroupByValue(fieldName, item) { + const serializedField = this.dataSource.context_data.fields[fieldName] + const fieldType = serializedField.type + + if (this.$registry.exists('chartFieldFormatting', fieldType)) { + const fieldFormatter = this.$registry.get( + 'chartFieldFormatting', + fieldType + ) + return fieldFormatter.formatGroupByFieldValue( + serializedField, + item[fieldName] + ) + } + + return item[fieldName] ?? '' + }, }, } </script> diff --git a/enterprise/web-frontend/modules/baserow_enterprise/plugin.js b/enterprise/web-frontend/modules/baserow_enterprise/plugin.js index 3ee15aa9d..013dea214 100644 --- a/enterprise/web-frontend/modules/baserow_enterprise/plugin.js +++ b/enterprise/web-frontend/modules/baserow_enterprise/plugin.js @@ -51,6 +51,7 @@ import { HubspotContactsDataSyncType, } from '@baserow_enterprise/dataSyncTypes' import { ChartWidgetType } from '@baserow_enterprise/dashboard/widgetTypes' +import { SingleSelectFormattingType } from '@baserow_enterprise/dashboard/chartFieldFormatting' import { PeriodicIntervalFieldsConfigureDataSyncType } from '@baserow_enterprise/configureDataSyncTypes' import { CountViewAggregationType, @@ -308,5 +309,9 @@ export default (context) => { if (app.$featureFlagIsEnabled(FF_DASHBOARDS)) { app.$registry.register('dashboardWidget', new ChartWidgetType(context)) + app.$registry.register( + 'chartFieldFormatting', + new SingleSelectFormattingType(context) + ) } }