diff --git a/backend/src/baserow/contrib/database/fields/field_types.py b/backend/src/baserow/contrib/database/fields/field_types.py index 6b34336aa..428c45efa 100755 --- a/backend/src/baserow/contrib/database/fields/field_types.py +++ b/backend/src/baserow/contrib/database/fields/field_types.py @@ -6008,7 +6008,10 @@ class LookupFieldType(FormulaFieldType): class MultipleCollaboratorsFieldType( - CollationSortMixin, ManyToManyFieldTypeSerializeToInputValueMixin, FieldType + CollationSortMixin, + ManyToManyFieldTypeSerializeToInputValueMixin, + ManyToManyGroupByMixin, + FieldType, ): type = "multiple_collaborators" model_class = MultipleCollaboratorsField @@ -6024,6 +6027,7 @@ class MultipleCollaboratorsFieldType( "notify_user_when_added": serializers.BooleanField(required=False), } is_many_to_many_field = True + _can_group_by = True def get_serializer_field(self, instance, **kwargs): required = kwargs.pop("required", False) diff --git a/backend/tests/baserow/contrib/database/api/views/test_view_serializers.py b/backend/tests/baserow/contrib/database/api/views/test_view_serializers.py index 69fe67241..3367cbc5f 100644 --- a/backend/tests/baserow/contrib/database/api/views/test_view_serializers.py +++ b/backend/tests/baserow/contrib/database/api/views/test_view_serializers.py @@ -65,7 +65,8 @@ def test_serialize_group_by_metadata(api_client, data_fixture): @pytest.mark.django_db def test_serialize_group_by_metadata_on_all_fields_in_interesting_table(data_fixture): - table, *_ = setup_interesting_test_table(data_fixture) + table, _, _, _, context = setup_interesting_test_table(data_fixture) + user2, user3 = context["user2"], context["user3"] model = table.get_model() queryset = model.objects.all() rows = list(queryset) @@ -284,6 +285,14 @@ def test_serialize_group_by_metadata_on_all_fields_in_interesting_table(data_fix {"field_decimal_link_row": [], "count": 1}, {"field_decimal_link_row": [1, 2, 3], "count": 1}, ], + "multiple_collaborators_link_row": [ + {"field_multiple_collaborators_link_row": [], "count": 1}, + {"field_multiple_collaborators_link_row": [1, 2], "count": 1}, + ], + "multiple_collaborators": [ + {"field_multiple_collaborators": [], "count": 1}, + {"field_multiple_collaborators": [user2.id, user3.id], "count": 1}, + ], } for key, actual_value in actual_result_per_field_name.items(): expected_value = expected_result.get(key, None) diff --git a/backend/tests/baserow/contrib/database/field/test_multiple_collaborators_field_type.py b/backend/tests/baserow/contrib/database/field/test_multiple_collaborators_field_type.py index c95be4666..4ab9542eb 100644 --- a/backend/tests/baserow/contrib/database/field/test_multiple_collaborators_field_type.py +++ b/backend/tests/baserow/contrib/database/field/test_multiple_collaborators_field_type.py @@ -5,9 +5,11 @@ from django.apps.registry import apps from django.contrib.auth import get_user_model from django.db import connection from django.test.utils import CaptureQueriesContext +from django.urls import reverse import pytest from faker import Faker +from pytest_unordered import unordered from baserow.contrib.database.fields.field_types import MultipleCollaboratorsFieldType from baserow.contrib.database.fields.handler import FieldHandler @@ -937,3 +939,164 @@ def test_multiple_collaborators_formula_field_cache_users_query(data_fixture): export_row(row) assert len(queries_for_all_others.captured_queries) == 0 + + +@pytest.mark.django_db +def test_get_group_by_metadata_in_rows_with_multiple_collaborators_field(data_fixture): + user = data_fixture.create_user(first_name="A") + user_2 = data_fixture.create_user(first_name="B") + workspace = data_fixture.create_workspace(members=[user, user_2]) + database = data_fixture.create_database_application(workspace=workspace) + table = data_fixture.create_database_table(database=database) + text_field = data_fixture.create_text_field( + table=table, order=0, name="Color", text_default="white" + ) + multiple_collaborators_field = data_fixture.create_multiple_collaborators_field( + table=table + ) + + RowHandler().force_create_rows( + user=user, + table=table, + rows_values=[ + { + f"field_{text_field.id}": "Row 1", + f"field_{multiple_collaborators_field.id}": [], + }, + { + f"field_{text_field.id}": "Row 2", + f"field_{multiple_collaborators_field.id}": [], + }, + { + f"field_{text_field.id}": "Row 3", + f"field_{multiple_collaborators_field.id}": [{"id": user.id}], + }, + { + f"field_{text_field.id}": "Row 4", + f"field_{multiple_collaborators_field.id}": [{"id": user.id}], + }, + { + f"field_{text_field.id}": "Row 5", + f"field_{multiple_collaborators_field.id}": [{"id": user_2.id}], + }, + { + f"field_{text_field.id}": "Row 6", + f"field_{multiple_collaborators_field.id}": [{"id": user_2.id}], + }, + { + f"field_{text_field.id}": "Row 7", + f"field_{multiple_collaborators_field.id}": [ + {"id": user.id}, + {"id": user_2.id}, + ], + }, + { + f"field_{text_field.id}": "Row 8", + f"field_{multiple_collaborators_field.id}": [ + {"id": user.id}, + {"id": user_2.id}, + ], + }, + { + f"field_{text_field.id}": "Row 9", + f"field_{multiple_collaborators_field.id}": [ + {"id": user_2.id}, + {"id": user.id}, + ], + }, + ], + ) + + model = table.get_model() + + queryset = model.objects.all().enhance_by_fields() + rows = list(queryset) + + handler = ViewHandler() + counts = handler.get_group_by_metadata_in_rows( + [multiple_collaborators_field], rows, queryset + ) + + # Resolve the queryset, so that we can do a comparison. + for c in counts.keys(): + counts[c] = list(counts[c]) + + assert counts == { + multiple_collaborators_field: unordered( + [ + {"count": 2, f"field_{multiple_collaborators_field.id}": []}, + { + "count": 2, + f"field_{multiple_collaborators_field.id}": [user.id], + }, + { + "count": 2, + f"field_{multiple_collaborators_field.id}": [ + user.id, + user_2.id, + ], + }, + { + "count": 2, + f"field_{multiple_collaborators_field.id}": [user_2.id], + }, + { + "count": 1, + f"field_{multiple_collaborators_field.id}": [ + user_2.id, + user.id, + ], + }, + ] + ) + } + + +@pytest.mark.django_db +def test_list_rows_with_group_by_and_multiple_collaborators_field( + api_client, data_fixture +): + user, token = data_fixture.create_user_and_token( + email="test@test.nl", password="password", first_name="A" + ) + user_2 = data_fixture.create_user(first_name="B") + user_3 = data_fixture.create_user(first_name="C") + workspace = data_fixture.create_workspace(members=[user, user_2, user_3]) + database = data_fixture.create_database_application(workspace=workspace) + table = data_fixture.create_database_table(database=database) + multiple_collaborators_field = data_fixture.create_multiple_collaborators_field( + table=table + ) + grid = data_fixture.create_grid_view(table=table) + data_fixture.create_view_group_by(view=grid, field=multiple_collaborators_field) + + RowHandler().create_row( + user=user, + table=table, + values={ + f"field_{multiple_collaborators_field.id}": [ + {"id": user.id}, + {"id": user_2.id}, + {"id": user_3.id}, + ], + }, + ) + + url = reverse("api:database:views:grid:list", kwargs={"view_id": grid.id}) + response = api_client.get(url, **{"HTTP_AUTHORIZATION": f"JWT {token}"}) + response_json = response.json() + + assert response_json["group_by_metadata"] == { + f"field_{multiple_collaborators_field.id}": unordered( + [ + { + f"field_{multiple_collaborators_field.id}": [ + user.id, + user_2.id, + user_3.id, + ], + "count": 1, + }, + ] + ), + } diff --git a/changelog/entries/unreleased/feature/3448_multiple_collaborator_group_by_support.json b/changelog/entries/unreleased/feature/3448_multiple_collaborator_group_by_support.json new file mode 100644 index 000000000..936d9de1f --- /dev/null +++ b/changelog/entries/unreleased/feature/3448_multiple_collaborator_group_by_support.json @@ -0,0 +1,7 @@ +{ + "type": "feature", + "message": "Multiple collaborator field group by support.", + "issue_number": 3447, + "bullet_points": [], + "created_at": "2025-02-25" +} diff --git a/web-frontend/modules/database/fieldTypes.js b/web-frontend/modules/database/fieldTypes.js index 576d4cd08..4281fb9e6 100644 --- a/web-frontend/modules/database/fieldTypes.js +++ b/web-frontend/modules/database/fieldTypes.js @@ -4335,6 +4335,27 @@ export class MultipleCollaboratorsFieldType extends FieldType { canBeReferencedByFormulaField() { return true } + + getCanGroupByInView(field) { + return true + } + + getRowValueFromGroupValue(field, value) { + return value.map((optId) => { + return { id: optId } + }) + } + + getGroupValueFromRowValue(field, value) { + return value && value.map((o) => o.id) + } + + isEqual(field, value1, value2) { + const value1Ids = value1.map((v) => v.id) + const value2Ids = value2.map((v) => v.id) + + return _.isEqual(value1Ids, value2Ids) + } } export class UUIDFieldType extends FieldType {