From: Oleg Broytman Date: Fri, 29 Dec 2023 08:44:38 +0000 (+0300) Subject: Merge branch 'master' into wx X-Git-Tag: 0.3.0~9^2~31 X-Git-Url: https://git.phdru.name/?a=commitdiff_plain;h=c48fbfdbf80a404d5446d6c003864b032150ceaf;hp=4fdb8f9855aa5f32911776804cb6ce18feefc637;p=m_librarian.git Merge branch 'master' into wx * master: Refactor: Separate web forms processing from search Perf(web/app): Get `download_path` only if there're books to download Style: Re-group and reorder imports --- diff --git a/m_librarian/db.py b/m_librarian/db.py index d910688..064ca9a 100755 --- a/m_librarian/db.py +++ b/m_librarian/db.py @@ -2,9 +2,11 @@ from __future__ import print_function import os + from sqlobject import SQLObject, StringCol, UnicodeCol, IntCol, BoolCol, \ ForeignKey, DateCol, DatabaseIndex, RelatedJoin, \ connectionForURI, sqlhub, SQLObjectNotFound, dberrors + from .compat import string_type from .config import get_config diff --git a/m_librarian/glst.py b/m_librarian/glst.py index fedb27b..206dcc4 100755 --- a/m_librarian/glst.py +++ b/m_librarian/glst.py @@ -1,9 +1,10 @@ #! /usr/bin/env python from __future__ import print_function -import codecs from glob import glob +import codecs import os + from sqlobject import sqlhub, SQLObjectNotFound from .db import Genre diff --git a/m_librarian/inp.py b/m_librarian/inp.py index 3789dd2..f910b10 100644 --- a/m_librarian/inp.py +++ b/m_librarian/inp.py @@ -1,8 +1,10 @@ import os from zipfile import ZipFile + from sqlobject import sqlhub from sqlobject.sqlbuilder import Select + from .db import Author, Book, Extension, Genre, Language, \ insert_name, insert_author diff --git a/m_librarian/search.py b/m_librarian/search.py index 82889a8..c190dc8 100644 --- a/m_librarian/search.py +++ b/m_librarian/search.py @@ -1,4 +1,5 @@ -from sqlobject.sqlbuilder import AND, OR, func +from sqlobject.sqlbuilder import AND, OR, func, CONCAT + from .config import get_config from .db import Author, Book, Extension, Genre, Language @@ -109,3 +110,83 @@ def search_genres(search_type, case_sensitive, values, orderBy=None): def search_languages(search_type, case_sensitive, values, orderBy=None): return _search(Language, search_type, case_sensitive, values, orderBy=orderBy) + + +def decode(value): + if isinstance(value, bytes): + return value.decode('utf-8') + return value + + +def _guess_case_sensitivity(value): + return not value.islower() + + +def search_authors_raw(value, search_type, case_sensitive): + value = decode(value) + if not search_type: + search_type = 'start' + if case_sensitive is None: + case_sensitive = _guess_case_sensitivity(value) + expressions = [( + CONCAT(Author.q.surname, ' ', Author.q.name, ' ', Author.q.misc_name), + decode(value) + )] + authors = search_authors(search_type, case_sensitive, {}, expressions, + orderBy=('surname', 'name', 'misc_name')) + columns = get_config().getlist('columns', 'author', ['fullname']) + return { + 'authors': list(authors), + 'search_authors': value, + 'search_type': search_type, + 'case_sensitive': case_sensitive, + 'columns': columns, + } + + +def books_by_author(aid): + use_filters = get_config().getint('filters', 'use_in_books_list', 1) + columns = get_config().getlist('columns', 'book', ['title']) + author = Author.get(aid) + if use_filters: + join_expressions = [] + join_expressions.append(Book.j.authors) + join_expressions.append(Author.q.id == aid) + books = search_books('full', None, {}, join_expressions, + orderBy=('series', 'ser_no', 'title', '-date'), + use_filters=use_filters) + else: + books = Book.select( + Book.j.authors & (Author.q.id == aid), + orderBy=['series', 'ser_no', 'title'], + ) + return { + 'books_by_author': {author.fullname: list(books)}, + 'columns': columns, + } + + +def search_books_raw(value, search_type, case_sensitive, use_filters): + value = decode(value) + if not search_type: + search_type = 'start' + if case_sensitive is None: + case_sensitive = _guess_case_sensitivity(value) + books = search_books(search_type, case_sensitive, {'title': value}, None, + orderBy=('title',), use_filters=use_filters) + books_by_authors = {} + for book in books: + author = book.author1 + if author in books_by_authors: + books_by_author = books_by_authors[author] + else: + books_by_author = books_by_authors[author] = [] + books_by_author.append(book) + columns = get_config().getlist('columns', 'book', ['title']) + return { + 'books_by_author': books_by_authors, + 'search_books': value, + 'search_type': search_type, + 'case_sensitive': case_sensitive, + 'columns': columns, + } diff --git a/m_librarian/web/app.py b/m_librarian/web/app.py index a5ea5b2..283ddb7 100644 --- a/m_librarian/web/app.py +++ b/m_librarian/web/app.py @@ -1,14 +1,12 @@ # -*- coding: utf-8 -*- import os - -from sqlobject.sqlbuilder import CONCAT from bottle import cheetah_view, redirect, request, route, static_file from ..config import get_config -from ..db import Author, Book +from ..db import Book from ..download import download -from ..search import search_authors, search_books +from ..search import search_authors_raw, books_by_author, search_books_raw @route('/') @@ -30,68 +28,21 @@ def search_authors_get(): return {} -def decode(value): - if isinstance(value, bytes): - return value.decode('utf-8') - return value - - -def _guess_case_sensitivity(value): - return not value.islower() - - @route('/search_authors/', method='POST') @cheetah_view('list_authors.tmpl') def search_authors_post(): value = request.forms.get('search_authors') if not value: return redirect('/search_authors/') - value = decode(value) search_type = request.forms.get('search_type') - if not search_type: - search_type = 'start' case_sensitive = request.forms.get('case_sensitive') - if case_sensitive is None: - case_sensitive = _guess_case_sensitivity(value) - expressions = [( - CONCAT(Author.q.surname, ' ', Author.q.name, ' ', Author.q.misc_name), - decode(value) - )] - authors = search_authors(search_type, case_sensitive, {}, expressions, - orderBy=('surname', 'name', 'misc_name')) - columns = get_config().getlist('columns', 'author', ['fullname']) - return { - 'authors': list(authors), - 'search_authors': value, - 'search_type': search_type, - 'case_sensitive': case_sensitive, - 'columns': columns, - } + return search_authors_raw(value, search_type, case_sensitive) @route('/books-by-author//', method='GET') @cheetah_view('list_books.tmpl') -def books_by_author(aid): - use_filters = get_config().getint('filters', 'use_in_books_list', 1) - columns = get_config().getlist('columns', 'book', ['title']) - author = Author.get(aid) - if use_filters: - join_expressions = [] - join_expressions.append(Book.j.authors) - join_expressions.append(Author.q.id == aid) - books = search_books('full', None, {}, join_expressions, - orderBy=('series', 'ser_no', 'title', '-date'), - use_filters=use_filters) - else: - books = Book.select( - Book.j.authors & (Author.q.id == aid), - orderBy=['series', 'ser_no', 'title'], - ) - - return { - 'books_by_author': {author.fullname: list(books)}, - 'columns': columns, - } +def _books_by_author(aid): + return books_by_author(aid) @route('/static/') @@ -113,8 +64,8 @@ def download_books(): if k.split('_')[-1] == 'books': for bid in form.getall(k): books_ids.append(bid) - download_path = get_config().getpath('download', 'path') if books_ids: + download_path = get_config().getpath('download', 'path') for bid in books_ids: book = Book.get(int(bid)) download(book, download_path) @@ -146,29 +97,7 @@ def search_books_post(): value = request.forms.get('search_books') if not value: return redirect('/search_books/') - value = decode(value) search_type = request.forms.get('search_type') - if not search_type: - search_type = 'start' case_sensitive = request.forms.get('case_sensitive') - if case_sensitive is None: - case_sensitive = _guess_case_sensitivity(value) use_filters = request.forms.get('use_filters') - books = search_books(search_type, case_sensitive, {'title': value}, None, - orderBy=('title',), use_filters=use_filters) - books_by_authors = {} - for book in books: - author = book.author1 - if author in books_by_authors: - books_by_author = books_by_authors[author] - else: - books_by_author = books_by_authors[author] = [] - books_by_author.append(book) - columns = get_config().getlist('columns', 'book', ['title']) - return { - 'books_by_author': books_by_authors, - 'search_books': value, - 'search_type': search_type, - 'case_sensitive': case_sensitive, - 'columns': columns, - } + return search_books_raw(value, search_type, case_sensitive, use_filters) diff --git a/scripts/ml-initdb.py b/scripts/ml-initdb.py index c229c5e..4c21b1a 100755 --- a/scripts/ml-initdb.py +++ b/scripts/ml-initdb.py @@ -1,6 +1,7 @@ #! /usr/bin/env python import argparse + from m_librarian.config import get_config from m_librarian.db import open_db, init_db from m_librarian.glst import import_glst diff --git a/scripts/ml-search.py b/scripts/ml-search.py index 28daa0b..7e6c9da 100755 --- a/scripts/ml-search.py +++ b/scripts/ml-search.py @@ -4,6 +4,7 @@ from __future__ import print_function import argparse import os import sys + from sqlobject.sqlbuilder import CONCAT from m_lib.defenc import default_encoding diff --git a/scripts/ml-web.py b/scripts/ml-web.py index 773be00..c93b5fd 100755 --- a/scripts/ml-web.py +++ b/scripts/ml-web.py @@ -7,9 +7,9 @@ import webbrowser from bottle import thread # portable import from m_librarian.db import open_db -import m_librarian.web.app # noqa: F401 imported but unused from m_librarian.web.server import run_server from m_librarian.web.utils import get_lock, close_lock, get_open_port +import m_librarian.web.app # noqa: F401 imported but unused def start_browser(port): diff --git a/tests/dbutils.py b/tests/dbutils.py index bb5a362..06a9e9c 100644 --- a/tests/dbutils.py +++ b/tests/dbutils.py @@ -1,6 +1,8 @@ import os + from sqlobject.tests.dbtest import getConnection + from m_librarian.db import open_db, init_db from m_librarian.inp import import_inpx