From a64fa9770f2a8851cd3a382f30847178b5456a61 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Wed, 3 Sep 2025 12:32:05 +0200 Subject: [PATCH] refactor searchBook to allow for regex matches --- src/backend/database.py | 81 +++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/src/backend/database.py b/src/backend/database.py index e2e5d03..32b60c6 100644 --- a/src/backend/database.py +++ b/src/backend/database.py @@ -1,6 +1,7 @@ import datetime import json import os +import re import sqlite3 as sql import sys import tempfile @@ -382,49 +383,67 @@ class Database: """ return self.query_db("SELECT id FROM media ORDER BY id DESC", one=True)[0] - def searchBook(self, data: dict[str, str]) -> list[tuple[BookData, int]]: + def searchBook( + self, data: dict[str, str] + ) -> Optional[list[tuple["BookData", int, int]]]: """ - Search a book in the database based on the sent data. + Search a book in the database using regex against signature/title. Args: - data (dict[str, str]): A dictionary containing the data to be searched for. The dictionary can contain the following: - - signature: The signature of the book - - title: The title of the book + data: may contain: + - "signature": regex to match against BookData.signature + - "title": regex to match against BookData.title Returns: - list[tuple[BookData, int]]: A list of tuples containing the wrapped Metadata and the id of the book + list of (BookData, app_id, prof_id) tuples, or None if invalid args """ - rdata = self.query_db("SELECT * FROM media WHERE deleted=0") - # log.debug(rdata, len(rdata)) + + # Determine mode (kept compatible with your original logic) mode = 0 - if len(data) == 1: - if "signature" in data.keys(): - mode = 1 - elif "title" in data.keys(): - mode = 2 - elif len(data) == 2: + if len(data) == 1 and "signature" in data: + mode = 1 + elif len(data) == 1 and "title" in data: + mode = 2 + elif len(data) == 2 and "signature" in data and "title" in data: mode = 3 else: return None - ret = [] - for book in rdata: - bookdata = BookData().from_string(book[1]) - app_id = book[2] - prof_id = book[3] + + def _compile(expr: str) -> re.Pattern: + try: + return re.compile(expr, re.IGNORECASE | re.UNICODE) + except re.error: + # If user provided a broken regex, treat it as a literal + return re.compile(re.escape(expr), re.IGNORECASE | re.UNICODE) + + sig_re = _compile(data["signature"]) if mode in (1, 3) else None + title_re = _compile(data["title"]) if mode in (2, 3) else None + + # Fetch candidates once + rows = self.query_db("SELECT * FROM media WHERE deleted=0") + + results: list[tuple["BookData", int, int]] = [] + for row in rows: + bookdata = BookData().from_string( + row[1] + ) # assumes row[1] is the serialized bookdata + app_id = row[2] + prof_id = row[3] + + sig_val = getattr(bookdata, "signature", None) or "" + title_val = getattr(bookdata, "title", None) or "" + if mode == 1: - if data["signature"] in bookdata.signature: - ret.append((bookdata, app_id, prof_id)) + if sig_re.search(sig_val): + results.append((bookdata, app_id, prof_id)) elif mode == 2: - if data["title"] in bookdata.title: - ret.append((bookdata, app_id, prof_id)) - elif mode == 3: - if ( - data["signature"] in bookdata.signature - and data["title"] in bookdata.title - ): - ret.append((bookdata, app_id, prof_id)) - # log.debug(ret) - return ret + if title_re.search(title_val): + results.append((bookdata, app_id, prof_id)) + else: # mode == 3 + if sig_re.search(sig_val) and title_re.search(title_val): + results.append((bookdata, app_id, prof_id)) + + return results def setAvailability(self, book_id: str, available: str): """