feat: sort by "date added" to library (#674)

* ui: add sorting mode dropdown

* feat: pass sorting mode to Library.search_library

* feat: implement sorting by creation date

* ui: add dropdown for sorting direction

* ui: update shown entries after changing sorting mode / direction

* docs: mark sorting by "Date Created" as completed

* fix: remove sorting options that have not been implemented

* fix: rename sorting mode to "Date Added"

* fix: check off the right item on the roadmap

* feat: translate sorting UI

* fix: address review comments
This commit is contained in:
Jann Stute
2025-01-10 16:46:23 +01:00
committed by GitHub
parent 936157c3c2
commit 3cd56a881f
6 changed files with 84 additions and 3 deletions

View File

@@ -99,6 +99,7 @@ Features are broken up into the following priority levels, with nested prioritie
- [ ] Fuzzy Search [LOW] [#400](https://github.com/TagStudioDev/TagStudio/issues/400)
- [ ] Sortable results [HIGH] [#68](https://github.com/TagStudioDev/TagStudio/issues/68)
- [ ] Sort by relevance [HIGH]
- [x] Sort by date added [HIGH]
- [ ] Sort by date created [HIGH]
- [ ] Sort by date modified [HIGH]
- [ ] Sort by date taken (photos) [MEDIUM]
@@ -182,6 +183,7 @@ These version milestones are rough estimations for when the previous core featur
- [ ] Field content search [HIGH]
- [ ] Sortable results [HIGH]
- [ ] Sort by relevance [HIGH]
- [x] Sort by date added [HIGH]
- [ ] Sort by date created [HIGH]
- [ ] Sort by date modified [HIGH]
- [ ] Sort by date taken (photos) [MEDIUM]

View File

@@ -212,5 +212,7 @@
"view.size.4": "Extra Large",
"window.message.error_opening_library": "Error opening library.",
"window.title.error": "Error",
"window.title.open_create_library": "Open/Create Library"
"window.title.open_create_library": "Open/Create Library",
"sorting.direction.ascending": "Ascending",
"sorting.direction.descending": "Descending"
}

View File

@@ -59,6 +59,10 @@ class ItemType(enum.Enum):
TAG_GROUP = 2
class SortingModeEnum(enum.Enum):
DATE_ADDED = "file.date_added"
@dataclass
class FilterState:
"""Represent a state of the Library grid view."""
@@ -66,6 +70,8 @@ class FilterState:
# these should remain
page_index: int | None = 0
page_size: int | None = 500
sorting_mode: SortingModeEnum = SortingModeEnum.DATE_ADDED
ascending: bool = True
# these should be erased on update
# Abstract Syntax Tree Of the current Search Query
@@ -110,6 +116,12 @@ class FilterState:
def with_page_size(self, page_size: int) -> "FilterState":
return replace(self, page_size=page_size)
def with_sorting_mode(self, mode: SortingModeEnum) -> "FilterState":
return replace(self, sorting_mode=mode)
def with_sorting_direction(self, ascending: bool) -> "FilterState":
return replace(self, ascending=ascending)
class FieldTypeEnum(enum.Enum):
TEXT_LINE = "Text Line"

View File

@@ -14,11 +14,14 @@ import structlog
from humanfriendly import format_timespan
from sqlalchemy import (
URL,
ColumnExpressionArgument,
Engine,
NullPool,
and_,
asc,
create_engine,
delete,
desc,
exists,
func,
or_,
@@ -42,7 +45,7 @@ from ...constants import (
)
from ...enums import LibraryPrefs
from .db import make_tables
from .enums import FieldTypeEnum, FilterState, TagColor
from .enums import FieldTypeEnum, FilterState, SortingModeEnum, TagColor
from .fields import (
BaseField,
DatetimeField,
@@ -576,6 +579,13 @@ class Library:
query_count = select(func.count()).select_from(statement.alias("entries"))
count_all: int = session.execute(query_count).scalar()
sort_on: ColumnExpressionArgument = Entry.id
match search.sorting_mode:
case SortingModeEnum.DATE_ADDED:
sort_on = Entry.id
statement = statement.order_by(asc(sort_on) if search.ascending else desc(sort_on))
statement = statement.limit(search.limit).offset(search.offset)
logger.info(

View File

@@ -72,6 +72,15 @@ class Ui_MainWindow(QMainWindow):
spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_3.addItem(spacerItem)
# Sorting Dropdowns
self.sorting_mode_combobox = QComboBox(self.centralwidget)
self.sorting_mode_combobox.setObjectName(u"sortingModeComboBox")
self.horizontalLayout_3.addWidget(self.sorting_mode_combobox)
self.sorting_direction_combobox = QComboBox(self.centralwidget)
self.sorting_direction_combobox.setObjectName(u"sortingDirectionCombobox")
self.horizontalLayout_3.addWidget(self.sorting_direction_combobox)
# Thumbnail Size placeholder
self.thumb_size_combobox = QComboBox(self.centralwidget)
self.thumb_size_combobox.setObjectName(u"thumbSizeComboBox")

View File

@@ -65,6 +65,7 @@ from src.core.library.alchemy.enums import (
FieldTypeEnum,
FilterState,
ItemType,
SortingModeEnum,
)
from src.core.library.alchemy.fields import _FieldID
from src.core.library.alchemy.library import Entry, LibraryStatus
@@ -551,16 +552,40 @@ class QtDriver(DriverMixin, QObject):
search_button.clicked.connect(
lambda: self.filter_items(
FilterState.from_search_query(self.main_window.searchField.text())
.with_sorting_mode(self.sorting_mode)
.with_sorting_direction(self.sorting_direction)
)
)
# Search Field
search_field: QLineEdit = self.main_window.searchField
search_field.returnPressed.connect(
# TODO - parse search field for filters
lambda: self.filter_items(
FilterState.from_search_query(self.main_window.searchField.text())
.with_sorting_mode(self.sorting_mode)
.with_sorting_direction(self.sorting_direction)
)
)
# Sorting Dropdowns
sort_mode_dropdown: QComboBox = self.main_window.sorting_mode_combobox
for sort_mode in SortingModeEnum:
sort_mode_dropdown.addItem(Translations[sort_mode.value], sort_mode)
sort_mode_dropdown.setCurrentIndex(
list(SortingModeEnum).index(self.filter.sorting_mode)
) # set according to self.filter
sort_mode_dropdown.currentIndexChanged.connect(self.sorting_mode_callback)
sort_dir_dropdown: QComboBox = self.main_window.sorting_direction_combobox
sort_dir_dropdown.addItem("Ascending", userData=True)
sort_dir_dropdown.addItem("Descending", userData=False)
Translations.translate_with_setter(
lambda text: sort_dir_dropdown.setItemText(0, text), "sorting.direction.ascending"
)
Translations.translate_with_setter(
lambda text: sort_dir_dropdown.setItemText(1, text), "sorting.direction.descending"
)
sort_dir_dropdown.setCurrentIndex(0) # Default: Ascending
sort_dir_dropdown.currentIndexChanged.connect(self.sorting_direction_callback)
# Thumbnail Size ComboBox
thumb_size_combobox: QComboBox = self.main_window.thumb_size_combobox
for size in self.thumb_sizes:
@@ -880,6 +905,24 @@ class QtDriver(DriverMixin, QObject):
content=strip_web_protocol(field.value),
)
@property
def sorting_direction(self) -> bool:
"""Whether to Sort the results in ascending order."""
return self.main_window.sorting_direction_combobox.currentData()
def sorting_direction_callback(self):
logger.info("Sorting Direction Changed", ascending=self.sorting_direction)
self.filter_items()
@property
def sorting_mode(self) -> SortingModeEnum:
"""What to sort by."""
return self.main_window.sorting_mode_combobox.currentData()
def sorting_mode_callback(self):
logger.info("Sorting Mode Changed", mode=self.sorting_mode)
self.filter_items()
def thumb_size_callback(self, index: int):
"""Perform actions needed when the thumbnail size selection is changed.
@@ -1192,6 +1235,9 @@ class QtDriver(DriverMixin, QObject):
if filter:
self.filter = dataclasses.replace(self.filter, **dataclasses.asdict(filter))
else:
self.filter.sorting_mode = self.sorting_mode
self.filter.ascending = self.sorting_direction
# inform user about running search
self.main_window.statusbar.showMessage(Translations["status.library_search_query"])