autocat3/Page.py

312 lines
9.3 KiB
Python
Raw Permalink Normal View History

2019-03-28 13:45:03 +00:00
#!/usr/bin/env python
# -*- mode: python; indent-tabs-mode: nil; -*- coding: utf-8 -*-
"""
Page.py
Copyright 2009-2010 by Marcello Perathoner
Distributable under the GNU General Public License Version 3 or newer.
Base class for all pages.
"""
from __future__ import unicode_literals
import logging
import cherrypy
from libgutenberg.MediaTypes import mediatypes as mt
from libgutenberg.GutenbergDatabase import DatabaseError
import BaseSearcher
import Formatters
2019-07-26 15:54:24 +00:00
from i18n_tool import ugettext as _
2019-03-28 13:45:03 +00:00
class Page (object):
""" Base for all pages. """
def __init__ (self):
self.supported_book_mediatypes = [ mt.epub, mt.mobi ]
@staticmethod
def format (os):
""" Output page. """
return Formatters.formatters[os.format].format (os.template, os)
def client_book_mediatypes (self):
""" Return the book mediatypes accepted by the client. """
client_accepted_book_mediatypes = []
accept_header = cherrypy.request.headers.get ('Accept')
if accept_header is None:
client_accepted_book_mediatypes = self.supported_book_mediatypes
else:
#cherrypy.log ("Accept: %s" % accept_header,
# context = 'REQUEST', severity = logging.DEBUG)
client_accepted_book_mediatypes = []
accepts = cherrypy.request.headers.elements ('Accept')
for accept in accepts:
if accept.value in self.supported_book_mediatypes:
if accept.qvalue > 0:
client_accepted_book_mediatypes.append (accept.value)
return client_accepted_book_mediatypes
class NullPage (Page):
""" An empty page. """
def index (self, **dummy_kwargs):
""" Output an empty page. """
return '<html/>'
2019-04-26 01:57:22 +00:00
class GoHomePage (Page):
""" Go to start page. """
2019-04-26 01:57:22 +00:00
def index (self, **kwargs):
os = BaseSearcher.OpenSearch ()
raise cherrypy.HTTPRedirect (os.url ('start'))
2019-03-28 13:45:03 +00:00
class SearchPage (Page):
""" Abstract base class for all search page classes. """
def setup (self, dummy_os, dummy_sql):
""" Let derived classes setup the query. """
raise NotImplementedError
def fixup (self, os):
""" Give derived classes a chance to further manipulate database results. """
pass
def finalize (self, os):
""" Give derived classes a chance to fix default finalization. """
pass
def nothing_found (self, os):
""" Give derived class a chance to react if no records were found. """
os.entries.insert (0, self.no_records_found (os))
def output_suggestions (self, os, max_suggestions_per_word = 3):
""" Make suggestions. """
# similarity == matching_trigrams / (len1 + len2 - matching_trigrams)
sql_query = """
SELECT
word,
nentry,
similarity (word, %(word)s) AS sml
FROM terms
WHERE word %% %(word)s
ORDER BY sml DESC, nentry DESC LIMIT %(suggestions)s;"""
q = os.query.lower ()
sugg = []
for word in q.split ():
if len (word) > 3:
try:
rows = BaseSearcher.SQLSearcher().execute (
sql_query,
{ 'word': word, 'suggestions': max_suggestions_per_word + 1})
for i, row in enumerate (rows):
if i >= max_suggestions_per_word:
break
corr = row.word
if corr != word:
sugg.append ( (word, corr) )
except DatabaseError:
pass
for word, corr in reversed (sugg):
os.entries.insert (0, self.did_you_mean (os, corr, q.replace (word, corr)))
def index (self, **kwargs):
""" Output a search result page. """
os = BaseSearcher.OpenSearch ()
os.log_request ('search')
if 'default_prefix' in kwargs:
raise cherrypy.HTTPError (400, 'Bad Request. Unknown parameter: default_prefix')
if os.start_index > BaseSearcher.MAX_RESULTS:
raise cherrypy.HTTPError (400, 'Bad Request. Parameter start_index too high')
sql = BaseSearcher.SQLStatement ()
sql.query = 'SELECT *'
sql.from_ = ['v_appserver_books_4 as books']
# let derived classes prepare the query
try:
self.setup (os, sql)
except ValueError as what:
raise cherrypy.HTTPError (400, 'Bad Request. ' + str (what))
os.fix_sortorder ()
# execute the query
try:
BaseSearcher.SQLSearcher ().search (os, sql)
except DatabaseError as what:
cherrypy.log ("SQL Error: " + str (what),
context = 'REQUEST', severity = logging.ERROR)
raise cherrypy.HTTPError (400, 'Bad Request. Check your query.')
# sync os.title and first entry header
if os.entries:
entry = os.entries[0]
if os.title and not entry.header:
entry.header = os.title
elif entry.header and not os.title:
os.title = entry.header
os.template = os.page = 'results'
# give derived class a chance to tweak result set
self.fixup (os)
# warn user about no records found
if os.total_results == 0:
self.nothing_found (os)
# suggest alternate queries
if os.total_results < 5:
self.output_suggestions (os)
# add sort by links
if os.start_index == 1 and os.total_results > 1:
if 'downloads' in os.alternate_sort_orders:
self.sort_by_downloads (os)
if 'release_date' in os.alternate_sort_orders:
self.sort_by_release_date (os)
if 'title' in os.alternate_sort_orders:
self.sort_by_title (os)
if 'alpha' in os.alternate_sort_orders:
self.sort_alphabetically (os)
if 'quantity' in os.alternate_sort_orders:
self.sort_by_quantity (os)
os.finalize ()
self.finalize (os)
if os.total_results > 0:
# call this after finalize ()
os.entries.insert (0, self.status_line (os))
return self.format (os)
@staticmethod
def sort_by_downloads (os):
""" Append the sort by downloads link. """
cat = BaseSearcher.Cat ()
cat.rel = 'popular'
cat.title = _('Sort by Popularity')
cat.url = os.url_carry (sort_order = 'downloads')
cat.class_ += 'navlink grayed'
cat.icon = 'popular'
cat.order = 4.0
os.entries.insert (0, cat)
@staticmethod
def sort_alphabetically (os):
""" Append the sort alphabetically link. """
cat = BaseSearcher.Cat ()
cat.rel = 'alphabethical'
cat.title = _('Sort Alphabetically')
cat.url = os.url_carry (sort_order = 'alpha')
cat.class_ += 'navlink grayed'
cat.icon = 'alpha'
cat.order = 4.1
os.entries.insert (0, cat)
@staticmethod
def sort_by_title (os):
""" Append the sort alphabetically link. """
cat = BaseSearcher.Cat ()
cat.rel = 'alphabethical'
cat.title = _('Sort Alphabetically')
cat.url = os.url_carry (sort_order = 'title')
cat.class_ += 'navlink grayed'
cat.icon = 'alpha'
cat.order = 4.1
os.entries.insert (0, cat)
@staticmethod
def sort_by_quantity (os):
""" Append the sort by quantity link. """
cat = BaseSearcher.Cat ()
cat.rel = 'numerous'
cat.title = _('Sort by Quantity')
cat.url = os.url_carry (sort_order = 'quantity')
cat.class_ += 'navlink grayed'
cat.icon = 'quantity'
cat.order = 4.2
os.entries.insert (0, cat)
@staticmethod
def sort_by_release_date (os):
""" Append the sort by release date link. """
cat = BaseSearcher.Cat ()
cat.rel = 'new'
cat.title = _('Sort by Release Date')
cat.url = os.url_carry (sort_order = 'release_date')
cat.class_ += 'navlink grayed'
cat.icon = 'date'
cat.order = 4.3
os.entries.insert (0, cat)
@staticmethod
def status_line (os):
""" Placeholder for status line. """
cat = BaseSearcher.Cat ()
cat.rel = '__statusline__'
cat.class_ += 'grayed'
cat.icon = 'bibrec'
cat.order = 10
cat.header = os.title
cat.title = _(u"Displaying results {from_}{to}").format (
from_ = os.start_index, to = os.end_index)
return cat
@staticmethod
def no_records_found (os):
""" Message. """
cat = BaseSearcher.Cat ()
cat.rel = '__notfound__'
cat.title = _('No records found.')
cat.url = os.url ('start')
cat.class_ += 'navlink grayed'
cat.icon = 'bibrec'
cat.order = 11
return cat
@staticmethod
def did_you_mean (os, corr, corrected_query):
""" Message. """
cat = BaseSearcher.Cat ()
cat.rel = '__didyoumean__'
cat.title = _('Did you mean: {correction}').format (correction = corr)
cat.url = os.url ('search', query = corrected_query)
cat.class_ += 'navlink'
cat.icon = 'suggestion'
cat.order = 12
return cat