feat: add filetype and mediatype searches (#575)

* feat: add filetype and mediatype searches

* style: fix some style issues

* fix: parametrize mediatype and filetype tests

* style: fix remaining unordered import

* style: fix pytest parametrize calls

* feat: add human-readable names to mediacategories

* feat: use human-readable names in mediacategory: search

* feat: add human-readable name to open document

* fix: fix returning multiple filetypes issue and add regression test
This commit is contained in:
python357-1
2024-11-14 15:52:00 -06:00
committed by GitHub
parent fb7ad928af
commit 97e0e80f6f
5 changed files with 101 additions and 0 deletions

View File

@@ -77,6 +77,9 @@ class FilterState:
path: Path | str | None = None
# file name
name: str | None = None
# file type
filetype: str | None = None
mediatype: str | None = None
# a generic query to be parsed
query: str | None = None
@@ -87,6 +90,7 @@ class FilterState:
# parse the value
if ":" in query:
kind, _, value = query.partition(":")
value = value.replace('"', "")
else:
# default to tag search
kind, value = "tag", query
@@ -101,6 +105,10 @@ class FilterState:
self.name = value
elif kind == "id":
self.id = int(self.id) if str(self.id).isnumeric() else self.id
elif kind == "filetype":
self.filetype = value
elif kind == "mediatype":
self.mediatype = value
else:
self.tag = self.tag and self.tag.strip()

View File

@@ -36,6 +36,7 @@ from ...constants import (
TS_FOLDER_NAME,
)
from ...enums import LibraryPrefs
from ...media_types import MediaCategories
from .db import make_tables
from .enums import FieldTypeEnum, FilterState, TagColor
from .fields import (
@@ -438,6 +439,18 @@ class Library:
)
elif search.path:
statement = statement.where(Entry.path.ilike(f"%{search.path}%"))
elif search.filetype:
statement = statement.where(Entry.suffix.ilike(f"{search.filetype}"))
elif search.mediatype:
extensions: set[str] = set[str]()
for media_cat in MediaCategories.ALL_CATEGORIES:
if search.mediatype == media_cat.name:
extensions = extensions | media_cat.extensions
break
# just need to map it to search db - suffixes do not have '.'
statement = statement.where(
Entry.suffix.in_(map(lambda x: x.replace(".", ""), extensions))
)
extensions = self.prefs(LibraryPrefs.EXTENSION_LIST)
is_exclude_list = self.prefs(LibraryPrefs.IS_EXCLUDE_LIST)

View File

@@ -62,6 +62,7 @@ class MediaCategory:
media_type: MediaType
extensions: set[str]
name: str
is_iana: bool = False
@@ -338,151 +339,181 @@ class MediaCategories:
media_type=MediaType.ADOBE_PHOTOSHOP,
extensions=_ADOBE_PHOTOSHOP_SET,
is_iana=False,
name="photoshop",
)
AFFINITY_PHOTO_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.AFFINITY_PHOTO,
extensions=_AFFINITY_PHOTO_SET,
is_iana=False,
name="affinity photo",
)
ARCHIVE_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.ARCHIVE,
extensions=_ARCHIVE_SET,
is_iana=False,
name="archive",
)
AUDIO_MIDI_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.AUDIO_MIDI,
extensions=_AUDIO_MIDI_SET,
is_iana=False,
name="audio midi",
)
AUDIO_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.AUDIO,
extensions=_AUDIO_SET | _AUDIO_MIDI_SET,
is_iana=True,
name="audio",
)
BLENDER_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.BLENDER,
extensions=_BLENDER_SET,
is_iana=False,
name="blender",
)
DATABASE_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.DATABASE,
extensions=_DATABASE_SET,
is_iana=False,
name="database",
)
DISK_IMAGE_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.DISK_IMAGE,
extensions=_DISK_IMAGE_SET,
is_iana=False,
name="disk image",
)
DOCUMENT_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.DOCUMENT,
extensions=_DOCUMENT_SET,
is_iana=False,
name="document",
)
EBOOK_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.EBOOK,
extensions=_EBOOK_SET,
is_iana=False,
name="ebook",
)
FONT_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.FONT,
extensions=_FONT_SET,
is_iana=True,
name="font",
)
IMAGE_ANIMATED_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.IMAGE_ANIMATED,
extensions=_IMAGE_ANIMATED_SET,
is_iana=False,
name="animated image",
)
IMAGE_RAW_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.IMAGE_RAW,
extensions=_IMAGE_RAW_SET,
is_iana=False,
name="raw image",
)
IMAGE_VECTOR_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.IMAGE_VECTOR,
extensions=_IMAGE_VECTOR_SET,
is_iana=False,
name="vector image",
)
IMAGE_RASTER_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.IMAGE,
extensions=_IMAGE_RASTER_SET,
is_iana=False,
name="raster image",
)
IMAGE_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.IMAGE,
extensions=_IMAGE_RASTER_SET | _IMAGE_RAW_SET | _IMAGE_VECTOR_SET,
is_iana=True,
name="image",
)
INSTALLER_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.INSTALLER,
extensions=_INSTALLER_SET,
is_iana=False,
name="installer",
)
MATERIAL_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.MATERIAL,
extensions=_MATERIAL_SET,
is_iana=False,
name="material",
)
MODEL_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.MODEL,
extensions=_MODEL_SET,
is_iana=True,
name="model",
)
OPEN_DOCUMENT_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.OPEN_DOCUMENT,
extensions=_OPEN_DOCUMENT_SET,
is_iana=False,
name="open document",
)
PACKAGE_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.PACKAGE,
extensions=_PACKAGE_SET,
is_iana=False,
name="package",
)
PDF_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.PDF,
extensions=_PDF_SET,
is_iana=False,
name="pdf",
)
PLAINTEXT_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.PLAINTEXT,
extensions=_PLAINTEXT_SET,
is_iana=False,
name="plaintext",
)
PRESENTATION_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.PRESENTATION,
extensions=_PRESENTATION_SET,
is_iana=False,
name="presentation",
)
PROGRAM_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.PROGRAM,
extensions=_PROGRAM_SET,
is_iana=False,
name="program",
)
SHORTCUT_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.SHORTCUT,
extensions=_SHORTCUT_SET,
is_iana=False,
name="shortcut",
)
SOURCE_ENGINE_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.SOURCE_ENGINE,
extensions=_SOURCE_ENGINE_SET,
is_iana=False,
name="source engine",
)
SPREADSHEET_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.SPREADSHEET,
extensions=_SPREADSHEET_SET,
is_iana=False,
name="spreadsheet",
)
TEXT_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.TEXT,
extensions=_DOCUMENT_SET | _PLAINTEXT_SET,
is_iana=True,
name="text",
)
VIDEO_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.VIDEO,
extensions=_VIDEO_SET,
is_iana=True,
name="video",
)
ALL_CATEGORIES: list[MediaCategory] = [

View File

@@ -21,6 +21,37 @@ def cwd():
return CWD
@pytest.fixture
def file_mediatypes_library():
lib = Library()
status = lib.open_library(pathlib.Path(""), ":memory:")
assert status.success
entry1 = Entry(
folder=lib.folder,
path=pathlib.Path("foo.png"),
fields=lib.default_fields,
)
entry2 = Entry(
folder=lib.folder,
path=pathlib.Path("bar.png"),
fields=lib.default_fields,
)
entry3 = Entry(
folder=lib.folder,
path=pathlib.Path("baz.apng"),
fields=lib.default_fields,
)
assert lib.add_entries([entry1, entry2, entry3])
assert len(lib.tags) == 2
return lib
@pytest.fixture
def library(request):
# when no param is passed, use the default

View File

@@ -401,3 +401,21 @@ def test_library_prefs_multiple_identical_vals():
# accessing .value should raise exception
with pytest.raises(AttributeError):
assert TestPrefs.BAR.value
@pytest.mark.parametrize(["filetype", "num_of_filetype"], [("md", 1), ("txt", 1), ("png", 0)])
def test_filetype_search(library, filetype, num_of_filetype):
results = library.search_library(FilterState(filetype=filetype))
assert len(results.items) == num_of_filetype
@pytest.mark.parametrize(["filetype", "num_of_filetype"], [("png", 2), ("apng", 1), ("ng", 0)])
def test_filetype_return_one_filetype(file_mediatypes_library, filetype, num_of_filetype):
results = file_mediatypes_library.search_library(FilterState(filetype=filetype))
assert len(results.items) == num_of_filetype
@pytest.mark.parametrize(["mediatype", "num_of_mediatype"], [("plaintext", 2), ("image", 0)])
def test_mediatype_search(library, mediatype, num_of_mediatype):
results = library.search_library(FilterState(mediatype=mediatype))
assert len(results.items) == num_of_mediatype