Skip to content

Commit 3183324

Browse files
feat: add filename and path sorting (#842)
* feat: add filename sorting to dropdown * feat: add file path sorting to dropdown * feat: implement path sorting * feat: add filename column and bump db version * feat: implement filename sorting * doc: tick off roadmap item for filename sorting * fix: use existing filename translation instead * fix: populate Entry.filename in constructor * fix: add missing assertion in search_library fixture * fix: update search test library * feat: add db migration test * fix: add missing library for test
1 parent 93dcfdd commit 3183324

File tree

11 files changed

+54
-4
lines changed

11 files changed

+54
-4
lines changed

docs/updates/roadmap.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ These version milestones are rough estimations for when the previous core featur
9494
- [ ] Field content search [HIGH]
9595
- [ ] Sort by date created [HIGH]
9696
- [ ] Sort by date modified [HIGH]
97-
- [ ] Sort by filename [HIGH]
97+
- [x] Sort by filename [HIGH]
9898
- [ ] HAS operator for composition tags [HIGH]
9999
- [ ] Search bar rework
100100
- [ ] Improved tag autocomplete [HIGH]

src/tagstudio/core/enums.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,4 @@ class LibraryPrefs(DefaultEnum):
8282
IS_EXCLUDE_LIST = True
8383
EXTENSION_LIST = [".json", ".xmp", ".aae"]
8484
PAGE_SIZE = 500
85-
DB_VERSION = 8
85+
DB_VERSION = 9

src/tagstudio/core/library/alchemy/enums.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ class ItemType(enum.Enum):
6767

6868
class SortingModeEnum(enum.Enum):
6969
DATE_ADDED = "file.date_added"
70+
FILE_NAME = "generic.filename"
71+
PATH = "file.path"
7072

7173

7274
@dataclass

src/tagstudio/core/library/alchemy/library.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,12 +472,25 @@ def open_sqlite_library(self, library_dir: Path, is_new: bool) -> LibraryStatus:
472472

473473
# Apply any post-SQL migration patches.
474474
if not is_new:
475+
# save backup if patches will be applied
476+
if LibraryPrefs.DB_VERSION.default != db_version:
477+
self.library_dir = library_dir
478+
self.save_library_backup_to_disk()
479+
self.library_dir = None
480+
481+
# schema changes first
475482
if db_version < 8:
476483
self.apply_db8_schema_changes(session)
484+
if db_version < 9:
485+
self.apply_db9_schema_changes(session)
486+
487+
# now the data changes
477488
if db_version == 6:
478489
self.apply_repairs_for_db6(session)
479490
if db_version >= 6 and db_version < 8:
480491
self.apply_db8_default_data(session)
492+
if db_version < 9:
493+
self.apply_db9_filename_population(session)
481494

482495
# Update DB_VERSION
483496
if LibraryPrefs.DB_VERSION.default > db_version:
@@ -580,6 +593,29 @@ def apply_db8_default_data(self, session: Session):
580593
)
581594
session.rollback()
582595

596+
def apply_db9_schema_changes(self, session: Session):
597+
"""Apply database schema changes introduced in DB_VERSION 9."""
598+
add_filename_column = text(
599+
"ALTER TABLE entries ADD COLUMN filename TEXT NOT NULL DEFAULT ''"
600+
)
601+
try:
602+
session.execute(add_filename_column)
603+
session.commit()
604+
logger.info("[Library][Migration] Added filename column to entries table")
605+
except Exception as e:
606+
logger.error(
607+
"[Library][Migration] Could not create filename column in entries table!",
608+
error=e,
609+
)
610+
session.rollback()
611+
612+
def apply_db9_filename_population(self, session: Session):
613+
"""Populate the filename column introduced in DB_VERSION 9."""
614+
for entry in self.get_entries():
615+
session.merge(entry).filename = entry.path.name
616+
session.commit()
617+
logger.info("[Library][Migration] Populated filename column in entries table")
618+
583619
@property
584620
def default_fields(self) -> list[BaseField]:
585621
with Session(self.engine) as session:
@@ -852,14 +888,18 @@ def search_library(
852888
statement = statement.distinct(Entry.id)
853889
start_time = time.time()
854890
query_count = select(func.count()).select_from(statement.alias("entries"))
855-
count_all: int = session.execute(query_count).scalar()
891+
count_all: int = session.execute(query_count).scalar() or 0
856892
end_time = time.time()
857893
logger.info(f"finished counting ({format_timespan(end_time - start_time)})")
858894

859895
sort_on: ColumnExpressionArgument = Entry.id
860896
match search.sorting_mode:
861897
case SortingModeEnum.DATE_ADDED:
862898
sort_on = Entry.id
899+
case SortingModeEnum.FILE_NAME:
900+
sort_on = func.lower(Entry.filename)
901+
case SortingModeEnum.PATH:
902+
sort_on = func.lower(Entry.path)
863903

864904
statement = statement.order_by(asc(sort_on) if search.ascending else desc(sort_on))
865905
statement = statement.limit(search.limit).offset(search.offset)
@@ -1371,6 +1411,8 @@ def save_library_backup_to_disk(self) -> Path:
13711411
target_path,
13721412
)
13731413

