aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoris Guyonvarch2025-12-30 17:34:13 +0100
committerJoris Guyonvarch2025-12-30 17:34:13 +0100
commit65cae8a887504f49735aa9035ae53fcffd10fbc9 (patch)
tree49bd8ddca5be035b51a08a9516d1ee78d18941c4
parent9566791adef22f0f1102bf73f0ba02ae9842b7cf (diff)
Add textual search
Search in titles, subtitles, authors and years.
-rw-r--r--README.md4
-rw-r--r--src/book_flow.py40
-rw-r--r--src/filters.py13
-rw-r--r--src/main_window.py11
-rw-r--r--src/str_format.py3
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')