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:
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
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) == ""
|
||||
|
|
|
@ -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"
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Add table
Reference in a new issue