1414+
logger.info("Library backup saved to disk.", path=target_path)
1415+
13741416
return target_path
13751417

13761418
def get_tag(self, tag_id: int) -> Tag | None:

src/tagstudio/core/library/alchemy/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ class Entry(Base):
187187
folder: Mapped[Folder] = relationship("Folder")
188188

189189
path: Mapped[Path] = mapped_column(PathType, unique=True)
190+
filename: Mapped[str] = mapped_column()
190191
suffix: Mapped[str] = mapped_column()
191192
date_created: Mapped[dt | None]
192193
date_modified: Mapped[dt | None]
@@ -232,6 +233,7 @@ def __init__(
232233
self.path = path
233234
self.folder = folder
234235
self.id = id
236+
self.filename = path.name
235237
self.suffix = path.suffix.lstrip(".").lower()
236238

237239
# The date the file associated with this entry was created.

src/tagstudio/resources/translations/de.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
"file.date_added": "Hinzufügungsdatum",
6767
"file.date_created": "Erstellungsdatum",
6868
"file.date_modified": "Datum geändert",
69+
"file.path": "Dateipfad",
6970
"file.dimensions": "Abmessungen",
7071
"file.duplicates.description": "TagStudio unterstützt das Importieren von DupeGuru-Ergebnissen um Dateiduplikate zu verwalten.",
7172
"file.duplicates.dupeguru.advice": "Nach dem Kopiervorgang kann DupeGuru benutzt werden und ungewollte Dateien zu löschen. Anschließend kann TagStudios \"Unverknüpfte Einträge reparieren\" Funktion im \"Werkzeuge\" Menü benutzt werden um die nicht verknüpften Einträge zu löschen.",

src/tagstudio/resources/translations/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
"file.date_added": "Date Added",
6969
"file.date_created": "Date Created",
7070
"file.date_modified": "Date Modified",
71+
"file.path": "File Path",
7172
"file.dimensions": "Dimensions",
7273
"file.duplicates.description": "TagStudio supports importing DupeGuru results to manage duplicate files.",
7374
"file.duplicates.dupeguru.advice": "After mirroring, you're free to use DupeGuru to delete the unwanted files. Afterwards, use TagStudio's \"Fix Unlinked Entries\" feature in the Tools menu in order to delete the unlinked Entries.",

tests/conftest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ def library(request):
114114
@pytest.fixture
115115
def search_library() -> Library:
116116
lib = Library()
117-
lib.open_library(Path(CWD / "fixtures" / "search_library"))
117+
status = lib.open_library(Path(CWD / "fixtures" / "search_library"))
118+
assert status.success
118119
return lib
119120

120121

Binary file not shown.
Binary file not shown.

tests/test_db_migrations.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
str(Path(CWD.parent / FIXTURES / EMPTY_LIBRARIES / "DB_VERSION_6")),
2323
str(Path(CWD.parent / FIXTURES / EMPTY_LIBRARIES / "DB_VERSION_7")),
2424
str(Path(CWD.parent / FIXTURES / EMPTY_LIBRARIES / "DB_VERSION_8")),
25+
str(Path(CWD.parent / FIXTURES / EMPTY_LIBRARIES / "DB_VERSION_9")),
2526
],
2627
)
2728
def test_library_migrations(path: str):

0 commit comments

Comments
 (0)