From 65cae8a887504f49735aa9035ae53fcffd10fbc9 Mon Sep 17 00:00:00 2001 From: Joris Guyonvarch Date: Tue, 30 Dec 2025 17:34:13 +0100 Subject: Add textual search Search in titles, subtitles, authors and years. --- README.md | 4 ---- src/book_flow.py | 40 ++++++++++++++++++++++++++++++---------- src/filters.py | 13 ++++++++++++- src/main_window.py | 11 +++++++---- src/str_format.py | 3 +++ 5 files changed, 52 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 04bdb2a..5eb839b 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,3 @@ nix develop --command books ```sh pytest ``` - -# Improvements - -- filters: textual search diff --git a/src/book_flow.py b/src/book_flow.py index bfd7114..dc9cad6 100644 --- a/src/book_flow.py +++ b/src/book_flow.py @@ -16,11 +16,12 @@ from src.book_form import BookForm from src.book_transfer import BookTransfer from src.picture_cache import PictureCache import src.book_files as book_files +import src.str_format as str_format class BookFlow(Gtk.ScrolledWindow): - def __init__(self, window, resources, library, ereader, conn, books, progress, genre, msg): + def __init__(self, window, resources, library, ereader, conn, books, progress, genre, search, msg): Gtk.ScrolledWindow.__init__(self) self.set_vexpand(True) @@ -37,7 +38,7 @@ class BookFlow(Gtk.ScrolledWindow): self._picture_cache = PictureCache() self._flowbox_children = {} - self.update_filters(progress, genre) + self.update_filters(progress, genre, search) self._flowbox.set_sort_func(self._sort_books) for book_id, data in books.items(): self.add(book_id, data) @@ -49,11 +50,11 @@ class BookFlow(Gtk.ScrolledWindow): if book_id in self._flowbox_children: self._flowbox.select_child(self._flowbox_children[book_id]) - def update_filters(self, progress, genre): + def update_filters(self, progress, genre, search): def f(flow_box_child): book_id = flow_box_child.get_name() data = self._books[book_id] - return self._is_selected(data, progress, genre) + return self._is_selected(data, progress, genre, search) self._flowbox.set_filter_func(f) @@ -111,14 +112,14 @@ class BookFlow(Gtk.ScrolledWindow): return 0 - def _is_selected(self, data, progress, genre): + def _is_selected(self, data, progress, genre, search): if data['progress'] != progress: return False - if genre == models.all_genres: - return True - if genre == models.no_genre: - return len(data['genres']) == 0 - return genre in data['genres'] + if genre == models.no_genre and len(data['genres']) > 0: + return False + if genre != models.all_genres and not genre in data['genres']: + return False + return match_search(data, search) def _on_left_click(self, gesture, n_press, x, y, book_id, data): if n_press == 2: @@ -183,3 +184,22 @@ def year_key(data): except Exception: return 0 return 0 + +def match_search(data, search): + for word in search.split(): + if not match_word(data, word): + return False + return True + +def match_word(data, word): + s = str_format.for_search(word) + if s in str_format.for_search(data['title']): + return True + if 'subtitle' in data and s in str_format.for_search(data['subtitle']): + return True + for author in data['authors']: + if s in str_format.for_search(author): + return True + if 'year' in data and s == str(data['year']): + return True + return False diff --git a/src/filters.py b/src/filters.py index ed3cb06..c652605 100644 --- a/src/filters.py +++ b/src/filters.py @@ -6,12 +6,13 @@ import src.models as models class Filters(Gtk.Box): - def __init__(self, init_books, init_progress, init_genre, msg): + def __init__(self, init_books, init_progress, init_genre, init_search, msg): Gtk.Box.__init__(self, spacing=10) genres = models.get_genres(init_books) self._progress = init_progress self._genre = init_genre + self._search = init_search self._msg = msg self._progress_dropdown = Gtk.DropDown.new_from_strings(models.all_progress) @@ -22,6 +23,10 @@ class Filters(Gtk.Box): self._genres_dropdown = self._get_genres_dropdown(genres) self.append(self._genres_dropdown) + self._search = Gtk.Entry() + self._search.connect('changed', self._on_update_search) + self.append(self._search) + def get_progress(self): return self._progress @@ -31,6 +36,9 @@ class Filters(Gtk.Box): def set_genre(self, genre): self._genre = genre + def get_search(self): + return self._search.get_text() + def select_progress(self, progress): self._progress = progress self._progress_dropdown.set_selected(models.all_progress.index(progress)) @@ -62,3 +70,6 @@ class Filters(Gtk.Box): def _on_selected_genre(self, dropdown, _data): self._genre = dropdown.get_selected_item().get_string() self._msg(['genre-selected', self._genre]) + + def _on_update_search(self, entry): + self._msg(['search-updated', self._search.get_text()]) diff --git a/src/main_window.py b/src/main_window.py index c01c120..bc9fde2 100644 --- a/src/main_window.py +++ b/src/main_window.py @@ -21,29 +21,32 @@ class MainWindow(Gtk.ApplicationWindow): init_books = db.get_books(conn) init_progress = 'Reading' init_genre = models.all_genres + init_search = '' add_book_button = Gtk.Button(label='Ajouter un livre') add_book_button.connect('clicked', lambda _: BookForm(self, resources, library, conn, self._msg).present()) header = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) utils.set_margin(header, 20) - self._filters = Filters(init_books, init_progress, init_genre, self._msg) + self._filters = Filters(init_books, init_progress, init_genre, init_search, self._msg) self._filters.set_hexpand(True) header.append(self._filters) header.append(add_book_button) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) box.append(header) - self._books = BookFlow(self, resources, library, ereader, conn, init_books, init_progress, init_genre, self._msg) + self._books = BookFlow(self, resources, library, ereader, conn, init_books, init_progress, init_genre, init_search, self._msg) box.append(self._books) self.set_child(box) def _msg(self, msg): match msg: case ['progress-selected', progress]: - self._books.update_filters(self._filters.get_progress(), self._filters.get_genre()) + self._books.update_filters(self._filters.get_progress(), self._filters.get_genre(), self._filters.get_search()) case ['genre-selected', genre]: - self._books.update_filters(self._filters.get_progress(), self._filters.get_genre()) + self._books.update_filters(self._filters.get_progress(), self._filters.get_genre(), self._filters.get_search()) + case ['search-updated', search]: + self._books.update_filters(self._filters.get_progress(), self._filters.get_genre(), self._filters.get_search()) case ['book-saved', book_id, data]: self._filters.select_progress(data['progress']) if not self._filters.get_genre() in data['genres']: diff --git a/src/str_format.py b/src/str_format.py index 5d8c412..aaa0440 100644 --- a/src/str_format.py +++ b/src/str_format.py @@ -6,6 +6,9 @@ def safe_path(name): simplified = ''.join([alnum_or_space(c) for c in unaccent(name.lower())]) return '-'.join(simplified.split()) +def for_search(s): + return unaccent(s.lower()) + def unaccent(s): return ''.join(c for c in unicodedata.normalize('NFD', s) if unicodedata.category(c) != 'Mn') -- cgit v1.2.3