From aa2925cde0589833c2e7c136b2d248d779ac37ef Mon Sep 17 00:00:00 2001 From: Jiri Date: Fri, 14 Jun 2024 06:29:22 +0800 Subject: [PATCH 1/9] add pytest to CI pipeline (#286) --- .github/workflows/pytest.yaml | 22 ++++++ pyproject.toml | 1 + requirements-dev.txt | 1 + tagstudio/src/core/library.py | 2 +- tagstudio/tests/conftest.py | 36 ++++++++++ .../test_library_search[--nomatch--].json | 1 + .../test_lib/test_library_search[First].json | 6 ++ .../test_lib/test_library_search[Second].json | 6 ++ .../test_lib/test_open_library.json | 4 ++ tagstudio/tests/core/test_lib.py | 18 +++++ tagstudio/tests/core/test_tags.py | 24 ++----- .../library/.TagStudio/ts_library.json | 69 +++++++++++++++++++ tagstudio/tests/fixtures/library/bar.txt | 0 tagstudio/tests/fixtures/library/foo.txt | 0 14 files changed, 172 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/pytest.yaml create mode 100644 tagstudio/tests/conftest.py create mode 100644 tagstudio/tests/core/__snapshots__/test_lib/test_library_search[--nomatch--].json create mode 100644 tagstudio/tests/core/__snapshots__/test_lib/test_library_search[First].json create mode 100644 tagstudio/tests/core/__snapshots__/test_lib/test_library_search[Second].json create mode 100644 tagstudio/tests/core/__snapshots__/test_lib/test_open_library.json create mode 100644 tagstudio/tests/core/test_lib.py create mode 100644 tagstudio/tests/fixtures/library/.TagStudio/ts_library.json create mode 100644 tagstudio/tests/fixtures/library/bar.txt create mode 100644 tagstudio/tests/fixtures/library/foo.txt diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml new file mode 100644 index 00000000..e8a458aa --- /dev/null +++ b/.github/workflows/pytest.yaml @@ -0,0 +1,22 @@ +name: pytest + +on: [push, pull_request] + +jobs: + pytest: + name: Run tests + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install -r requirements-dev.txt + + - name: Run tests + run: | + pytest tagstudio/tests/ diff --git a/pyproject.toml b/pyproject.toml index 1cd4b4c8..d60908da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,3 +6,4 @@ strict_optional = false disable_error_code = ["union-attr", "annotation-unchecked", "import-untyped"] explicit_package_bases = true warn_unused_ignores = true +exclude = ['tests'] diff --git a/requirements-dev.txt b/requirements-dev.txt index 7b90e16c..81de1b33 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -3,3 +3,4 @@ pre-commit==3.7.0 pytest==8.2.0 Pyinstaller==6.6.0 mypy==1.10.0 +syrupy==4.6.1 diff --git a/tagstudio/src/core/library.py b/tagstudio/src/core/library.py index 59f49401..9e9cf28b 100644 --- a/tagstudio/src/core/library.py +++ b/tagstudio/src/core/library.py @@ -80,7 +80,7 @@ class Entry: # self.word_count: int = None def __str__(self) -> str: - return f"\n{self.compressed_dict()}\n" + return str(self.compressed_dict()) def __repr__(self) -> str: return self.__str__() diff --git a/tagstudio/tests/conftest.py b/tagstudio/tests/conftest.py new file mode 100644 index 00000000..036acd8c --- /dev/null +++ b/tagstudio/tests/conftest.py @@ -0,0 +1,36 @@ +import sys +import pathlib + +import pytest +from syrupy.extensions.json import JSONSnapshotExtension + +CWD = pathlib.Path(__file__).parent + +sys.path.insert(0, str(CWD.parent)) + +from src.core.library import Tag, Library + + +@pytest.fixture +def test_tag(): + yield Tag( + id=1, + name="Tag Name", + shorthand="TN", + aliases=["First A", "Second A"], + subtags_ids=[2, 3, 4], + color="", + ) + + +@pytest.fixture +def test_library(): + lib = Library() + ret_code = lib.open_library(CWD / "fixtures" / "library") + assert ret_code == 1 + yield lib + + +@pytest.fixture +def snapshot_json(snapshot): + return snapshot.with_defaults(extension_class=JSONSnapshotExtension) diff --git a/tagstudio/tests/core/__snapshots__/test_lib/test_library_search[--nomatch--].json b/tagstudio/tests/core/__snapshots__/test_lib/test_library_search[--nomatch--].json new file mode 100644 index 00000000..fe51488c --- /dev/null +++ b/tagstudio/tests/core/__snapshots__/test_lib/test_library_search[--nomatch--].json @@ -0,0 +1 @@ +[] diff --git a/tagstudio/tests/core/__snapshots__/test_lib/test_library_search[First].json b/tagstudio/tests/core/__snapshots__/test_lib/test_library_search[First].json new file mode 100644 index 00000000..e4e6902c --- /dev/null +++ b/tagstudio/tests/core/__snapshots__/test_lib/test_library_search[First].json @@ -0,0 +1,6 @@ +[ + [ + "", + 2 + ] +] diff --git a/tagstudio/tests/core/__snapshots__/test_lib/test_library_search[Second].json b/tagstudio/tests/core/__snapshots__/test_lib/test_library_search[Second].json new file mode 100644 index 00000000..920ff495 --- /dev/null +++ b/tagstudio/tests/core/__snapshots__/test_lib/test_library_search[Second].json @@ -0,0 +1,6 @@ +[ + [ + "", + 1 + ] +] diff --git a/tagstudio/tests/core/__snapshots__/test_lib/test_open_library.json b/tagstudio/tests/core/__snapshots__/test_lib/test_open_library.json new file mode 100644 index 00000000..a576676f --- /dev/null +++ b/tagstudio/tests/core/__snapshots__/test_lib/test_open_library.json @@ -0,0 +1,4 @@ +[ + "{'id': 1, 'filename': 'foo.txt', 'path': '.', 'fields': [{6: [1001]}]}", + "{'id': 2, 'filename': 'bar.txt', 'path': '.', 'fields': [{6: [1000]}]}" +] diff --git a/tagstudio/tests/core/test_lib.py b/tagstudio/tests/core/test_lib.py new file mode 100644 index 00000000..997598f2 --- /dev/null +++ b/tagstudio/tests/core/test_lib.py @@ -0,0 +1,18 @@ +import pytest + + +def test_open_library(test_library, snapshot_json): + assert test_library.entries == snapshot_json + + +@pytest.mark.parametrize( + ["query"], + [ + ("First",), + ("Second",), + ("--nomatch--",), + ], +) +def test_library_search(test_library, query, snapshot_json): + res = test_library.search_library(query) + assert res == snapshot_json diff --git a/tagstudio/tests/core/test_tags.py b/tagstudio/tests/core/test_tags.py index e8e48754..43cde427 100644 --- a/tagstudio/tests/core/test_tags.py +++ b/tagstudio/tests/core/test_tags.py @@ -1,18 +1,8 @@ -from src.core.library import Tag +def test_subtag(test_tag): + test_tag.remove_subtag(2) + test_tag.remove_subtag(2) - -def test_construction(): - tag = Tag( - id=1, - name="Tag Name", - shorthand="TN", - aliases=["First A", "Second A"], - subtags_ids=[2, 3, 4], - color="", - ) - assert tag - - -def test_empty_construction(): - tag = Tag(id=1, name="", shorthand="", aliases=[], subtags_ids=[], color="") - assert tag + test_tag.add_subtag(5) + # repeated add should not add the subtag + test_tag.add_subtag(5) + assert test_tag.subtag_ids == [3, 4, 5] diff --git a/tagstudio/tests/fixtures/library/.TagStudio/ts_library.json b/tagstudio/tests/fixtures/library/.TagStudio/ts_library.json new file mode 100644 index 00000000..eeab9fd6 --- /dev/null +++ b/tagstudio/tests/fixtures/library/.TagStudio/ts_library.json @@ -0,0 +1,69 @@ +{ + "ts-version": "9.3.1", + "ext_list": [ + ".json", + ".xmp", + ".aae" + ], + "is_exclude_list": true, + "tags": [ + { + "id": 0, + "name": "Archived", + "aliases": [ + "Archive" + ], + "color": "Red" + }, + { + "id": 1, + "name": "Favorite", + "aliases": [ + "Favorited", + "Favorites" + ], + "color": "Yellow" + }, + { + "id": 1000, + "name": "first", + "shorthand": "first", + "color": "magenta" + }, + { + "id": 1001, + "name": "second", + "shorthand": "second", + "color": "blue" + } + ], + "collations": [], + "fields": [], + "macros": [], + "entries": [ + { + "id": 1, + "filename": "foo.txt", + "path": ".", + "fields": [ + { + "6": [ + 1001 + ] + } + ] + }, + { + "id": 2, + "filename": "bar.txt", + "path": ".", + "fields": [ + { + "6": [ + 1000 + ] + } + ] + } + ] +} diff --git a/tagstudio/tests/fixtures/library/bar.txt b/tagstudio/tests/fixtures/library/bar.txt new file mode 100644 index 00000000..e69de29b diff --git a/tagstudio/tests/fixtures/library/foo.txt b/tagstudio/tests/fixtures/library/foo.txt new file mode 100644 index 00000000..e69de29b From 82946cb0b8113f58092d496c5d908c7939d68f80 Mon Sep 17 00:00:00 2001 From: Travis Abendshien Date: Thu, 13 Jun 2024 15:39:13 -0700 Subject: [PATCH 2/9] Update CONTRIBUTING.md with PyTest info --- CONTRIBUTING.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 669ed460..942aa3eb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,8 +25,9 @@ Thank you so much for showing interest in contributing to TagStudio! Here are a ### Prerequisites - [Python](https://www.python.org/downloads/) 3.12 -- [Ruff](https://github.com/astral-sh/ruff) _(Included in `requirements-dev.txt`)_ -- [Mypy](https://github.com/python/mypy) _(Included in `requirements-dev.txt`)_ +- [Ruff](https://github.com/astral-sh/ruff) (Included in `requirements-dev.txt`) +- [Mypy](https://github.com/python/mypy) (Included in `requirements-dev.txt`) +- [PyTest](https://docs.pytest.org) (Included in `requirements-dev.txt`) ### Creating a Python Virtual Environment @@ -85,7 +86,7 @@ A Python linter and code formatter. Ruff uses the `pyproject.toml` as its config #### Running Locally -- Lint code with by moving into the `/tagstudio` directory with `cd tagstudio` and running `ruff --config ../pyproject.toml ` +- Lint code with by moving into the `/tagstudio` directory with `cd tagstudio` and running `ruff --config ../pyproject.toml`. - Format code with `ruff format` inside the repository directory Ruff is also available as a VS Code [extension](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff), PyCharm [plugin](https://plugins.jetbrains.com/plugin/20574-ruff), and [more](https://docs.astral.sh/ruff/integrations/). @@ -99,19 +100,16 @@ Mypy is a static type checker for Python. It sure has a lot to say sometimes, bu - **First time only:** Move into the `/tagstudio` directory with `cd tagstudio` and run the following: - `mkdir -p .mypy_cache` - `mypy --install-types --non-interactive` -- Check code by moving into the `/tagstudio` directory with `cd tagstudio` _(if you aren't already inside)_ and running `mypy --config-file ../pyproject.toml .` _(Don't forget the `.` at the end!)_ +- Check code by moving into the `/tagstudio` directory with `cd tagstudio` _(if you aren't already inside)_ and running `mypy --config-file ../pyproject.toml .`. _(Don't forget the `.` at the end!)_ > [!CAUTION] > There's a known issue between PySide v6.6.3 and Mypy where Mypy will detect issues with the `.pyi` files inside of PySide and prematurely stop checking files. This issue is not present in PySide v6.6.2, which _should_ be compatible with everything else if you wish to try using that version in the meantime. Mypy is also available as a VS Code [extension](https://marketplace.visualstudio.com/items?itemName=matangover.mypy), PyCharm [plugin](https://plugins.jetbrains.com/plugin/11086-mypy), and [more](https://plugins.jetbrains.com/plugin/11086-mypy). -### PyTest _(Work in Progress)_ +### PyTest -> [!IMPORTANT] -> Tests are not currently run as part of any automated workflow. - -To run all the tests use `python -m pytest tests/` from the `tagstudio` folder. +- Run all tests by moving into the `/tagstudio` directory with `cd tagstudio` and running `pytest tests/`. ## Code Guidelines From 8e065ca8aca209928b62c68dc99d95d6069afad5 Mon Sep 17 00:00:00 2001 From: Jiri Date: Sat, 15 Jun 2024 01:11:00 +0800 Subject: [PATCH 3/9] create testing library files ad-hoc (#292) --- .gitignore | 1 + tagstudio/tests/conftest.py | 8 +++++++- tagstudio/tests/fixtures/library/bar.txt | 0 tagstudio/tests/fixtures/library/foo.txt | 0 4 files changed, 8 insertions(+), 1 deletion(-) delete mode 100644 tagstudio/tests/fixtures/library/bar.txt delete mode 100644 tagstudio/tests/fixtures/library/foo.txt diff --git a/.gitignore b/.gitignore index f20f8b1c..4c9ce401 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ coverage.xml .hypothesis/ .pytest_cache/ cover/ +tagstudio/tests/fixtures/library/* # Translations *.mo diff --git a/tagstudio/tests/conftest.py b/tagstudio/tests/conftest.py index 036acd8c..2c5cd225 100644 --- a/tagstudio/tests/conftest.py +++ b/tagstudio/tests/conftest.py @@ -25,9 +25,15 @@ def test_tag(): @pytest.fixture def test_library(): + lib_dir = CWD / "fixtures" / "library" + lib = Library() - ret_code = lib.open_library(CWD / "fixtures" / "library") + ret_code = lib.open_library(lib_dir) assert ret_code == 1 + # create files for the entries + for entry in lib.entries: + (lib_dir / entry.filename).touch() + yield lib diff --git a/tagstudio/tests/fixtures/library/bar.txt b/tagstudio/tests/fixtures/library/bar.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/tagstudio/tests/fixtures/library/foo.txt b/tagstudio/tests/fixtures/library/foo.txt deleted file mode 100644 index e69de29b..00000000 From fae65bd9e95650c3a9b7e54054423d67d1d18f6c Mon Sep 17 00:00:00 2001 From: Lennart S <55597910+LennartCode@users.noreply.github.com> Date: Sat, 15 Jun 2024 22:10:55 +0200 Subject: [PATCH 4/9] docs: Fixed broken markdown doc link (#291) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9378827f..592332e3 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ As of writing (Alpha v9.3.0) the project is in a useable state, however it lacks ### What Features Are You Planning on Adding? > [!IMPORTANT] -> See the [Planned Features](/doc/planned_features.md) documentation for the latest feature lists. The lists here are currently being migrated over there with individual pages for larger features. +> See the [Planned Features](/doc/updates/planned_features.md) documentation for the latest feature lists. The lists here are currently being migrated over there with individual pages for larger features. Of the several features I have planned for the project, these are broken up into “priority” features and “future” features. Priority features were originally intended for the first public release, however are currently absent from the Alpha v9.x.x builds. From 5c25666e670752db4770e3c38c66bc7765401083 Mon Sep 17 00:00:00 2001 From: Travis Abendshien Date: Sat, 15 Jun 2024 13:19:28 -0700 Subject: [PATCH 5/9] Fix TypeError in folders_to_tags.py - Additionally use proper comparison syntax --- tagstudio/src/qt/modals/folders_to_tags.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tagstudio/src/qt/modals/folders_to_tags.py b/tagstudio/src/qt/modals/folders_to_tags.py index a6e2c3f8..24073c15 100644 --- a/tagstudio/src/qt/modals/folders_to_tags.py +++ b/tagstudio/src/qt/modals/folders_to_tags.py @@ -79,7 +79,7 @@ def folders_to_tags(library: Library): def reverse_tag(library: Library, tag: Tag, list: list[Tag]) -> list[Tag]: - if list != None: + if list is not None: list.append(tag) else: list = [tag] @@ -144,7 +144,7 @@ def generate_preview_data(library: Library): if cut: branch["dirs"].pop(folder) - if not "tag" in branch: + if "tag" not in branch: return if branch["tag"].id == -1 or len(branch["files"]) > 0: # Needs to be first return False @@ -289,7 +289,7 @@ class TreeItem(QWidget): self.children_layout.addWidget(item) for file in data["files"]: label = QLabel() - label.setText(" -> " + file) + label.setText(" -> " + str(file)) self.children_layout.addWidget(label) if len(data["files"]) == 0 and len(data["dirs"].values()) == 0: @@ -321,7 +321,7 @@ class ModifiedTagWidget( self.bg_button = QPushButton(self) self.bg_button.setFlat(True) - if parentTag != None: + if parentTag is not None: text = f"{tag.name} ({parentTag.name})".replace("&", "&&") else: text = tag.name.replace("&", "&&") From 4c6ebec529d2111c3901edfbd779c8e71f469a8b Mon Sep 17 00:00:00 2001 From: Jiri Date: Mon, 17 Jun 2024 05:24:48 +0800 Subject: [PATCH 6/9] refactoring: centralize field IDs (#157) * use enum with named fields instead of ad-hoc numbers * move tag ids into constants file --- tagstudio/src/core/constants.py | 3 ++ tagstudio/src/core/enums.py | 13 +++++ tagstudio/src/core/library.py | 61 +++++++++------------- tagstudio/src/core/ts_core.py | 1 + tagstudio/src/qt/modals/folders_to_tags.py | 3 +- tagstudio/src/qt/ts_qt.py | 6 ++- tagstudio/src/qt/widgets/item_thumb.py | 24 +++++---- tagstudio/src/qt/widgets/tag_box.py | 7 +-- 8 files changed, 68 insertions(+), 50 deletions(-) diff --git a/tagstudio/src/core/constants.py b/tagstudio/src/core/constants.py index d9a3f1d9..d07489aa 100644 --- a/tagstudio/src/core/constants.py +++ b/tagstudio/src/core/constants.py @@ -163,3 +163,6 @@ TAG_COLORS = [ "cool gray", "olive", ] + +TAG_FAVORITE = 1 +TAG_ARCHIVED = 0 diff --git a/tagstudio/src/core/enums.py b/tagstudio/src/core/enums.py index d6b274eb..7610a2cc 100644 --- a/tagstudio/src/core/enums.py +++ b/tagstudio/src/core/enums.py @@ -24,3 +24,16 @@ class SearchMode(int, enum.Enum): AND = 0 OR = 1 + + +class FieldID(int, enum.Enum): + TITLE = 0 + AUTHOR = 1 + ARTIST = 2 + DESCRIPTION = 4 + NOTES = 5 + TAGS = 6 + CONTENT_TAGS = 7 + META_TAGS = 8 + DATE_PUBLISHED = 14 + SOURCE = 21 diff --git a/tagstudio/src/core/library.py b/tagstudio/src/core/library.py index 9e9cf28b..2e5843df 100644 --- a/tagstudio/src/core/library.py +++ b/tagstudio/src/core/library.py @@ -5,7 +5,6 @@ """The Library object and related methods for TagStudio.""" import datetime -import json import logging import os import time @@ -18,6 +17,7 @@ from pathlib import Path from typing import cast, Generator from typing_extensions import Self +from src.core.enums import FieldID from src.core.json_typing import JsonCollation, JsonEntry, JsonLibary, JsonTag from src.core.utils.str import strip_punctuation from src.core.utils.web import strip_web_protocol @@ -1948,48 +1948,44 @@ class Library: if data: # Add a Title Field if the data doesn't already exist. if data.get("title"): - field_id = 0 # Title Field ID - if not self.does_field_content_exist(entry_id, field_id, data["title"]): - self.add_field_to_entry(entry_id, field_id) + if not self.does_field_content_exist( + entry_id, FieldID.TITLE, data["title"] + ): + self.add_field_to_entry(entry_id, FieldID.TITLE) self.update_entry_field(entry_id, -1, data["title"], "replace") # Add an Author Field if the data doesn't already exist. if data.get("author"): - field_id = 1 # Author Field ID if not self.does_field_content_exist( - entry_id, field_id, data["author"] + entry_id, FieldID.AUTHOR, data["author"] ): - self.add_field_to_entry(entry_id, field_id) + self.add_field_to_entry(entry_id, FieldID.AUTHOR) self.update_entry_field(entry_id, -1, data["author"], "replace") # Add an Artist Field if the data doesn't already exist. if data.get("artist"): - field_id = 2 # Artist Field ID if not self.does_field_content_exist( - entry_id, field_id, data["artist"] + entry_id, FieldID.ARTIST, data["artist"] ): - self.add_field_to_entry(entry_id, field_id) + self.add_field_to_entry(entry_id, FieldID.ARTIST) self.update_entry_field(entry_id, -1, data["artist"], "replace") # Add a Date Published Field if the data doesn't already exist. if data.get("date_published"): - field_id = 14 # Date Published Field ID date = str( datetime.datetime.strptime( data["date_published"], "%Y-%m-%d %H:%M:%S" ) ) - if not self.does_field_content_exist(entry_id, field_id, date): - self.add_field_to_entry(entry_id, field_id) + if not self.does_field_content_exist( + entry_id, FieldID.DATE_PUBLISHED, date + ): + self.add_field_to_entry(entry_id, FieldID.DATE_PUBLISHED) # entry = self.entries[entry_id] self.update_entry_field(entry_id, -1, date, "replace") # Process String Tags if the data doesn't already exist. if data.get("tags"): - tags_field_id = 6 # Tags Field ID - content_tags_field_id = 7 # Content Tags Field ID - meta_tags_field_id = 8 # Meta Tags Field ID - notes_field_id = 5 # Notes Field ID tags: list[str] = data["tags"] # extra: list[str] = [] # for tag in tags: @@ -2038,7 +2034,7 @@ class Library: # tag_field_indices = self.get_field_index_in_entry( # entry_index, tags_field_id) content_tags_field_indices = self.get_field_index_in_entry( - self.get_entry(entry_id), content_tags_field_id + self.get_entry(entry_id), FieldID.CONTENT_TAGS ) # meta_tags_field_indices = self.get_field_index_in_entry( # entry_index, meta_tags_field_id) @@ -2055,45 +2051,40 @@ class Library: entry_id, priority_field_index, [matching[0]], "append" ) else: - self.add_field_to_entry(entry_id, content_tags_field_id) + self.add_field_to_entry(entry_id, FieldID.CONTENT_TAGS) self.update_entry_field( entry_id, -1, [matching[0]], "append" ) # Add all original string tags as a note. str_tags = f"Original Tags: {tags}" - if not self.does_field_content_exist( - entry_id, notes_field_id, str_tags - ): - self.add_field_to_entry(entry_id, notes_field_id) + if not self.does_field_content_exist(entry_id, FieldID.NOTES, str_tags): + self.add_field_to_entry(entry_id, FieldID.NOTES) self.update_entry_field(entry_id, -1, str_tags, "replace") # Add a Description Field if the data doesn't already exist. - if "description" in data.keys() and data["description"]: - field_id = 4 # Description Field ID + if data.get("description"): if not self.does_field_content_exist( - entry_id, field_id, data["description"] + entry_id, FieldID.DESCRIPTION, data["description"] ): - self.add_field_to_entry(entry_id, field_id) + self.add_field_to_entry(entry_id, FieldID.DESCRIPTION) self.update_entry_field( entry_id, -1, data["description"], "replace" ) - if "content" in data.keys() and data["content"]: - field_id = 4 # Description Field ID + if data.get("content"): if not self.does_field_content_exist( - entry_id, field_id, data["content"] + entry_id, FieldID.DESCRIPTION, data["content"] ): - self.add_field_to_entry(entry_id, field_id) + self.add_field_to_entry(entry_id, FieldID.DESCRIPTION) self.update_entry_field(entry_id, -1, data["content"], "replace") - if "source" in data.keys() and data["source"]: - field_id = 21 # Source Field ID + if data.get("source"): for source in data["source"].split(" "): if source and source != " ": source = strip_web_protocol(string=source) if not self.does_field_content_exist( - entry_id, field_id, source + entry_id, FieldID.SOURCE, source ): - self.add_field_to_entry(entry_id, field_id) + self.add_field_to_entry(entry_id, FieldID.SOURCE) self.update_entry_field(entry_id, -1, source, "replace") def add_field_to_entry(self, entry_id: int, field_id: int) -> None: diff --git a/tagstudio/src/core/ts_core.py b/tagstudio/src/core/ts_core.py index 2644c9fd..63ac30e6 100644 --- a/tagstudio/src/core/ts_core.py +++ b/tagstudio/src/core/ts_core.py @@ -7,6 +7,7 @@ import json import os from pathlib import Path +from enum import Enum from src.core.library import Entry, Library from src.core.constants import TS_FOLDER_NAME, TEXT_FIELDS diff --git a/tagstudio/src/qt/modals/folders_to_tags.py b/tagstudio/src/qt/modals/folders_to_tags.py index 24073c15..7021d59d 100644 --- a/tagstudio/src/qt/modals/folders_to_tags.py +++ b/tagstudio/src/qt/modals/folders_to_tags.py @@ -18,6 +18,7 @@ from PySide6.QtWidgets import ( QFrame, ) +from src.core.enums import FieldID from src.core.library import Library, Tag from src.core.palette import ColorType, get_tag_color from src.qt.flowlayout import FlowLayout @@ -73,7 +74,7 @@ def folders_to_tags(library: Library): tag = add_folders_to_tree(folders) if tag: if not entry.has_tag(library, tag.id): - entry.add_tag(library, tag.id, 6) + entry.add_tag(library, tag.id, FieldID.TAGS) logging.info("Done") diff --git a/tagstudio/src/qt/ts_qt.py b/tagstudio/src/qt/ts_qt.py index e53f8212..be740368 100644 --- a/tagstudio/src/qt/ts_qt.py +++ b/tagstudio/src/qt/ts_qt.py @@ -64,6 +64,8 @@ from src.core.constants import ( TS_FOLDER_NAME, VERSION_BRANCH, VERSION, + TAG_FAVORITE, + TAG_ARCHIVED, ) from src.core.utils.web import strip_web_protocol from src.qt.flowlayout import FlowLayout @@ -1252,8 +1254,8 @@ class QtDriver(QObject): filepath = self.lib.library_dir / entry.path / entry.filename item_thumb.set_item_id(entry.id) - item_thumb.assign_archived(entry.has_tag(self.lib, 0)) - item_thumb.assign_favorite(entry.has_tag(self.lib, 1)) + item_thumb.assign_archived(entry.has_tag(self.lib, TAG_ARCHIVED)) + item_thumb.assign_favorite(entry.has_tag(self.lib, TAG_FAVORITE)) # ctrl_down = True if QGuiApplication.keyboardModifiers() else False # TODO: Change how this works. The click function # for collations a few lines down should NOT be allowed during modifier keys. diff --git a/tagstudio/src/qt/widgets/item_thumb.py b/tagstudio/src/qt/widgets/item_thumb.py index 89688b2d..82c72f4e 100644 --- a/tagstudio/src/qt/widgets/item_thumb.py +++ b/tagstudio/src/qt/widgets/item_thumb.py @@ -7,7 +7,6 @@ import logging import os import time import typing -from types import FunctionType from pathlib import Path from typing import Optional @@ -23,9 +22,15 @@ from PySide6.QtWidgets import ( QCheckBox, ) - +from src.core.enums import FieldID from src.core.library import ItemType, Library, Entry -from src.core.constants import AUDIO_TYPES, VIDEO_TYPES, IMAGE_TYPES +from src.core.constants import ( + AUDIO_TYPES, + VIDEO_TYPES, + IMAGE_TYPES, + TAG_FAVORITE, + TAG_ARCHIVED, +) from src.qt.flowlayout import FlowWidget from src.qt.helpers.file_opener import FileOpenerHelper from src.qt.widgets.thumb_renderer import ThumbRenderer @@ -38,9 +43,6 @@ ERROR = f"[ERROR]" WARNING = f"[WARNING]" INFO = f"[INFO]" -DEFAULT_META_TAG_FIELD = 8 -TAG_FAVORITE = 1 -TAG_ARCHIVED = 0 logging.basicConfig(format="%(message)s", level=logging.INFO) @@ -405,8 +407,12 @@ class ItemThumb(FlowWidget): if self.mode == ItemType.ENTRY: # logging.info(f'[UPDATE BADGES] ENTRY: {self.lib.get_entry(self.item_id)}') # logging.info(f'[UPDATE BADGES] ARCH: {self.lib.get_entry(self.item_id).has_tag(self.lib, 0)}, FAV: {self.lib.get_entry(self.item_id).has_tag(self.lib, 1)}') - self.assign_archived(self.lib.get_entry(self.item_id).has_tag(self.lib, 0)) - self.assign_favorite(self.lib.get_entry(self.item_id).has_tag(self.lib, 1)) + self.assign_archived( + self.lib.get_entry(self.item_id).has_tag(self.lib, TAG_ARCHIVED) + ) + self.assign_favorite( + self.lib.get_entry(self.item_id).has_tag(self.lib, TAG_FAVORITE) + ) def set_item_id(self, id: int): """ @@ -475,7 +481,7 @@ class ItemThumb(FlowWidget): entry.add_tag( self.panel.driver.lib, tag_id, - field_id=DEFAULT_META_TAG_FIELD, + field_id=FieldID.META_TAGS, field_index=-1, ) else: diff --git a/tagstudio/src/qt/widgets/tag_box.py b/tagstudio/src/qt/widgets/tag_box.py index bcc551a8..06b8b1fe 100644 --- a/tagstudio/src/qt/widgets/tag_box.py +++ b/tagstudio/src/qt/widgets/tag_box.py @@ -10,6 +10,7 @@ import typing from PySide6.QtCore import Signal, Qt from PySide6.QtWidgets import QPushButton +from src.core.constants import TAG_FAVORITE, TAG_ARCHIVED from src.core.library import Library, Tag from src.qt.flowlayout import FlowLayout from src.qt.widgets.fields import FieldWidget @@ -141,7 +142,7 @@ class TagBoxWidget(FieldWidget): # panel.tag_updated.connect(lambda tag: self.lib.update_tag(tag)) self.edit_modal.show() - def add_tag_callback(self, tag_id): + def add_tag_callback(self, tag_id: int): # self.base_layout.addWidget(TagWidget(self.lib, self.lib.get_tag(tag), True)) # self.tags.append(tag) logging.info( @@ -154,7 +155,7 @@ class TagBoxWidget(FieldWidget): self.driver.lib, tag_id, field_id=id, field_index=-1 ) self.updated.emit() - if tag_id == 0 or tag_id == 1: + if tag_id in (TAG_FAVORITE, TAG_ARCHIVED): self.driver.update_badges() # if type((x[0]) == ThumbButton): @@ -180,7 +181,7 @@ class TagBoxWidget(FieldWidget): self.driver.lib, tag_id, field_index=index[0] ) self.updated.emit() - if tag_id == 0 or tag_id == 1: + if tag_id in (TAG_FAVORITE, TAG_ARCHIVED): self.driver.update_badges() # def show_add_button(self, value:bool): From b3c01e180a0c54483a7fc1e7eba0063b3ff3f4d2 Mon Sep 17 00:00:00 2001 From: Theasacraft <91694323+Thesacraft@users.noreply.github.com> Date: Mon, 17 Jun 2024 01:53:38 +0200 Subject: [PATCH 7/9] Update to pyside6 version 6.7.1 (#223) * Update to pyside6.7.1 * Fix Ruff * Fix MyPy * Update mypy job to also use PySide6 6.7.1 * Remove unused imports * Add Description to class * Ruff format * Fix Warning in pagination.py * Probably fix Pyside app test * Rename CustomQPushButton to QPushButtonWrapper also renamed custom_qbutton.py to qbutton_wrapper.py --- .github/workflows/mypy.yaml | 2 -- requirements.txt | 7 ++-- tagstudio/src/qt/helpers/qbutton_wrapper.py | 16 +++++++++ tagstudio/src/qt/modals/add_field.py | 1 + tagstudio/src/qt/pagination.py | 21 ++++++----- tagstudio/src/qt/widgets/fields.py | 24 ++++++------- tagstudio/src/qt/widgets/item_thumb.py | 8 ++--- tagstudio/src/qt/widgets/preview_panel.py | 40 ++++++++++----------- tagstudio/src/qt/widgets/thumb_button.py | 5 +-- 9 files changed, 68 insertions(+), 56 deletions(-) create mode 100644 tagstudio/src/qt/helpers/qbutton_wrapper.py diff --git a/.github/workflows/mypy.yaml b/.github/workflows/mypy.yaml index 61e2a9bc..374a1c5e 100644 --- a/.github/workflows/mypy.yaml +++ b/.github/workflows/mypy.yaml @@ -23,8 +23,6 @@ jobs: - name: Install dependencies run: | - # pyside 6.6.3 has some issue in their .pyi files - pip install PySide6==6.6.2 pip install -r requirements.txt pip install mypy==1.10.0 mkdir tagstudio/.mypy_cache diff --git a/requirements.txt b/requirements.txt index 7a1b5d53..a353c70c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,12 @@ humanfriendly==10.0 opencv_python>=4.8.0.74,<=4.9.0.80 Pillow==10.3.0 -PySide6>=6.5.1.1,<=6.6.3.1 -PySide6_Addons>=6.5.1.1,<=6.6.3.1 -PySide6_Essentials>=6.5.1.1,<=6.6.3.1 +PySide6==6.7.1 +PySide6_Addons==6.7.1 +PySide6_Essentials==6.7.1 typing_extensions>=3.10.0.0,<=4.11.0 ujson>=5.8.0,<=5.9.0 +numpy==1.26.4 rawpy==0.21.0 pillow-heif==0.16.0 chardet==5.2.0 diff --git a/tagstudio/src/qt/helpers/qbutton_wrapper.py b/tagstudio/src/qt/helpers/qbutton_wrapper.py new file mode 100644 index 00000000..ea554446 --- /dev/null +++ b/tagstudio/src/qt/helpers/qbutton_wrapper.py @@ -0,0 +1,16 @@ +# Copyright (C) 2024 Travis Abendshien (CyanVoxel). +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + +from PySide6.QtWidgets import QPushButton + + +class QPushButtonWrapper(QPushButton): + """ + This is a customized implementation of the PySide6 QPushButton that allows to suppress the warning that is triggered + by disconnecting a signal that is not currently connected. + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.is_connected = False diff --git a/tagstudio/src/qt/modals/add_field.py b/tagstudio/src/qt/modals/add_field.py index f315b86e..c0137da2 100644 --- a/tagstudio/src/qt/modals/add_field.py +++ b/tagstudio/src/qt/modals/add_field.py @@ -24,6 +24,7 @@ class AddFieldModal(QWidget): # - OR - # [Cancel] [Save] super().__init__() + self.is_connected = False self.lib = library self.setWindowTitle(f"Add Field") self.setWindowModality(Qt.WindowModality.ApplicationModal) diff --git a/tagstudio/src/qt/pagination.py b/tagstudio/src/qt/pagination.py index 8d46b8ee..904dd19f 100644 --- a/tagstudio/src/qt/pagination.py +++ b/tagstudio/src/qt/pagination.py @@ -15,7 +15,7 @@ from PySide6.QtWidgets import ( QLineEdit, QSizePolicy, ) - +from src.qt.helpers.qbutton_wrapper import QPushButtonWrapper # class NumberEdit(QLineEdit): # def __init__(self, parent=None) -> None: @@ -50,13 +50,13 @@ class Pagination(QWidget, QObject): # self.setMinimumHeight(32) # [<] ---------------------------------- - self.prev_button = QPushButton() + self.prev_button = QPushButtonWrapper() self.prev_button.setText("<") self.prev_button.setMinimumSize(self.button_size) self.prev_button.setMaximumSize(self.button_size) # --- [1] ------------------------------ - self.start_button = QPushButton() + self.start_button = QPushButtonWrapper() self.start_button.setMinimumSize(self.button_size) self.start_button.setMaximumSize(self.button_size) # self.start_button.setStyleSheet('background:cyan;') @@ -104,14 +104,14 @@ class Pagination(QWidget, QObject): self.end_ellipses.setText(". . .") # ----------------------------- [42] --- - self.end_button = QPushButton() + self.end_button = QPushButtonWrapper() self.end_button.setMinimumSize(self.button_size) self.end_button.setMaximumSize(self.button_size) # self.end_button.setMaximumHeight(self.button_size.height()) # self.end_button.setStyleSheet('background:red;') # ---------------------------------- [>] - self.next_button = QPushButton() + self.next_button = QPushButtonWrapper() self.next_button.setText(">") self.next_button.setMinimumSize(self.button_size) self.next_button.setMaximumSize(self.button_size) @@ -428,16 +428,15 @@ class Pagination(QWidget, QObject): # print(f'GOTO PAGE: {index}') self.update_buttons(self.page_count, index) - def _assign_click(self, button: QPushButton, index): - try: + def _assign_click(self, button: QPushButtonWrapper, index): + if button.is_connected: button.clicked.disconnect() - except RuntimeError: - pass button.clicked.connect(lambda checked=False, i=index: self._goto_page(i)) + button.is_connected = True def _populate_buffer_buttons(self): for i in range(max(self.buffer_page_count * 2, 5)): - button = QPushButton() + button = QPushButtonWrapper() button.setMinimumSize(self.button_size) button.setMaximumSize(self.button_size) button.setHidden(True) @@ -445,7 +444,7 @@ class Pagination(QWidget, QObject): self.start_buffer_layout.addWidget(button) for i in range(max(self.buffer_page_count * 2, 5)): - button = QPushButton() + button = QPushButtonWrapper() button.setMinimumSize(self.button_size) button.setMaximumSize(self.button_size) button.setHidden(True) diff --git a/tagstudio/src/qt/widgets/fields.py b/tagstudio/src/qt/widgets/fields.py index dfef94e2..355a0fa9 100644 --- a/tagstudio/src/qt/widgets/fields.py +++ b/tagstudio/src/qt/widgets/fields.py @@ -13,6 +13,7 @@ from PIL import Image, ImageQt from PySide6.QtCore import Qt, QEvent from PySide6.QtGui import QPixmap, QEnterEvent from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton +from src.qt.helpers.qbutton_wrapper import QPushButtonWrapper class FieldContainer(QWidget): @@ -81,7 +82,7 @@ class FieldContainer(QWidget): self.title_layout.addStretch(2) - self.copy_button = QPushButton() + self.copy_button = QPushButtonWrapper() self.copy_button.setMinimumSize(button_size, button_size) self.copy_button.setMaximumSize(button_size, button_size) self.copy_button.setFlat(True) @@ -92,7 +93,7 @@ class FieldContainer(QWidget): self.title_layout.addWidget(self.copy_button) self.copy_button.setHidden(True) - self.edit_button = QPushButton() + self.edit_button = QPushButtonWrapper() self.edit_button.setMinimumSize(button_size, button_size) self.edit_button.setMaximumSize(button_size, button_size) self.edit_button.setFlat(True) @@ -101,7 +102,7 @@ class FieldContainer(QWidget): self.title_layout.addWidget(self.edit_button) self.edit_button.setHidden(True) - self.remove_button = QPushButton() + self.remove_button = QPushButtonWrapper() self.remove_button.setMinimumSize(button_size, button_size) self.remove_button.setMaximumSize(button_size, button_size) self.remove_button.setFlat(True) @@ -124,31 +125,30 @@ class FieldContainer(QWidget): # self.set_inner_widget(mode) def set_copy_callback(self, callback: Optional[MethodType]): - try: + if self.copy_button.is_connected: self.copy_button.clicked.disconnect() - except RuntimeError: - pass self.copy_callback = callback self.copy_button.clicked.connect(callback) + if callback is not None: + self.copy_button.is_connected = True def set_edit_callback(self, callback: Optional[MethodType]): - try: + if self.edit_button.is_connected: self.edit_button.clicked.disconnect() - except RuntimeError: - pass self.edit_callback = callback self.edit_button.clicked.connect(callback) + if callback is not None: + self.edit_button.is_connected = True def set_remove_callback(self, callback: Optional[Callable]): - try: + if self.remove_button.is_connected: self.remove_button.clicked.disconnect() - except RuntimeError: - pass self.remove_callback = callback self.remove_button.clicked.connect(callback) + self.remove_button.is_connected = True def set_inner_widget(self, widget: "FieldWidget"): # widget.setStyleSheet('background-color:green;') diff --git a/tagstudio/src/qt/widgets/item_thumb.py b/tagstudio/src/qt/widgets/item_thumb.py index 82c72f4e..5be2e3fb 100644 --- a/tagstudio/src/qt/widgets/item_thumb.py +++ b/tagstudio/src/qt/widgets/item_thumb.py @@ -1,8 +1,7 @@ # Copyright (C) 2024 Travis Abendshien (CyanVoxel). # Licensed under the GPL-3.0 License. # Created for TagStudio: https://github.com/CyanVoxel/TagStudio - - +import contextlib import logging import os import time @@ -396,12 +395,11 @@ class ItemThumb(FlowWidget): def update_clickable(self, clickable: typing.Callable): """Updates attributes of a thumbnail element.""" # logging.info(f'[GUI] Updating Click Event for element {id(element)}: {id(clickable) if clickable else None}') - try: + if self.thumb_button.is_connected: self.thumb_button.clicked.disconnect() - except RuntimeError: - pass if clickable: self.thumb_button.clicked.connect(clickable) + self.thumb_button.is_connected = True def update_badges(self): if self.mode == ItemType.ENTRY: diff --git a/tagstudio/src/qt/widgets/preview_panel.py b/tagstudio/src/qt/widgets/preview_panel.py index 18289c49..4096c0d7 100644 --- a/tagstudio/src/qt/widgets/preview_panel.py +++ b/tagstudio/src/qt/widgets/preview_panel.py @@ -40,6 +40,7 @@ from src.qt.widgets.text import TextWidget from src.qt.widgets.panel import PanelModal from src.qt.widgets.text_box_edit import EditTextBox from src.qt.widgets.text_line_edit import EditTextLine +from src.qt.helpers.qbutton_wrapper import QPushButtonWrapper from src.qt.widgets.video_player import VideoPlayer @@ -61,6 +62,7 @@ class PreviewPanel(QWidget): def __init__(self, library: Library, driver: "QtDriver"): super().__init__() + self.is_connected = False self.lib = library self.driver: QtDriver = driver self.initialized = False @@ -83,7 +85,7 @@ class PreviewPanel(QWidget): self.open_file_action = QAction("Open file", self) self.open_explorer_action = QAction("Open file in explorer", self) - self.preview_img = QPushButton() + self.preview_img = QPushButtonWrapper() self.preview_img.setMinimumSize(*self.img_button_size) self.preview_img.setFlat(True) self.preview_img.setContextMenuPolicy(Qt.ContextMenuPolicy.ActionsContextMenu) @@ -218,7 +220,7 @@ class PreviewPanel(QWidget): self.afb_layout = QVBoxLayout(self.afb_container) self.afb_layout.setContentsMargins(0, 12, 0, 0) - self.add_field_button = QPushButton() + self.add_field_button = QPushButtonWrapper() self.add_field_button.setCursor(Qt.CursorShape.PointingHandCursor) self.add_field_button.setMinimumSize(96, 28) self.add_field_button.setMaximumSize(96, 28) @@ -279,7 +281,9 @@ class PreviewPanel(QWidget): row_layout.addWidget(label) layout.addLayout(row_layout) - def set_button_style(btn: QPushButton, extras: list[str] | None = None): + def set_button_style( + btn: QPushButtonWrapper | QPushButton, extras: list[str] | None = None + ): base_style = [ f"background-color:{Theme.COLOR_BG.value};", "border-radius:6px;", @@ -317,7 +321,6 @@ class PreviewPanel(QWidget): button.clicked.connect(open_library_button_clicked(full_val)) set_button_style(button) - button_remove = QPushButton("➖") button_remove.setCursor(Qt.CursorShape.PointingHandCursor) button_remove.setFixedWidth(30) @@ -411,16 +414,16 @@ class PreviewPanel(QWidget): self.afb_container, Qt.AlignmentFlag.AlignHCenter ) - try: + if self.afm.is_connected: self.afm.done.disconnect() + if self.add_field_button.is_connected: self.add_field_button.clicked.disconnect() - except RuntimeError: - pass # self.afm.done.connect(lambda f: (self.lib.add_field_to_entry(self.selected[0][1], f), self.update_widgets())) self.afm.done.connect( lambda f: (self.add_field_to_selected(f), self.update_widgets()) ) + self.afm.is_connected = True self.add_field_button.clicked.connect(self.afm.show) def add_field_to_selected(self, field_id: int): @@ -466,10 +469,8 @@ class PreviewPanel(QWidget): True, update_on_ratio_change=True, ) - try: + if self.preview_img.is_connected: self.preview_img.clicked.disconnect() - except RuntimeError: - pass for i, c in enumerate(self.containers): c.setHidden(True) self.preview_img.show() @@ -588,14 +589,12 @@ class PreviewPanel(QWidget): f"[PreviewPanel][ERROR] Couldn't Render thumbnail for {filepath} (because of {e})" ) - try: + if self.preview_img.is_connected: self.preview_img.clicked.disconnect() - except RuntimeError: - pass self.preview_img.clicked.connect( lambda checked=False, filepath=filepath: open_file(filepath) ) - + self.preview_img.is_connected = True self.selected = list(self.driver.selected) for i, f in enumerate(item.fields): self.write_container(i, f) @@ -641,10 +640,8 @@ class PreviewPanel(QWidget): True, update_on_ratio_change=True, ) - try: + if self.preview_img.is_connected: self.preview_img.clicked.disconnect() - except RuntimeError: - pass self.common_fields = [] self.mixed_fields = [] @@ -773,12 +770,12 @@ class PreviewPanel(QWidget): """ Replacement for tag_callback. """ - try: + if self.is_connected: self.tags_updated.disconnect() - except RuntimeError: - pass + logging.info("[UPDATE CONTAINER] Setting tags updated slot") self.tags_updated.connect(slot) + self.is_connected = True # def write_container(self, item:Union[Entry, Collation, Tag], index, field): def write_container(self, index, field, mixed=False): @@ -1067,7 +1064,8 @@ class PreviewPanel(QWidget): ) # remove_mb.setStandardButtons(QMessageBox.StandardButton.Cancel) remove_mb.setDefaultButton(cancel_button) + remove_mb.setEscapeButton(cancel_button) result = remove_mb.exec_() # logging.info(result) - if result == 1: + if result == 3: callback() diff --git a/tagstudio/src/qt/widgets/thumb_button.py b/tagstudio/src/qt/widgets/thumb_button.py index 7878259c..179efaec 100644 --- a/tagstudio/src/qt/widgets/thumb_button.py +++ b/tagstudio/src/qt/widgets/thumb_button.py @@ -6,10 +6,11 @@ from PySide6 import QtCore from PySide6.QtCore import QEvent from PySide6.QtGui import QEnterEvent, QPainter, QColor, QPen, QPainterPath, QPaintEvent -from PySide6.QtWidgets import QWidget, QPushButton +from PySide6.QtWidgets import QWidget +from src.qt.helpers.qbutton_wrapper import QPushButtonWrapper -class ThumbButton(QPushButton): +class ThumbButton(QPushButtonWrapper): def __init__(self, parent: QWidget, thumb_size: tuple[int, int]) -> None: super().__init__(parent) self.thumb_size: tuple[int, int] = thumb_size From 501ab1f9772690761982696f1800fa8fed969a79 Mon Sep 17 00:00:00 2001 From: Travis Abendshien Date: Sun, 16 Jun 2024 19:54:56 -0700 Subject: [PATCH 8/9] Update issue templates to ask for title --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 ++ .github/ISSUE_TEMPLATE/feature_request.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index a30593af..97cc8dc1 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -6,6 +6,8 @@ body: - type: markdown attributes: value: | + *Please add an appropriate title for this issue.* + Before reporting, read the [documentation](https://github.com/TagStudioDev/TagStudio/blob/main/doc/index.md) and search existing [issues](https://github.com/TagStudioDev/TagStudio/issues). Validate that you are using an up-to-date version[^1], your issue might already be fixed! Questions, guidance, and usage goes in [discussions](https://github.com/TagStudioDev/TagStudio/discussions). Invalid issues will be closed. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 916fd5d9..3f98ccf0 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -6,6 +6,8 @@ body: - type: markdown attributes: value: | + *Please add an appropriate title for this feature request.* + Before suggesting, read the [documentation](https://github.com/TagStudioDev/TagStudio/blob/main/doc/index.md) and search existing [issues](https://github.com/TagStudioDev/TagStudio/issues). Validate that you are using an up-to-date version[^1], your feature might already be implemented! Questions, guidance, and usage goes in [discussions](https://github.com/TagStudioDev/TagStudio/discussions). Invalid issues will be closed. From 1204d2b7b50c4401a6b444ca82bf6421aa6d4789 Mon Sep 17 00:00:00 2001 From: Travis Abendshien Date: Fri, 21 Jun 2024 11:28:12 -0700 Subject: [PATCH 9/9] Fix search ignoring case of extension list --- tagstudio/src/core/library.py | 8 ++++---- tagstudio/src/qt/modals/file_extension.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tagstudio/src/core/library.py b/tagstudio/src/core/library.py index 2e5843df..107b88cf 100644 --- a/tagstudio/src/core/library.py +++ b/tagstudio/src/core/library.py @@ -889,12 +889,12 @@ class Library: and "tagstudio_thumbs" not in f.parts and not f.is_dir() ): - if f.suffix not in self.ext_list and self.is_exclude_list: + if f.suffix.lower() not in self.ext_list and self.is_exclude_list: self.dir_file_count += 1 file = f.relative_to(self.library_dir) if file not in self.filename_to_entry_id_map: self.files_not_in_library.append(file) - elif f.suffix in self.ext_list and not self.is_exclude_list: + elif f.suffix.lower() in self.ext_list and not self.is_exclude_list: self.dir_file_count += 1 file = f.relative_to(self.library_dir) try: @@ -1382,7 +1382,7 @@ class Library: # non_entry_count = 0 # Iterate over all Entries ============================================================= for entry in self.entries: - allowed_ext: bool = entry.filename.suffix not in self.ext_list + allowed_ext: bool = entry.filename.suffix.lower() not in self.ext_list # try: # entry: Entry = self.entries[self.file_to_library_index_map[self._source_filenames[i]]] # print(f'{entry}') @@ -1539,7 +1539,7 @@ class Library: else: for entry in self.entries: added = False - allowed_ext = entry.filename.suffix not in self.ext_list + allowed_ext = entry.filename.suffix.lower() not in self.ext_list if allowed_ext == self.is_exclude_list: for f in entry.fields: if self.get_field_attr(f, "type") == "collation": diff --git a/tagstudio/src/qt/modals/file_extension.py b/tagstudio/src/qt/modals/file_extension.py index 00fc2607..c95b520b 100644 --- a/tagstudio/src/qt/modals/file_extension.py +++ b/tagstudio/src/qt/modals/file_extension.py @@ -110,4 +110,4 @@ class FileExtensionModal(PanelWidget): for i in range(self.table.rowCount()): ext = self.table.item(i, 0) if ext and ext.text(): - self.lib.ext_list.append(ext.text()) + self.lib.ext_list.append(ext.text().lower())