Files
@ 2fa90aa41d54
Branch filter:
Location: rattail-project/rattail/rattail/autocomplete/products.py
2fa90aa41d54
5.8 KiB
text/x-python
Update changelog
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | # -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2021 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Autocomplete Handler for Products
"""
from __future__ import unicode_literals, absolute_import
import sqlalchemy as sa
from sqlalchemy import orm
from rattail.autocomplete import Autocompleter
from rattail.db import model
class ProductAutocompleter(Autocompleter):
"""
Autocompleter for Products.
Actually this will search both the
:attr:`~rattail.db.model.products.Brand.name` and
:attr:`~rattail.db.model.products.Product.description` fields.
Note that this will *not* include products marked as "deleted" -
see :class:`ProductAllAutocompleter` if you need those too for
some reason.
"""
autocompleter_key = 'products'
model_class = model.Product
autocomplete_fieldname = 'description'
def make_base_query(self, session):
model = self.model
return session.query(model.Product)\
.outerjoin(model.Brand)\
.options(orm.joinedload(model.Product.brand))
def restrict_autocomplete_query(self, session, query, **kwargs):
# do not show "deleted" items by default
query = query.filter(model.Product.deleted == False)
return query
def filter_autocomplete_query(self, session, query, term):
model = self.model
# filter by user-provided term
criteria = []
for word in term.split():
criteria.append(sa.or_(
model.Brand.name.ilike('%{}%'.format(word)),
model.Product.description.ilike('%{}%'.format(word))))
query = query.filter(sa.and_(*criteria))
return query
def sort_autocomplete_query(self, session, query):
model = self.model
return query.order_by(model.Brand.name,
model.Product.description)
def autocomplete_display(self, product):
return product.full_description
class ProductAllAutocompleter(ProductAutocompleter):
"""
Autocompleter for Products, which shows *all* results, including
"deleted" items etc.
"""
def restrict_autocomplete_query(self, session, query, **kwargs):
return query
class ProductNewOrderAutocompleter(ProductAutocompleter):
"""
Special "new order" autocompleter for products.
We set it apart with a different key (``'products.neworder'``) so
that you can override it independently of other product
autocompleters.
But the default logic for this one is a bit special too, in that
it will try to search for *both* UPC (or whatever your "key" is)
*and* product brand, description etc. The UPC (key) must match
"exactly" whereas the description etc. uses wildcard search.
.. attribute:: lookup_only_keys
You can set this to an explicit list of product "keys" to
restrict which types of lookups are performed for that.
"""
autocompleter_key = 'products.neworder'
model_class = model.Product
autocomplete_fieldname = 'description'
lookup_only_keys = []
def get_autocomplete_data(self, session, term, **kwargs):
"""
Collect data for all matching results. This will run two
queries, one for product "key" (e.g. UPC) and another to
search brand, description etc.
Note that this still honors :attr:`max_results` and will run
the product "key" query first, without limit since it will
have very few results. Then the wildcard search query runs
and will be limited.
"""
# first run the product "key" query, and save matches for later
self.key_matches = self.find_product_key_matches(session, term,
include_keys=True)
# then run the "normal" upstream query to get data from the
# wildcard search. note that this will be results-limited
data = super(ProductNewOrderAutocompleter, self).get_autocomplete_data(
session, term, **kwargs)
return data
def get_autocomplete_results(self, data):
"""
Format the data into a final results set for return to the
caller.
"""
# first get normal "search" results
data = super(ProductNewOrderAutocompleter, self).get_autocomplete_results(data)
# then inject key matches at the beginning
for i, match in enumerate(self.key_matches):
key, product = match
data.insert(i, {'label': self.autocomplete_display(product),
'value': self.autocomplete_value(product)})
return data
def find_product_key_matches(self, session, term, **kwargs):
"""
Find the products where the "key" matches the given term.
Usually this just means finding a UPC match.
"""
handler = self.app.get_products_handler()
if 'only' not in kwargs and self.lookup_only_keys:
kwargs['only'] = self.lookup_only_keys
products = handler.find_products_by_key(session, term, **kwargs)
return products
|