1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-08 06:40:07 +00:00

Resolve "Basic filters for last modified by field"

This commit is contained in:
Petr Stribny 2023-11-24 13:35:37 +00:00
parent 46c91136bc
commit 2b7007817c
8 changed files with 328 additions and 10 deletions
backend
src/baserow/contrib/database
tests/baserow/contrib/database/view
changelog/entries/unreleased/feature
web-frontend
modules/database
test/unit/database

View file

@ -334,6 +334,8 @@ class DatabaseConfig(AppConfig):
NotEqualViewFilterType,
SingleSelectEqualViewFilterType,
SingleSelectNotEqualViewFilterType,
UserIsNotViewFilterType,
UserIsViewFilterType,
)
view_filter_type_registry.register(EqualViewFilterType())
@ -382,6 +384,8 @@ class DatabaseConfig(AppConfig):
view_filter_type_registry.register(MultipleSelectHasNotViewFilterType())
view_filter_type_registry.register(MultipleCollaboratorsHasViewFilterType())
view_filter_type_registry.register(MultipleCollaboratorsHasNotViewFilterType())
view_filter_type_registry.register(UserIsViewFilterType())
view_filter_type_registry.register(UserIsNotViewFilterType())
from .views.view_aggregations import (
AverageViewAggregationType,

View file

@ -26,6 +26,7 @@ from baserow.contrib.database.fields.field_types import (
EmailFieldType,
FileFieldType,
FormulaFieldType,
LastModifiedByFieldType,
LastModifiedFieldType,
LinkRowFieldType,
LongTextFieldType,
@ -1297,6 +1298,66 @@ class MultipleCollaboratorsHasNotViewFilterType(
type = "multiple_collaborators_has_not"
class UserIsViewFilterType(ViewFilterType):
"""
The user is filter accepts the ID of the user to filter for
and filters the rows where the field has the provided user.
"""
type = "user_is"
compatible_field_types = [LastModifiedByFieldType.type]
USER_KEY = f"users"
def get_filter(self, field_name, value, model_field, field):
value = value.strip()
return Q(**{f"{field_name}__id": int(value)})
def get_export_serialized_value(self, value, id_mapping):
if self.USER_KEY not in id_mapping:
workspace_id = id_mapping.get("workspace_id", None)
if workspace_id is None:
return value
id_mapping[self.USER_KEY] = defaultdict(list)
workspaceusers_from_workspace = WorkspaceUser.objects.filter(
workspace_id=workspace_id
).select_related("user")
for workspaceuser in workspaceusers_from_workspace:
id_mapping[self.USER_KEY][
str(workspaceuser.user.id)
] = workspaceuser.user.email
return id_mapping[self.USER_KEY].get(value, "")
def set_import_serialized_value(self, value, id_mapping):
workspace_id = id_mapping.get("workspace_id", None)
if workspace_id is None:
return ""
if self.USER_KEY not in id_mapping:
id_mapping[self.USER_KEY] = defaultdict(list)
workspaceusers_from_workspace = WorkspaceUser.objects.filter(
workspace_id=workspace_id
).select_related("user")
for workspaceuser in workspaceusers_from_workspace:
id_mapping[self.USER_KEY][str(workspaceuser.user.email)] = str(
workspaceuser.user.id
)
return id_mapping[self.USER_KEY].get(value, "")
class UserIsNotViewFilterType(NotViewFilterTypeMixin, UserIsViewFilterType):
"""
The user is not filter accepts the ID of the user to filter for
and filters the rows where the field does not have the provided user.
"""
type = "user_is_not"
class EmptyViewFilterType(ViewFilterType):
"""
The empty filter checks if the field value is empty, this can be '', null,

View file

@ -3182,7 +3182,6 @@ def test_empty_filter_type(data_fixture):
populated_date_interval_formula_field = data_fixture.create_formula_field(
table=table, formula="date_interval('1 year')"
)
multiple_select_field = data_fixture.create_multiple_select_field(table=table)
option_2 = data_fixture.create_select_option(field=multiple_select_field)
option_3 = data_fixture.create_select_option(field=multiple_select_field)
@ -3337,7 +3336,6 @@ def test_not_empty_filter_type(data_fixture):
populated_date_interval_formula_field = data_fixture.create_formula_field(
table=table, formula="date_interval('1 year')"
)
multiple_select_field = data_fixture.create_multiple_select_field(table=table)
option_2 = data_fixture.create_select_option(field=multiple_select_field)
option_3 = data_fixture.create_select_option(field=multiple_select_field)
@ -6216,3 +6214,103 @@ def test_date_after_days_ago_filter_type(data_fixture):
filter.save()
rows = apply_filter()
assert rows == []
@pytest.mark.field_last_modified_by
@pytest.mark.django_db
def test_user_is_filter_type(data_fixture):
user = data_fixture.create_user()
user2 = data_fixture.create_user()
table = data_fixture.create_database_table(user=user)
grid_view = data_fixture.create_grid_view(table=table)
last_modified_by_field = data_fixture.create_last_modified_by_field(table=table)
handler = ViewHandler()
model = table.get_model()
row = model.objects.create(
**{
f"last_modified_by": user,
}
)
row_2 = model.objects.create(
**{
f"last_modified_by": user2,
}
)
row_3 = model.objects.create(
**{
f"last_modified_by": None,
}
)
view_filter = data_fixture.create_view_filter(
view=grid_view,
field=last_modified_by_field,
type="user_is",
value=user.id,
)
ids = [r.id for r in handler.apply_filters(grid_view, model.objects.all()).all()]
assert len(ids) == 1
assert row.id in ids
@pytest.mark.field_last_modified_by
@pytest.mark.django_db
def test_user_is_not_filter_type(data_fixture):
user = data_fixture.create_user()
user2 = data_fixture.create_user()
table = data_fixture.create_database_table(user=user)
grid_view = data_fixture.create_grid_view(table=table)
last_modified_by_field = data_fixture.create_last_modified_by_field(table=table)
handler = ViewHandler()
model = table.get_model()
row = model.objects.create(
**{
f"last_modified_by": user,
}
)
row_2 = model.objects.create(
**{
f"last_modified_by": user2,
}
)
row_3 = model.objects.create(
**{
f"last_modified_by": None,
}
)
view_filter = data_fixture.create_view_filter(
view=grid_view,
field=last_modified_by_field,
type="user_is_not",
value=user.id,
)
ids = [r.id for r in handler.apply_filters(grid_view, model.objects.all()).all()]
assert len(ids) == 2
assert row_2.id in ids
assert row_3.id in ids
@pytest.mark.field_last_modified_by
@pytest.mark.django_db
def test_user_is_filter_type_export_import(data_fixture):
workspace = data_fixture.create_workspace()
user = data_fixture.create_user(workspace=workspace)
view_filter_type = view_filter_type_registry.get("user_is")
assert (
view_filter_type.get_export_serialized_value(
f"{user.id}", {"workspace_id": workspace.id}
)
== f"{user.email}"
)
id_mapping = {"workspace_id": workspace.id}
assert view_filter_type.set_import_serialized_value(user.email, id_mapping) == str(
user.id
)
assert view_filter_type.set_import_serialized_value(user.email, {}) == ""
assert view_filter_type.set_import_serialized_value("", id_mapping) == ""
assert view_filter_type.set_import_serialized_value("wrong", id_mapping) == ""

View file

@ -0,0 +1,7 @@
{
"type": "feature",
"message": "Add is, is not, empty, not empty filters for last modified by field",
"issue_number": 2109,
"bullet_points": [],
"created_at": "2023-11-22"
}

View file

@ -16,7 +16,7 @@
class="select-options__dropdown-selected dropdown__selected"
@click="show()"
>
<div v-if="hasValue()" class="select-options__dropdown-option">
<div v-if="hasValue()">
{{ selectedName }}
</div>
<i

View file

@ -76,6 +76,8 @@ import {
LinkRowNotContainsFilterType,
ContainsWordViewFilterType,
DoesntContainWordViewFilterType,
UserIsFilterType,
UserIsNotFilterType,
} from '@baserow/modules/database/viewFilters'
import {
CSVImporterType,
@ -421,6 +423,8 @@ export default (context) => {
)
app.$registry.register('viewFilter', new EmptyViewFilterType(context))
app.$registry.register('viewFilter', new NotEmptyViewFilterType(context))
app.$registry.register('viewFilter', new UserIsFilterType(context))
app.$registry.register('viewFilter', new UserIsNotFilterType(context))
app.$registry.register(
'viewOwnershipType',

View file

@ -1579,6 +1579,70 @@ export class MultipleCollaboratorsHasNotFilterType extends ViewFilterType {
}
}
export class UserIsFilterType extends ViewFilterType {
static getType() {
return 'user_is'
}
getName() {
const { i18n } = this.app
return i18n.t('viewFilter.is')
}
getExample() {
return '1'
}
getInputComponent() {
return ViewFilterTypeCollaborators
}
getCompatibleFieldTypes() {
return ['last_modified_by']
}
matches(rowValue, filterValue, field, fieldType) {
if (!isNumeric(filterValue)) {
return true
}
const filterValueId = parseInt(filterValue)
return rowValue?.id === filterValueId
}
}
export class UserIsNotFilterType extends ViewFilterType {
static getType() {
return 'user_is_not'
}
getName() {
const { i18n } = this.app
return i18n.t('viewFilter.isNot')
}
getExample() {
return '1'
}
getInputComponent() {
return ViewFilterTypeCollaborators
}
getCompatibleFieldTypes() {
return ['last_modified_by']
}
matches(rowValue, filterValue, field, fieldType) {
if (!isNumeric(filterValue)) {
return true
}
const filterValueId = parseInt(filterValue)
return rowValue?.id !== filterValueId
}
}
export class BooleanViewFilterType extends ViewFilterType {
static getType() {
return 'boolean'

View file

@ -3,6 +3,8 @@ import { createFile } from '@baserow/test/fixtures/fields'
import {
EqualViewFilterType,
FilenameContainsViewFilterType,
UserIsFilterType,
UserIsNotFilterType,
} from '@baserow/modules/database/viewFilters'
describe('View Filter Tests', () => {
@ -39,10 +41,14 @@ describe('View Filter Tests', () => {
const row = store.getters['page/view/grid/getRow'](1)
await editFieldWithoutSavingNewValue(row, 'exactly_matching_string')
await editFieldWithoutSavingNewValue(row, 'text', 'exactly_matching_string')
expect(row._.matchFilters).toBe(true)
await editFieldWithoutSavingNewValue(row, 'newly_edited_value_not_matching')
await editFieldWithoutSavingNewValue(
row,
'text',
'newly_edited_value_not_matching'
)
expect(row._.matchFilters).toBe(false)
})
@ -81,28 +87,30 @@ describe('View Filter Tests', () => {
const row = store.getters['page/view/grid/getRow'](1)
await editFieldWithoutSavingNewValue(row, [createFile('test_file_name')])
await editFieldWithoutSavingNewValue(row, 'file', [
createFile('test_file_name'),
])
expect(row._.matchFilters).toBe(true)
await editFieldWithoutSavingNewValue(row, [
await editFieldWithoutSavingNewValue(row, 'file', [
createFile('not_matching_new_file_name'),
])
expect(row._.matchFilters).toBe(false)
await editFieldWithoutSavingNewValue(row, [
await editFieldWithoutSavingNewValue(row, 'file', [
createFile('test_file_name'),
createFile('not_matching_new_file_name'),
])
expect(row._.matchFilters).toBe(true)
})
async function editFieldWithoutSavingNewValue(row, newValue) {
async function editFieldWithoutSavingNewValue(row, fieldType, newValue) {
await store.dispatch('page/view/grid/updateMatchFilters', {
view: store.getters['view/first'],
fields: [
{
id: 1,
type: 'file',
type: fieldType,
primary: true,
},
],
@ -112,4 +120,76 @@ describe('View Filter Tests', () => {
},
})
}
test('user is filter', async () => {
const userId = 1
await thereIsATableWithRowAndFilter(
{
name: 'Last Modified By Field',
type: 'last_modified_by',
primary: true,
},
{ id: 1, order: 0, field_1: null },
{
id: 1,
view: 1,
field: 1,
type: UserIsFilterType.getType(),
value: userId,
}
)
const row = store.getters['page/view/grid/getRow'](1)
await editFieldWithoutSavingNewValue(row, 'last_modified_by', {
id: userId,
name: 'User 1',
})
expect(row._.matchFilters).toBe(true)
await editFieldWithoutSavingNewValue(row, 'last_modified_by', {
id: 2,
name: 'User 2',
})
expect(row._.matchFilters).toBe(false)
await editFieldWithoutSavingNewValue(row, 'last_modified_by', null)
expect(row._.matchFilters).toBe(false)
})
test('user is not filter', async () => {
const userId = 1
await thereIsATableWithRowAndFilter(
{
name: 'Last Modified By Field',
type: 'last_modified_by',
primary: true,
},
{ id: 1, order: 0, field_1: null },
{
id: 1,
view: 1,
field: 1,
type: UserIsNotFilterType.getType(),
value: userId,
}
)
const row = store.getters['page/view/grid/getRow'](1)
await editFieldWithoutSavingNewValue(row, 'last_modified_by', {
id: userId,
name: 'User 1',
})
expect(row._.matchFilters).toBe(false)
await editFieldWithoutSavingNewValue(row, 'last_modified_by', {
id: 2,
name: 'User 2',
})
expect(row._.matchFilters).toBe(true)
await editFieldWithoutSavingNewValue(row, 'last_modified_by', null)
expect(row._.matchFilters).toBe(true)
})
})