1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-15 01:28:30 +00:00

Introduced has file type filter

This commit is contained in:
Bram Wiepjes 2021-09-22 12:05:09 +00:00
parent 55aaec00bf
commit a7918da273
8 changed files with 255 additions and 0 deletions
backend
src/baserow/contrib/database
tests/baserow/contrib/database/view
changelog.md
web-frontend

View file

@ -112,6 +112,7 @@ class DatabaseConfig(AppConfig):
LowerThanViewFilterType,
ContainsViewFilterType,
FilenameContainsViewFilterType,
HasFileTypeViewFilterType,
ContainsNotViewFilterType,
BooleanViewFilterType,
SingleSelectEqualViewFilterType,
@ -123,6 +124,7 @@ class DatabaseConfig(AppConfig):
view_filter_type_registry.register(EqualViewFilterType())
view_filter_type_registry.register(NotEqualViewFilterType())
view_filter_type_registry.register(FilenameContainsViewFilterType())
view_filter_type_registry.register(HasFileTypeViewFilterType())
view_filter_type_registry.register(ContainsViewFilterType())
view_filter_type_registry.register(ContainsNotViewFilterType())
view_filter_type_registry.register(HigherThanViewFilterType())

View file

@ -94,6 +94,32 @@ class FilenameContainsViewFilterType(ViewFilterType):
return filename_contains_filter(*args)
class HasFileTypeViewFilterType(ViewFilterType):
"""
The file field type column contains an array of objects with details related to
the uploaded user files. Every object always contains the property `is_image`
that indicates if the user file is an image. Using the Django contains lookup,
we can check if there is at least one object where the `is_image` is True. If the
filter value is `image`, there must at least be one object where the `is_image`
is true. If the filter value is `document` there must at least be one object
where the `is_image` is false. If an unsupported filter value is provided, we don't
want to filter on anything.
"""
type = "has_file_type"
compatible_field_types = [FileFieldType.type]
def get_filter(self, field_name, value, model_field, field):
value = value.strip()
is_image = value == "image"
is_document = value == "document"
if is_document or is_image:
return Q(**{f"{field_name}__contains": [{"is_image": is_image}]})
else:
return Q()
class ContainsViewFilterType(ViewFilterType):
"""
The contains filter checks if the field value contains the provided filter value.

View file

@ -2334,6 +2334,107 @@ def test_filename_contains_filter_type(data_fixture):
assert len(results) == 1
@pytest.mark.django_db
def test_has_file_type(data_fixture):
user = data_fixture.create_user()
table = data_fixture.create_database_table(user=user)
grid_view = data_fixture.create_grid_view(table=table)
file_field = data_fixture.create_file_field(table=table)
handler = ViewHandler()
model = table.get_model()
row_with_single_image = model.objects.create(
**{
f"field_{file_field.id}": [
{"visible_name": "test_file.jpg", "is_image": True}
],
}
)
row_with_single_document = model.objects.create(
**{
f"field_{file_field.id}": [{"visible_name": "doc.doc", "is_image": False}],
}
)
row_with_multiple_documents = model.objects.create(
**{
f"field_{file_field.id}": [
{"visible_name": "test.doc", "is_image": False},
{"visible_name": "test.txt", "is_image": False},
{"visible_name": "test.doc", "is_image": False},
{"visible_name": "test.txt", "is_image": False},
],
}
)
row_with_multiple_images = model.objects.create(
**{
f"field_{file_field.id}": [
{"visible_name": "test.jpg", "is_image": True},
{"visible_name": "test.png", "is_image": True},
],
}
)
row_with_single_image_and_multiple_documents = model.objects.create(
**{
f"field_{file_field.id}": [
{"visible_name": "test_doc.doc", "is_image": False},
{"visible_name": "test_doc.doc", "is_image": False},
{"visible_name": "test_image.jpg", "is_image": True},
{"visible_name": "test_doc.doc", "is_image": False},
],
}
)
row_with_single_document_and_multiple_images = model.objects.create(
**{
f"field_{file_field.id}": [
{"visible_name": "image1.jpg", "is_image": True},
{"visible_name": "image2.png", "is_image": True},
{"visible_name": "doc.doc", "is_image": False},
{"visible_name": "image3.jpg", "is_image": True},
],
}
)
model.objects.create(**{f"field_{file_field.id}": []})
view_filter = data_fixture.create_view_filter(
view=grid_view,
field=file_field,
type="has_file_type",
value="",
)
ids = [r.id for r in handler.apply_filters(grid_view, model.objects.all()).all()]
assert len(ids) == 7
view_filter.value = "image"
view_filter.save()
ids = [r.id for r in handler.apply_filters(grid_view, model.objects.all()).all()]
assert len(ids) == 4
assert row_with_single_image.id in ids
assert row_with_multiple_images.id in ids
assert row_with_single_image_and_multiple_documents.id in ids
assert row_with_single_document_and_multiple_images.id in ids
view_filter.value = "document"
view_filter.save()
ids = [r.id for r in handler.apply_filters(grid_view, model.objects.all()).all()]
assert len(ids) == 4
assert row_with_single_document.id in ids
assert row_with_multiple_documents.id in ids
assert row_with_single_image_and_multiple_documents.id in ids
assert row_with_single_document_and_multiple_images.id in ids
data_fixture.create_view_filter(
view=grid_view,
field=file_field,
type="has_file_type",
value="image",
)
ids = [r.id for r in handler.apply_filters(grid_view, model.objects.all()).all()]
assert len(ids) == 2
assert row_with_single_image_and_multiple_documents.id in ids
assert row_with_single_document_and_multiple_images.id in ids
@pytest.mark.django_db
def test_link_row_preload_values(data_fixture, django_assert_num_queries):
user = data_fixture.create_user()

View file

@ -19,6 +19,7 @@
* Fix minor error that could sometimes occur when a row and it's table/group/database
were deleted in rapid succession.
* Fix accidentally locking of too many rows in various tables during update operations.
* Introduced the has file type filter.
## Released (2021-08-11)

View file

@ -0,0 +1,36 @@
<template>
<Dropdown
:value="filter.value"
:show-search="false"
:disabled="readOnly"
class="filters__value-dropdown dropdown--tiny"
@input="$emit('input', $event)"
>
<DropdownItem name="image" value="image"></DropdownItem>
<DropdownItem name="document" value="document"></DropdownItem>
</Dropdown>
</template>
<script>
export default {
name: 'ViewFilterTypeFileTypeDropdown',
props: {
filter: {
type: Object,
required: true,
},
fields: {
type: Array,
required: true,
},
primary: {
type: Object,
required: true,
},
readOnly: {
type: Boolean,
required: true,
},
},
}
</script>

View file

@ -22,6 +22,7 @@ import {
DateNotEqualViewFilterType,
ContainsViewFilterType,
FilenameContainsViewFilterType,
HasFileTypeViewFilterType,
ContainsNotViewFilterType,
HigherThanViewFilterType,
LowerThanViewFilterType,
@ -91,6 +92,7 @@ export default (context) => {
'viewFilter',
new FilenameContainsViewFilterType(context)
)
app.$registry.register('viewFilter', new HasFileTypeViewFilterType(context))
app.$registry.register('viewFilter', new ContainsNotViewFilterType(context))
app.$registry.register('viewFilter', new HigherThanViewFilterType(context))
app.$registry.register('viewFilter', new LowerThanViewFilterType(context))

View file

@ -9,6 +9,7 @@ import ViewFilterTypeTimeZone from '@baserow/modules/database/components/view/Vi
import ViewFilterTypeLinkRow from '@baserow/modules/database/components/view/ViewFilterTypeLinkRow'
import { trueString } from '@baserow/modules/database/utils/constants'
import { isNumeric } from '@baserow/modules/core/utils/string'
import ViewFilterTypeFileTypeDropdown from '@baserow/modules/database/components/view/ViewFilterTypeFileTypeDropdown'
export class ViewFilterType extends Registerable {
/**
@ -177,6 +178,45 @@ export class FilenameContainsViewFilterType extends ViewFilterType {
}
}
export class HasFileTypeViewFilterType extends ViewFilterType {
static getType() {
return 'has_file_type'
}
getName() {
return 'has file type'
}
getExample() {
return 'image | document'
}
getInputComponent() {
return ViewFilterTypeFileTypeDropdown
}
getCompatibleFieldTypes() {
return ['file']
}
matches(rowValue, filterValue, field, fieldType) {
const isImage = filterValue === 'image'
const isDocument = filterValue === 'document'
if (!isImage && !isDocument) {
return true
}
for (let i = 0; i < rowValue.length; i++) {
if (rowValue[i].is_image === isImage) {
return true
}
}
return false
}
}
export class ContainsViewFilterType extends ViewFilterType {
static getType() {
return 'contains'

View file

@ -6,6 +6,7 @@ import {
DateEqualViewFilterType,
DateNotEqualViewFilterType,
DateEqualsTodayViewFilterType,
HasFileTypeViewFilterType,
} from '@baserow/modules/database/viewFilters'
const dateBeforeCasesWithTimezone = [
@ -400,4 +401,50 @@ describe('All Tests', () => {
)
expect(result).toBe(values.expected)
})
test('HasFileType contains image', () => {
expect(new HasFileTypeViewFilterType().matches([], '', {})).toBe(true)
expect(new HasFileTypeViewFilterType().matches([], 'image', {})).toBe(false)
expect(new HasFileTypeViewFilterType().matches([], 'document', {})).toBe(
false
)
expect(
new HasFileTypeViewFilterType().matches([{ is_image: true }], 'image', {})
).toBe(true)
expect(
new HasFileTypeViewFilterType().matches(
[{ is_image: true }],
'document',
{}
)
).toBe(false)
expect(
new HasFileTypeViewFilterType().matches(
[{ is_image: false }],
'image',
{}
)
).toBe(false)
expect(
new HasFileTypeViewFilterType().matches(
[{ is_image: false }],
'document',
{}
)
).toBe(true)
expect(
new HasFileTypeViewFilterType().matches(
[{ is_image: true }, { is_image: false }],
'image',
{}
)
).toBe(true)
expect(
new HasFileTypeViewFilterType().matches(
[{ is_image: true }, { is_image: false }],
'document',
{}
)
).toBe(true)
})
})